/* Al-Hakeem Design System - CSS Custom Properties */
/* Based on DESIGN-SYSTEM.md specification */

/* ============================================================================
   FONTS
   ============================================================================ */

/* ============================================================================
   ACTIVIST — per-weight families
   ============================================================================
   The designer ships 10 OTFs (arfonts-activist_all.zip). Each weight has
   its OWN file, so each weight gets its OWN CSS family — the family NAME
   encodes the weight, no numeric font-weight declaration needed. Callers
   pick a cut by family token only:

       font-family: var(--font-activist-semibold);   /* always SemiBold */

   Italic variants live in the SAME family (one extra @font-face per
   family) and are picked via font-style: italic on the consuming rule.
*/

@font-face { font-family: "Activist-ExtraLight"; src: url("/static/fonts/activist/activist-extra-light.otf")         format("opentype"); font-style: normal; font-display: swap; }
@font-face { font-family: "Activist-ExtraLight"; src: url("/static/fonts/activist/activist-extra-light-italic.otf")  format("opentype"); font-style: italic; font-display: swap; }

@font-face { font-family: "Activist-Light";      src: url("/static/fonts/activist/activist-light.otf")               format("opentype"); font-style: normal; font-display: swap; }
@font-face { font-family: "Activist-Light";      src: url("/static/fonts/activist/activist-light-italic.otf")        format("opentype"); font-style: italic; font-display: swap; }

@font-face { font-family: "Activist-Regular";    src: url("/static/fonts/activist/activist-regular.otf")             format("opentype"); font-style: normal; font-display: swap; }
@font-face { font-family: "Activist-Regular";    src: url("/static/fonts/activist/activist-regular-italic.otf")      format("opentype"); font-style: italic; font-display: swap; }

@font-face { font-family: "Activist-SemiBold";   src: url("/static/fonts/activist/activist-semi-bold.otf")           format("opentype"); font-style: normal; font-display: swap; }
@font-face { font-family: "Activist-SemiBold";   src: url("/static/fonts/activist/activist-semi-bold-italic.otf")    format("opentype"); font-style: italic; font-display: swap; }

@font-face { font-family: "Activist-Bold";       src: url("/static/fonts/activist/activist-bold.otf")                format("opentype"); font-style: normal; font-display: swap; }
@font-face { font-family: "Activist-Bold";       src: url("/static/fonts/activist/activist-bold-italic.otf")         format("opentype"); font-style: italic; font-display: swap; }


/* ────────────────────────────────────────────────────────────────────────
   Arabic-Indic numeral visual-balance  (revised 2026-05-27)

   The earlier `size-adjust + unicode-range` trick (boost Activist digits
   in-place) didn't move the needle in practice — the Activist OTF
   likely doesn't ship glyphs for U+0660-U+0669, so the browser pulls
   the Arabic-Indic digits from the system fallback (Noto Sans Arabic /
   Arial), where our @font-face descriptor has no effect.

   New approach — two layers that work TOGETHER:

   1. `font-size-adjust` on body (below in :root rules):
        Tells the browser to scale every font in the stack so its
        x-height matches a fixed ratio. This pulls the fallback's
        x-height up to roughly match Activist's, so Arabic glyphs
        (digits included) stop reading as undersized.

   2. `.ds-num` utility class:
        For places where Arabic-Indic numerals are the WHOLE point
        of the field (dates, phone numbers, reference codes, the
        registration step counter, etc.) we wrap them with this
        class which bumps font-size one tier (+1pt visually). Keeps
        body copy unchanged so general Arabic text doesn't bloat.

   The two layers are independent — either can ship alone — so the
   panel still degrades gracefully on browsers that don't yet support
   `font-size-adjust` (Safari < 17, Chrome < 92, Firefox < 89).
   ──────────────────────────────────────────────────────────────────────── */
.ds-num {
  /* Subtle boost so Arabic-Indic digits stop reading as undersized
     next to Latin glyphs. 1.08em (~+1pt at body sizes) chosen after
     1.18em caused the whole header to look bloated. `em` keeps it
     proportional to context: at type-ar-12 → ~13px, at type-ar-14
     → ~15px — small enough that labels around it stay anchored.
     `tabular-nums` aligns columns of dates / phone numbers cleanly. */
  font-size: 1.08em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}


:root {
  /* ========================================================================
     TYPOGRAPHY
     ======================================================================== */

  /* `--font-ar-family` is the page-default font (set on <body>). It aliases
     the Regular per-weight family so unstyled text reads as Activist Regular
     — the combined weight-keyed "Activist" family was removed since each
     cut now lives in its own family, addressed via the tokens below. */
  --font-ar-family:            "Activist-Regular",    "Noto Sans Arabic", Arial, sans-serif;
  --font-activist-extra-light: "Activist-ExtraLight", "Noto Sans Arabic", Arial, sans-serif;
  --font-activist-light:       "Activist-Light",      "Noto Sans Arabic", Arial, sans-serif;
  --font-activist-regular:     "Activist-Regular",    "Noto Sans Arabic", Arial, sans-serif;
  --font-activist-semibold:    "Activist-SemiBold",   "Noto Sans Arabic", Arial, sans-serif;
  --font-activist-bold:        "Activist-Bold",       "Noto Sans Arabic", Arial, sans-serif;

  /* Type Scale */
  --type-ar-12: 0.75rem;
  --type-ar-14: 0.875rem;
  --type-ar-16: 1rem;
  --type-ar-18: 1.125rem;
  --type-ar-22: 1.375rem;
  --type-ar-24: 1.5rem;
  --type-ar-28: 1.75rem;
  --type-ar-32: 2rem;
  --type-ar-36: 2.25rem;
  --type-ar-40: 2.5rem;
  --type-ar-44: 2.75rem;
  --type-ar-48: 3rem;
  --type-ar-52: 3.25rem;
  --type-ar-57: 3.5625rem;

  --line-default: normal;
  /* Synced from login-page tokens.css. Use --line-100 when the line-box
     must equal the font-size with no extra leading (so flex `gap` is the
     true visible distance between baselines). */
  --line-0:   0;
  --line-100: 1;
  --line-120: 1.2;
  --line-130: 1.3;
  --tracking-default: 0;

  /* Text Direction */
  --direction-ar: rtl;
  --text-align-ar-start: right;
  --text-align-ar-end: left;

  /* ========================================================================
     COLOR PALETTES
     ======================================================================== */

  /* Blue (Primary Brand) */
  --color-blue-50: #eff4ff;
  --color-blue-100: #dae6ff;
  --color-blue-200: #bdd4ff;
  --color-blue-300: #90b8ff;
  --color-blue-400: #5c93fe;
  --color-blue-500: #366bfb;
  --color-blue-600: #234cf0;
  --color-blue-700: #1836dd;
  --color-blue-800: #1a2db3;
  --color-blue-900: #1b2c8d;
  --color-blue-950: #161d55;
  --color-blue-1000: #03030a;

  /* Green (Success / Positive) */
  /* Synced with login-page/src/app/tokens.css (Core.Color — Green). */
  --color-green-50: #e8fff8;
  --color-green-100: #c8ffec;
  --color-green-200: #97ffdf;
  --color-green-300: #55ffd3;
  --color-green-400: #0adfae;
  --color-green-500: #00dcab;
  --color-green-600: #00b48d;
  --color-green-700: #009075;
  --color-green-800: #00725d;
  --color-green-900: #005d4f;
  --color-green-950: #00352e;
  --color-green-1000: #000a09;

  /* Pink (Danger / Female Gender) */
  --color-pink-50: #fef1fa;
  --color-pink-100: #fde6f6;
  --color-pink-200: #feccf0;
  --color-pink-300: #fea3e2;
  --color-pink-400: #fd69cc;
  --color-pink-500: #f73db5;
  --color-pink-600: #e71b95;
  --color-pink-700: #c90d77;
  --color-pink-800: #a60e62;
  --color-pink-900: #8a1154;
  --color-pink-950: #550230;
  --color-pink-1000: #0a0006;

  /* Cerulean (Info / Locked States) */
  --color-cerulean-50: #f0faff;
  --color-cerulean-100: #e0f3fe;
  --color-cerulean-200: #b9e9fe;
  --color-cerulean-300: #7cd8fd;
  --color-cerulean-400: #36c6fa;
  --color-cerulean-500: #0ba6df;
  --color-cerulean-600: #008dc9;
  --color-cerulean-700: #0171a3;
  --color-cerulean-800: #065e86;
  --color-cerulean-900: #0b4e6f;
  --color-cerulean-950: #07324a;
  --color-cerulean-1000: #01070a;

  /* BlackHaze (Neutral Gray) */
  --color-blackHaze-50: #f6f7f9;
  --color-blackHaze-100: #eceef2;
  --color-blackHaze-200: #d5dbe2;
  --color-blackHaze-300: #b0bac9;
  --color-blackHaze-400: #8595ab;
  --color-blackHaze-500: #667991;
  --color-blackHaze-600: #516178;
  --color-blackHaze-700: #424e62;
  --color-blackHaze-800: #394353;
  --color-blackHaze-900: #333b47;
  --color-blackHaze-950: #22272f;
  --color-blackHaze-1000: #07080a;

  /* Cararra (Warm Neutral) */
  --color-cararra-50: #edf0ed;
  --color-cararra-100: #e1e6e1;
  --color-cararra-200: #c2cdc3;
  --color-cararra-300: #9cac9e;
  --color-cararra-400: #778a79;
  --color-cararra-500: #5c705f;
  --color-cararra-600: #48594a;
  --color-cararra-700: #3c493e;
  --color-cararra-800: #333c34;
  --color-cararra-900: #2d342f;
  --color-cararra-950: #171c18;
  --color-cararra-1000: #080a09;

  /* BrightSun (Warning) */
  --color-brightSun-50: #fffbeb;
  --color-brightSun-100: #fff3c6;
  --color-brightSun-200: #ffe588;
  --color-brightSun-300: #ffd24c;
  --color-brightSun-400: #ffbd20;
  --color-brightSun-500: #f99b07;
  --color-brightSun-600: #dd7302;
  --color-brightSun-700: #b75006;
  --color-brightSun-800: #943d0c;
  --color-brightSun-900: #7a330d;
  --color-brightSun-950: #461902;
  --color-brightSun-1000: #0a0400;

  /* Radical Red (Error) */
  --color-radicalRed-50: #fff0f2;
  --color-radicalRed-100: #ffe2e8;
  --color-radicalRed-200: #ffcad6;
  --color-radicalRed-300: #ff9fb4;
  --color-radicalRed-400: #ff698d;
  --color-radicalRed-500: #ff2c64;
  --color-radicalRed-600: #ed1156;
  --color-radicalRed-700: #c80849;
  --color-radicalRed-800: #a80944;
  --color-radicalRed-900: #8f0c40;
  --color-radicalRed-950: #50011e;
  --color-radicalRed-1000: #0a0004;

  /* Red (true red — added 2026-05-28 per user feedback on the doctor
     review decision bar. The existing `radicalRed-*` family is pink-
     leaning (carmine / hot pink) and reads as "playful" rather than
     "STOP / harm" when used for destructive actions like rejecting a
     doctor's application. This family is a Tailwind-style red palette
     reserved for those destructive paths. */
  --color-red-50:  #fef2f2;
  --color-red-100: #fee2e2;
  --color-red-200: #fecaca;
  --color-red-300: #fca5a5;
  --color-red-400: #f87171;
  --color-red-500: #ef4444;
  --color-red-600: #dc2626;
  --color-red-700: #b91c1c;
  --color-red-800: #991b1b;
  --color-red-900: #7f1d1d;
  --color-red-950: #450a0a;
  --color-red-1000: #1f0303;

  /* White */
  --color-white-50: #ffffff;
  --color-white-100: #efefef;
  --color-white-200: #dcdcdc;
  --color-white-300: #bdbdbd;
  --color-white-400: #989898;
  --color-white-500: #7c7c7c;
  --color-white-600: #656565;
  --color-white-700: #525252;
  --color-white-800: #464646;
  --color-white-900: #3d3d3d;
  --color-white-950: #292929;
  --color-white-1000: #0a0a0a;

  /* Divider */
  --color-divider-line: #e7eef2;
  --color-divider-text: #7c9db5;

  /* ========================================================================
     SEMANTIC TOKENS
     ======================================================================== */

  /* Background */
  --bg-page: var(--color-cararra-50);
  --bg-card: var(--color-white-50);
  --bg-input: var(--color-blackHaze-50);
  --bg-sidebar: var(--color-white-50);

  /* Text */
  --text-onPage-primary: var(--color-cararra-1000);
  --text-onPage-secondary: var(--color-cararra-800);
  --text-onCard-primary: var(--color-blue-1000);
  --text-onCard-secondary: var(--color-blue-1000);
  --text-link: var(--color-blue-600);
  --text-disabled: var(--color-blue-400);
  --text-divider: var(--color-divider-text);
  --text-label: var(--color-blackHaze-950);
  --text-input: var(--color-blackHaze-950);
  --text-placeholder: var(--color-blackHaze-500);

  /* Brand */
  --brand-primary: var(--color-blue-600);
  --brand-primaryText: var(--color-blue-50);

  /* Border */
  --border-default: var(--color-blue-600);
  --border-disabled: var(--color-blue-400);
  --border-divider: var(--color-divider-line);

  /* ========================================================================
     SPACING
     ======================================================================== */

  --space-0: 0rem;
  --space-0_5: 0.125rem;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-7: 2rem;
  --space-8: 2.5rem;
  --space-9: 3rem;
  --space-10: 3.5rem;
  --space-11: 4rem;
  --space-12: 5rem;
  --space-13: 6rem;
  --space-14: 7.5rem;
  --space-15: 10rem;

  /* Layout */
  --layout-padding-inline: var(--space-7);
  --content-padding-block: 2.5rem;
  --card-padding: var(--space-7);
  --card-gap: var(--space-6);

  /* ========================================================================
     BORDER & RADIUS
     ======================================================================== */

  --stroke-none: 0rem;
  --stroke-1: 0.0625rem;
  --stroke-1_5: 0.09375rem;
  --stroke-2: 0.125rem;
  --stroke-4: 0.25rem;

  --radius-0: 0rem;
  --radius-2: 0.125rem;
  --radius-4: 0.25rem;
  --radius-8: 0.5rem;

  /* ========================================================================
     COMPONENT SIZES
     ======================================================================== */

  /* The document row's block size (`.ds-qual-row`), measured from its own
     content: 2 × --space-4 padding + the title/status line. Anything that
     stands IN PLACE OF a document row — today `.ds-empty-state` — matches
     this instead of restating the number, so the two can never drift apart
     when the row's design changes. */
  --ds-doc-row-block-size: 4.875rem;

  /* ========================================================================
     OPACITY
     ======================================================================== */

  --opacity-0: 0;
  --opacity-20: 0.2;
  --opacity-40: 0.4;
  --opacity-60: 0.6;
  --opacity-70: 0.7;
  --opacity-80: 0.8;
  --opacity-100: 1;

  /* ========================================================================
     ICON SIZES
     ======================================================================== */

  --icon-size-12: 0.75rem;
  --icon-size-16: 1rem;
  --icon-size-20: 1.25rem;
  --icon-size-24: 1.5rem;
  --icon-size-32: 2rem;
  --icon-size-40: 2.5rem;
  --icon-size-48: 3rem;

  /* ========================================================================
     SPECIAL VALUES (string aliases for cleaner inline style)
     ======================================================================== */

  --value-none: none;
  --value-transparent: transparent;

  /* ========================================================================
     Z-INDEX
     ======================================================================== */

  --z-base: 0;
  --z-content: 10;
  --z-sticky: 20;
  --z-dropdown: 30;
  --z-overlay: 40;
  --z-modal: 50;
  --z-popover: 60;
  --z-tooltip: 70;
  --z-notification: 80;
  --z-max: 100;

  /* ========================================================================
     INTERACTIVE STATES
     ======================================================================== */

  /* Primary (Filled Blue) */
  /* Primary button — text colour is blue-50 across every interactive state
     (default / hover / pressed / focus-visible). Figma master 1595:3503.
     Only the disabled state shifts the label to blue-200. */
  --interactive-primary-default-bg: var(--color-blue-600);
  --interactive-primary-default-text: var(--color-blue-50);
  --interactive-primary-hover-bg: var(--color-blue-500);
  --interactive-primary-hover-text: var(--color-blue-50);
  --interactive-primary-active-bg: var(--color-blue-800);  /* legacy alias — kept for any callers using --active-bg directly; pressed uses -pressed-bg below */
  --interactive-primary-active-text: var(--color-blue-50);
  --interactive-primary-pressed-bg: var(--color-blue-700);
  --interactive-primary-pressed-text: var(--color-blue-50);
  --interactive-primary-disabled-bg: var(--color-blue-400);
  --interactive-primary-disabled-text: var(--color-blue-200);

  /* Secondary (Outlined Blue) */
  --interactive-secondary-default-bg: transparent;
  --interactive-secondary-default-text: var(--color-blue-600);
  --interactive-secondary-default-border: var(--color-blue-600);
  --interactive-secondary-hover-text: var(--color-blue-500);
  --interactive-secondary-hover-border: var(--color-blue-500);
  --interactive-secondary-disabled-text: var(--color-blue-400);
  --interactive-secondary-disabled-border: var(--color-blue-400);

  /* Tertiary (Ghost Blue — text-only button) */
  --interactive-tertiary-default-bg: transparent;
  --interactive-tertiary-default-text: var(--color-blue-600);
  --interactive-tertiary-default-border: transparent;
  --interactive-tertiary-hover-bg: transparent;
  --interactive-tertiary-hover-text: var(--color-blue-500);
  --interactive-tertiary-hover-border: transparent;
  --interactive-tertiary-active-bg: transparent;
  --interactive-tertiary-active-text: var(--color-blue-800);
  --interactive-tertiary-active-border: transparent;
  --interactive-tertiary-pressed-bg: transparent;
  --interactive-tertiary-pressed-text: var(--color-blue-700);
  --interactive-tertiary-pressed-border: transparent;
  --interactive-tertiary-disabled-bg: transparent;
  --interactive-tertiary-disabled-text: var(--color-blue-400);
  --interactive-tertiary-disabled-border: transparent;

  /* Danger (Filled Red — proper `red` family, NOT radicalRed)
     -----------------------------------------------------------------
     2026-05-28 re-re-color: `radicalRed-600` (#ed1156) reads as hot
     PINK to reviewers, which clashes with the universal "STOP / harm /
     rejection" semantic. Tailwind-style `red-600` (#dc2626) is the
     correct destructive-action colour and is now in the palette as
     `--color-red-*`. radicalRed remains in the palette for non-
     destructive pink accents elsewhere. */
  --interactive-danger-default-bg: var(--color-red-600);
  --interactive-danger-default-text: var(--color-red-50);
  --interactive-danger-hover-bg: var(--color-red-700);
  --interactive-danger-hover-text: var(--color-red-50);
  --interactive-danger-disabled-bg: var(--color-red-200);
  --interactive-danger-disabled-text: var(--color-red-400);

  /* ========================================================================
     FEEDBACK SYSTEM
     ========================================================================
     Each variant has:
       bg       — fill behind the alert
       border   — 1px outline
       icon     — fill colour for the leading SVG glyph
       text     — accent / dismiss icon colour
       message  — title text (max-contrast on bg)
       desc     — description text (medium contrast on bg)
     Pattern (per `foundations/color.md`):
       bg = palette-50, border|icon = palette-500, message = palette-950, desc = palette-700
  */

  --feedback-success-bg: var(--color-green-50);
  --feedback-success-border: var(--color-green-500);
  --feedback-success-icon: var(--color-green-500);
  --feedback-success-text: var(--color-green-500);
  --feedback-success-message: var(--color-green-950);
  --feedback-success-desc: var(--color-green-700);

  --feedback-error-bg: var(--color-radicalRed-50);
  --feedback-error-border: var(--color-radicalRed-500);
  --feedback-error-icon: var(--color-radicalRed-500);
  --feedback-error-text: var(--color-radicalRed-500);
  --feedback-error-message: var(--color-radicalRed-950);
  --feedback-error-desc: var(--color-radicalRed-500);

  --feedback-warning-bg: var(--color-brightSun-50);
  --feedback-warning-border: var(--color-brightSun-500);
  --feedback-warning-icon: var(--color-brightSun-500);
  --feedback-warning-text: var(--color-brightSun-500);
  --feedback-warning-message: var(--color-brightSun-950);
  --feedback-warning-desc: var(--color-brightSun-700);

  --feedback-info-bg: var(--color-cerulean-50);
  --feedback-info-border: var(--color-cerulean-500);
  --feedback-info-icon: var(--color-cerulean-500);
  --feedback-info-text: var(--color-cerulean-500);
  --feedback-info-message: var(--color-cerulean-950);
  --feedback-info-desc: var(--color-cerulean-700);

  /* Network status — uses the same shape as the four message variants. */
  --feedback-offline-bg: var(--color-blackHaze-50);
  --feedback-offline-border: var(--color-blackHaze-500);
  --feedback-offline-icon: var(--color-blackHaze-500);
  --feedback-offline-text: var(--color-blackHaze-500);
  --feedback-offline-message: var(--color-blackHaze-950);
  --feedback-offline-desc: var(--color-blackHaze-700);

  --feedback-online-bg: var(--color-green-50);
  --feedback-online-border: var(--color-green-500);
  --feedback-online-icon: var(--color-green-500);
  --feedback-online-text: var(--color-green-500);
  --feedback-online-message: var(--color-green-950);
  --feedback-online-desc: var(--color-green-950);

  /* ========================================================================
     FIELD STATE LADDER (foundations/color.md, components/fields/README.md)
     ========================================================================
     Every form field derives its visual state from the 10-state ladder:
       default → focused → focused_filled → filled
       disabled  readonly  success  error  warning  info
     Tokens follow `--field-{state}-{slot}` where slot ∈
       { bg, label, value, placeholder, border, icon }.
     Border is only painted when the state has a 1.5px outline
     (focused, focused_filled, error, warning).
  */

  /* Default */
  --field-default-bg: var(--color-blackHaze-50);
  --field-default-label: var(--color-blackHaze-1000);
  --field-default-value: var(--color-blackHaze-800);
  --field-default-placeholder: var(--color-blackHaze-800);
  --field-default-border: var(--value-transparent);
  --field-default-icon: var(--color-blackHaze-500); /* Figma briefcase SVG stroke = #667991 = blackHaze-500 */

  /* Focused (no value yet) */
  --field-focused-bg: var(--field-default-bg);
  --field-focused-label: var(--field-default-label);
  --field-focused-value: var(--field-default-value);
  --field-focused-placeholder: var(--field-default-placeholder);
  --field-focused-border: var(--color-blue-600);
  --field-focused-icon: var(--field-default-icon);
  --field-focused-cursor: var(--color-blue-600);

  /* Focused + filled (clear-affordance shown) */
  --field-focused_filled-bg: var(--field-default-bg);
  --field-focused_filled-label: var(--field-default-label);
  --field-focused_filled-value: var(--field-default-value);
  --field-focused_filled-placeholder: var(--field-default-placeholder);
  --field-focused_filled-border: var(--color-blue-600);
  --field-focused_filled-icon: var(--field-default-icon);
  --field-focused-filled-clear: var(--color-blue-300);

  /* Filled (not focused, has value) */
  --field-filled-bg: var(--field-default-bg);
  --field-filled-label: var(--field-default-label);
  --field-filled-value: var(--field-default-value);
  --field-filled-placeholder: var(--field-default-placeholder);
  --field-filled-border: var(--value-transparent);
  --field-filled-icon: var(--field-default-icon);

  /* Readonly */
  --field-readonly-bg: var(--color-blackHaze-100);
  --field-readonly-label: var(--color-blackHaze-1000);
  --field-readonly-value: var(--color-blackHaze-300); /* Figma: blackHaze-300 (was -500) */
  --field-readonly-placeholder: var(--color-blackHaze-300);
  --field-readonly-border: var(--value-transparent);
  --field-readonly-icon: var(--color-blackHaze-300);

  /* Disabled */
  --field-disabled-bg: var(--color-blackHaze-50);
  --field-disabled-label: var(--color-blackHaze-300);
  --field-disabled-value: var(--color-blackHaze-200);
  --field-disabled-placeholder: var(--color-blackHaze-200);
  --field-disabled-border: var(--value-transparent);
  --field-disabled-icon: var(--color-blackHaze-200);

  /* Success */
  --field-success-bg: var(--color-blackHaze-50);
  --field-success-label: var(--color-green-1000); /* Figma: green-1000 (was blackHaze-1000) */
  --field-success-value: var(--color-green-600);
  --field-success-placeholder: var(--field-default-placeholder);
  --field-success-border: var(--value-transparent);
  --field-success-icon: var(--field-default-icon);
  --field-success-status-icon: var(--color-green-600); /* Figma success tick = green-600 (was -500) */

  /* Error */
  --field-error-bg: var(--color-radicalRed-50);
  --field-error-label: var(--color-radicalRed-950);
  --field-error-value: var(--color-radicalRed-500);
  --field-error-placeholder: var(--field-default-placeholder);
  --field-error-border: var(--color-radicalRed-600);
  --field-error-icon: var(--color-radicalRed-500);
  --field-error-status-icon: var(--color-radicalRed-500);

  /* Warning */
  --field-warning-bg: var(--color-brightSun-50);
  --field-warning-label: var(--color-brightSun-950);
  --field-warning-value: var(--color-brightSun-500);
  --field-warning-placeholder: var(--field-default-placeholder);
  --field-warning-border: var(--color-brightSun-600);
  --field-warning-icon: var(--color-brightSun-500);

  /* Info */
  --field-info-bg: var(--color-blackHaze-50);
  --field-info-label: var(--color-blackHaze-1000);
  --field-info-value: var(--color-blackHaze-800);
  --field-info-placeholder: var(--field-default-placeholder);
  --field-info-border: var(--value-transparent);
  --field-info-icon: var(--field-default-icon);

  /* Status icon shown in `focused_filled` (the clear × glyph wrapper). */
  --field-focused_filled-status-icon: var(--color-blue-300);

  /* ========================================================================
     BUTTON SHADOWS  (Figma node 1595:3503 — Primary Button master)
     ========================================================================
     Two-layer halos tinted to the button's own fill, geometry fixed at
     0/6/6 + 0/12/12 across every state (matches the Figma drop-shadow
     stack). Alphas are 0.06 + 0.08 — DO NOT increase them on hover; the
     hover signal comes from the bg colour change, not a heavier shadow.
     Disabled fields drop the shadow entirely.

     Tint per state matches the button's own background colour:
       - default:  blue-600 (#234cf0) — 35, 76, 240
       - hover:    blue-500 (#366bfb) — 54, 107, 251
       - pressed:  blue-700 (#1836dd) — 24,  54, 221
  */

  --btn-primary-shadow-default:
    0 0.375rem 0.375rem 0 color-mix(in srgb, var(--color-blue-600) 6%, transparent),
    0 0.75rem  0.75rem  0 color-mix(in srgb, var(--color-blue-600) 8%, transparent);

  --btn-primary-shadow-hover:
    0 0.375rem 0.375rem 0 color-mix(in srgb, var(--color-blue-500) 6%, transparent),
    0 0.75rem  0.75rem  0 color-mix(in srgb, var(--color-blue-500) 8%, transparent);

  --btn-primary-shadow-pressed:
    0 0.375rem 0.375rem 0 color-mix(in srgb, var(--color-blue-700) 6%, transparent),
    0 0.75rem  0.75rem  0 color-mix(in srgb, var(--color-blue-700) 8%, transparent);

  --btn-primary-shadow-focus: var(--btn-primary-shadow-default);

  /* Back-compat alias used by older code. */
  --btn-primary-shadow: var(--btn-primary-shadow-default);

  /* Focus ring (1.5px) — Figma uses the bg colour itself (blue-600), not a
     lighter tint, so the ring reads at the same strength as the inner pill. */
  --btn-primary-focus-border: var(--color-blue-600);

  /* Soft floating-panel shadows for dropdowns / popovers. */
  --avatar-dropdown-shadow: 0 0.75rem 1.5rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 6%, transparent);

  /* ========================================================================
     HEADER / TOPBAR CHROME
     ======================================================================== */

  --header-bg: var(--bg-card);
  --header-logo-text: var(--brand-primary);
  --header-lang-label: var(--text-onCard-secondary);
  --header-lang-name: var(--text-onCard-primary);
  --header-lang-border: var(--border-divider);
  --header-lang-label-opacity: var(--opacity-60);

  /* ========================================================================
     ACCOUNT MENU / AVATAR (used by topbar dropdown)
     ======================================================================== */

  --avatar-size: var(--icon-size-32);
  --avatar-icon-size: var(--icon-size-20);
  --avatar-male-bg: var(--color-cerulean-100);
  --avatar-male-icon: var(--color-cerulean-700);
  --avatar-female-bg: var(--color-pink-100);
  --avatar-female-icon: var(--color-pink-700);

  --avatar-dropdown-bg: var(--bg-card);
  --avatar-dropdown-radius: var(--radius-8);
  --avatar-dropdown-text: var(--text-onCard-primary);
  --avatar-dropdown-icon: var(--color-blackHaze-500);
  --avatar-dropdown-divider: var(--border-divider);
  --avatar-dropdown-profile-hover-bg: var(--color-blackHaze-50);
  --avatar-dropdown-logout-text: var(--color-radicalRed-600);
  --avatar-dropdown-logout-hover-bg: var(--color-radicalRed-50);

  --menu-action-default-bg: var(--value-transparent);
  --menu-action-default-text: var(--text-onCard-primary);
  --menu-action-default-icon: var(--color-blackHaze-500);
  --menu-action-hover-bg: var(--color-blackHaze-50);
  --menu-action-hover-text: var(--text-onCard-primary);
  --menu-action-hover-icon: var(--color-blue-600);
  --menu-action-disabled-bg: var(--value-transparent);
  --menu-action-disabled-text: var(--text-disabled);
  --menu-action-disabled-icon: var(--color-blackHaze-300);

  /* ========================================================================
     SIDEBAR (Panel-specific)
     ======================================================================== */

  --sidebar-width: 16rem;
  --sidebar-collapsed-width: 4rem;
  --topbar-height: 4rem;
}

/* ============================================================================
   GLOBAL STYLES
   ============================================================================ */

body {
  font-family: var(--font-ar-family);
  font-weight: 400;
  font-size: var(--type-ar-16);
  line-height: var(--line-default);
  color: var(--text-onPage-primary);
  background-color: var(--bg-page);
  /* NOTE 2026-05-27: an earlier attempt set `font-size-adjust: 0.5`
     here to normalise x-height between Activist (Arabic) and its
     Latin fallbacks. In practice it scaled the WHOLE document up
     visibly — every paragraph, every label, the sidebar nav — so we
     dropped it and now lean on the targeted `.ds-num` utility class
     for the cases that actually needed help (digit-heavy fields). */
}

/* ============================================================================
   TRANSITIONS
   ============================================================================ */

.btn-transition {
  transition: background-color 0.2s ease, box-shadow 0.2s ease,
    transform 0.15s ease, opacity 0.2s ease;
}

.btn-transition:hover:not(:disabled) {
  transform: translateY(-0.0625rem);
  box-shadow: 0 0.25rem 0.75rem 0 color-mix(in srgb, var(--color-blue-600) 18%, transparent);
}

.btn-transition:active:not(:disabled) {
  transform: translateY(0.0625rem);
  box-shadow: 0 0.0625rem 0.25rem 0 color-mix(in srgb, var(--color-blue-600) 10%, transparent);
}

.input-focus-transition {
  transition: box-shadow 0.25s ease, border-color 0.25s ease;
}

.input-focus-transition:focus-within {
  box-shadow: 0 0 0 0.1875rem color-mix(in srgb, var(--color-blue-600) 8%, transparent);
}

/* ============================================================================
   RTL DIRECTION FLIPS
   ============================================================================ */

[dir="rtl"] .flip-rtl {
  transform: scaleX(-1);
}

/* ============================================================================
   ANIMATIONS
   ============================================================================ */

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(1.25rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-1rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.anim-fade-in-up {
  animation: fadeInUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.anim-slide-down {
  animation: slideDown 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* ----------------------------------------------------------------------------
   AVATAR LOADING SKELETON
   Used on the doctors-list 48px circular photo (templates/doctors/list.html).
   The <span> wrapper carries the moving gradient; the <img> child sits on
   top fully transparent until its `onload` flips the `is-loaded` class on
   the wrapper, which kills the animation, drops the gradient, and fades
   the actual photo in over 200ms. RTL-agnostic — the gradient axis is
   logical (the background-position numbers are symmetric anyway).
   ---------------------------------------------------------------------------- */
@keyframes ds-avatar-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

.ds-avatar-skeleton {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-4);   /* square avatar, 4px (user 2026-06-14) */
  overflow: hidden;
  /* border-box so the ring lives INSIDE the 48×48 box — the avatar's
     outer footprint stays identical whether the row is in skeleton,
     loaded, or static state. Otherwise content-box (the default for
     inline-block) would inflate the column to 48 + 2*stroke. */
  box-sizing: border-box;
  /* Thin blackHaze ring frames every avatar — keeps the silhouette
     consistent against any row background (zebra stripe, hover blue,
     selection). Token-only: --stroke-1 (0.0625rem) for thickness,
     --color-blackHaze-200 for the line. */
  border: var(--stroke-1) solid var(--color-blackHaze-200);
  /* Two-stop blackHaze gradient — sits inside our token palette so we
     never touch raw hex. 200% width gives the highlight band room to
     traverse without the gradient ends ever showing as a hard seam. */
  background-image: linear-gradient(
    90deg,
    var(--color-blackHaze-100) 0%,
    var(--color-blackHaze-200) 50%,
    var(--color-blackHaze-100) 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: ds-avatar-shimmer 1.4s ease-in-out infinite;
}

.ds-avatar-skeleton > img {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-4);
  object-fit: cover;
  display: block;
  opacity: 0;
  transition: opacity 200ms ease-out;
}

.ds-avatar-skeleton.is-loaded {
  background-image: none;
  animation: none;
}

.ds-avatar-skeleton.is-loaded > img {
  opacity: 1;
}

/* The gendered default SVGs are local files that decode synchronously —
   they don't need a skeleton frame. The plain (non-wrapper) variant
   below renders them straight, no animation overhead. Same blackHaze
   ring as the loaded variant so all rows look uniform. */
.ds-avatar-static {
  display: inline-block;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-4);   /* square avatar, 4px (user 2026-06-14) */
  border: var(--stroke-1) solid var(--color-blackHaze-200);
  /* `box-sizing: border-box` keeps the OUTER footprint at 48×48 so the
     ring doesn't push the surrounding row text by 2px. Same reason for
     the explicit width/height on `.ds-avatar-skeleton` above — the
     wrapper there is already block-sized by border-box defaults inside
     the cascade. */
  box-sizing: border-box;
}

/* ----------------------------------------------------------------------------
   STACKED REVIEWERS  (used in templates/doctors/list.html, "من يراجع" column)
   Renders 24px circular avatars for every admin who has issued at least one
   verdict (Approve / Reject / Request-Changes) on the doctor's current
   review round. Multiple reviewers stack with a -0.5rem overlap, and a
   "+N" chip caps the row at 3 visible avatars before collapsing the rest.
   Token-only — no raw hex, no raw px (the small px on the avatar size is
   a rem-equivalent: 24px = 1.5rem; if you ever swap to a rem token, just
   replace width/height — the rest of the cascade keeps working).
   ---------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
   DOCTOR DETAIL HEADER PHOTO  (templates/doctors/detail.html)
   64×64 square with radius-4 corners. Two visual modes driven by the
   presence of an uploaded photo:
     · default (.ds-doctor-detail-photo)            → photo fills edge-to-edge
     · fallback (.ds-doctor-detail-photo--fallback) → centred gendered SVG
   The class is set server-side based on whether `doctor_photo` is
   populated. The `onerror` handler on the <img> also toggles the
   `--fallback` modifier when a previously-uploaded photo's presigned
   URL has expired and the gendered SVG takes over — keeping the
   visual treatment consistent.
   ---------------------------------------------------------------------------- */
.ds-doctor-detail-photo--fallback {
  /* Lift the gendered avatar SVG visually off the background by
     tinting the frame a touch toward cerulean — this way an
     "uploaded-soon-after" preview isn't mistaken for the placeholder. */
  background-color: var(--color-blackHaze-50, var(--color-blackHaze-100));
}

/* ----------------------------------------------------------------------------
   DOCTOR DETAIL STATUS BADGE — local size uplift  (added 2026-05-27)
   The default .ds-badge is sized for table cells (12px font, 4×8 padding)
   which reads as a tiny chip next to the 22px doctor name in the detail
   header. We bump it ONE tier up here so the status anchors visually
   against the name — but scope the change to `.ds-doctor-detail-status`
   so the list-page and other surfaces keep the compact chip. The marker
   square (the coloured dot rendered via ::before) scales up in lock-step
   so the proportions stay clean. RTL-safe — all spacings are logical.
   ---------------------------------------------------------------------------- */
.ds-doctor-detail-status > .ds-badge {
  font-size: var(--type-ar-14);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-4);
  gap: var(--space-2);
}
.ds-doctor-detail-status > .ds-badge::before {
  /* Bigger marker square to keep the visual ratio with the larger label. */
  width: 0.625rem;   /* 10px */
  height: 0.625rem;
}

.ds-reviewers-stack {
  /* `inline-flex` so the stack flows next to whatever sits beside it in
     the table cell, but still establishes its own baseline. The row-
     reverse mirror happens automatically under RTL because we rely on
     `flex-direction: row` (logical direction) — but we use a negative
     `margin-inline-start` on the children instead of `margin-left`,
     so the overlap direction also flips cleanly. */
  display: inline-flex;
  align-items: center;
  flex-direction: row;
  vertical-align: middle;
}

.ds-reviewer-avatar {
  position: relative;
  display: inline-block;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  overflow: hidden;
  /* The white ring is what creates the "stacked playing cards" depth —
     each avatar's blackHaze edge sits on top of a 2px white halo that
     visually separates it from the avatar behind it. */
  box-sizing: border-box;
  border: var(--stroke-2) solid var(--color-white-50);
  background-color: var(--color-blackHaze-100);
  flex-shrink: 0;
}

/* Every child after the first slides under its predecessor by half its
   width. Using `margin-inline-start` (not `-left`) flips correctly for
   RTL: in RTL the second card slides UNDER the first from the right. */
.ds-reviewers-stack > .ds-reviewer-avatar + .ds-reviewer-avatar,
.ds-reviewers-stack > .ds-reviewer-avatar + .ds-reviewers-more,
.ds-reviewers-stack > .ds-reviewers-more + .ds-reviewer-avatar {
  margin-inline-start: -0.5rem;
}

.ds-reviewer-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Initials chip when the admin has no avatar uploaded. Cerulean tint
   keeps the colour in the same family the panel uses for "actively
   under review" (badge `cerulean-500`). */
.ds-reviewer-avatar--initials {
  background-color: var(--color-cerulean-100);
  color: var(--color-cerulean-900);
  font-size: 0.6875rem;        /* 11px — readable inside 24px circle */
  font-weight: 600;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-transform: uppercase;
  letter-spacing: 0;
}

/* "+N" chip for the overflow tail. Sized identically to an avatar so
   the stack baseline stays even. */
.ds-reviewers-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  box-sizing: border-box;
  border: var(--stroke-2) solid var(--color-white-50);
  background-color: var(--color-blackHaze-200);
  color: var(--color-blackHaze-900);
  font-size: 0.625rem;         /* 10px — same band as the initials chip */
  font-weight: 600;
  line-height: 1;
  flex-shrink: 0;
}

/* Tooltip on the whole stack (data-tip set in the template) — reuses the
   existing `[data-tip]:hover::after` rule already defined for the verified
   badge. The `position: relative` lets the absolutely-positioned tooltip
   anchor here. */
.ds-reviewers-stack[data-tip] {
  position: relative;
}

/* When the stack is rendered as a <button> (i.e. ≥2 reviewers, where a
   popup makes sense), give it pointer affordance + transparent button
   chrome so it visually matches the read-only span variant. */
button.ds-reviewers-stack {
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
}
button.ds-reviewers-stack:focus-visible {
  outline: var(--stroke-2) solid var(--color-cerulean-500);
  outline-offset: 0.125rem;
  border-radius: var(--radius-8);
}

/* ----------------------------------------------------------------------------
   REVIEWERS POPUP  (used in templates/doctors/list.html for ≥2 reviewers)
   A single global dialog sits at the page bottom; clicks on any clickable
   stack drive it via data-attributes (see the JS at the foot of list.html).
   Token-safe: every color is a Core.Color token or a color-mix on one;
   every radius is on the {0,2,4,8} ladder.
   ---------------------------------------------------------------------------- */
.ds-reviewers-popup {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: none;                  /* JS flips this to flex on open */
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  /* Backdrop = pure token tint, no raw rgba(). 50% of blackHaze-1000
     (the darkest neutral) blended with transparent gives the same
     dim-the-page feel without violating the design-system rule. */
  background-color: color-mix(in srgb, var(--color-blackHaze-1000) 50%, transparent);
}
.ds-reviewers-popup.is-open {
  display: flex;
  animation: fadeInUp 0.22s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.ds-reviewers-popup__dialog {
  width: 100%;
  max-width: 28rem;               /* 448px */
  background-color: var(--bg-card, var(--color-white-50));
  border-radius: var(--radius-8);
  /* Card → inner sections will use radius-4 to respect the nested
     radius ladder (8 → 4 → 2). */
  box-shadow: 0 1.5rem 3rem color-mix(in srgb, var(--color-blackHaze-1000) 16%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  max-height: min(80vh, 32rem);
}

.ds-reviewers-popup__header {
  padding: var(--space-4) var(--space-5);
  border-block-end: var(--stroke-1) solid var(--color-blackHaze-200);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.ds-reviewers-popup__title {
  margin: 0;
  font-size: var(--type-ar-16);
  font-weight: 600;
  color: var(--color-blackHaze-900);
  line-height: 1.4;
  /* min-width: 0 + ellipsis keeps a very long doctor name from pushing
     the close button off the dialog. */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}

.ds-reviewers-popup__close {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-4);
  border: 0;
  background: transparent;
  color: var(--color-blackHaze-700);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 1.25rem;
  line-height: 1;
  transition: background-color 120ms ease, color 120ms ease;
}
.ds-reviewers-popup__close:hover {
  background-color: var(--color-blackHaze-100);
  color: var(--color-blackHaze-900);
}

.ds-reviewers-popup__list {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0;
  overflow-y: auto;
}

.ds-reviewers-popup__item {
  display: flex;
  /* `flex-start` so the avatar stays anchored to the top of the row
     once decisions extend the meta column vertically. With center, a
     reviewer who decided on three steps would float their avatar to
     the middle of an awkward block. */
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  /* Separator between consecutive reviewers — easier to scan when
     multiple admins each have several decisions logged. The last
     child loses it via `&:last-child` below. */
  border-block-end: var(--stroke-1) solid var(--color-blackHaze-100);
  transition: background-color 120ms ease;
}
.ds-reviewers-popup__item:last-child {
  border-block-end: 0;
}
.ds-reviewers-popup__item:hover {
  background-color: var(--color-blackHaze-50, var(--color-blackHaze-100));
}

.ds-reviewers-popup__avatar {
  width: 2.5rem;                  /* 40px */
  height: 2.5rem;
  border-radius: 50%;
  flex-shrink: 0;
  box-sizing: border-box;
  border: var(--stroke-1) solid var(--color-blackHaze-200);
  background-color: var(--color-blackHaze-100);
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ds-reviewers-popup__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ds-reviewers-popup__avatar--initials {
  background-color: var(--color-cerulean-100);
  color: var(--color-cerulean-900);
  font-size: var(--type-ar-14);
  font-weight: 600;
  text-transform: uppercase;
}

.ds-reviewers-popup__meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: var(--space-0_5);
}
.ds-reviewers-popup__name {
  font-size: var(--type-ar-14);
  font-weight: 600;
  color: var(--color-blackHaze-900);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-reviewers-popup__email {
  font-size: var(--type-ar-12);
  color: var(--color-blackHaze-700);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Email is naturally LTR even inside an RTL dialog. */
  direction: ltr;
  text-align: start;
}

/* Decision list inside a reviewer item — chronological log of every
   step the admin has decided on for this doctor in the current round.
   The 0.5rem block-start margin separates it from the name/email block
   while keeping the row vertically dense. */
.ds-reviewers-popup__decisions {
  list-style: none;
  margin: var(--space-2) 0 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  /* Inset matches the avatar's left edge + gap so each decision line
     reads as a child of the admin who took it. */
  padding-inline-start: 0;
}

.ds-reviewers-popup__decision {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: var(--space-2);
  /* radius-2 (the smallest of the {0,2,4,8} ladder) — nested inside
     the radius-4 reviewer item which itself sits inside the radius-8
     dialog. */
  border-radius: var(--radius-2);
  padding: var(--space-1) var(--space-2);
  /* Subtle resting bg so each decision is visually distinct from the
     enclosing reviewer row without competing for attention. */
  background-color: var(--color-blackHaze-50, var(--color-blackHaze-100));
}

.ds-reviewers-popup__decision__badge {
  /* 22px chip — small enough to sit next to a 14px label without
     dominating, large enough to host a 14px icon comfortably. */
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The svg icon inside inherits `color` via stroke="currentColor", so
     setting `color` here propagates to the glyph. */
  color: var(--color-white-50);
}
.ds-reviewers-popup__decision__badge svg {
  width: 0.875rem;     /* 14px */
  height: 0.875rem;
  display: block;
}
/* Verdict-specific colors — each pair (bg + glyph contrast) lives
   exclusively in the Core.Color palette. */
.ds-reviewers-popup__decision__badge--approved {
  background-color: var(--color-green-500);
}
.ds-reviewers-popup__decision__badge--changes_requested {
  background-color: var(--color-brightSun-500);
  color: var(--color-blackHaze-1000);   /* dark on yellow for AAA */
}
.ds-reviewers-popup__decision__badge--rejected {
  background-color: var(--color-radicalRed-500);
}

.ds-reviewers-popup__decision__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.125rem;
}
.ds-reviewers-popup__decision__step {
  font-size: var(--type-ar-14);
  font-weight: 500;
  color: var(--color-blackHaze-900);
  line-height: 1.3;
  /* Wrap long step names rather than truncate — the dialog can grow
     vertically (max-height + scroll on the list). */
  white-space: normal;
  word-break: break-word;
}
.ds-reviewers-popup__decision__time {
  font-size: var(--type-ar-12);
  color: var(--color-blackHaze-700);
  line-height: 1.3;
  /* `font-variant-numeric: tabular-nums` keeps Arabic numerals
     vertically aligned across rows even when widths differ. */
  font-variant-numeric: tabular-nums;
}

/* ============================================================================
   REDUCED MOTION
   ============================================================================ */

@media (prefers-reduced-motion: reduce) {
  .anim-fade-in-up,
  .anim-slide-down,
  .btn-transition,
  .input-focus-transition,
  .ds-btn,
  .ds-field {
    animation: none !important;
    transition: none !important;
  }

  .ds-btn:hover:not(:disabled) {
    transform: none !important;
  }
}

/* ============================================================================
   DS COMPONENT BASE STYLES
   ============================================================================
   The structural styling for each component lives in CSS classes here so
   pages don't need to inline it. Page templates set semantic content + any
   per-instance overrides; everything visual flows from these classes.

   Patterns in use:
   - Two-layer "ring + card" shape on Buttons and Fields (see
     `foundations/radius-and-stroke.md`). Outer wrapper carries a 1.5px
     transparent border that becomes coloured on focus → zero layout shift.
   - Per-weight font-family tokens (`--font-activist-{weight}`) — declared
     at the top of this file — pick the matching font-face automatically.
*/


/* ============================================================================
   BUTTON — components/button.md
   ============================================================================
   Anatomy:
     <button class="ds-btn ds-btn--{variant} ds-btn--{size}">
       <span class="ds-btn__inner">
         [start-icon] label [end-icon]
       </span>
     </button>

   - Outer .ds-btn  : transparent 1.5px border, padding-0_5, radius-8.
                       On :focus-visible, border becomes blue-300 and the
                       outer radius drops to radius-4.
   - Inner .ds-btn__inner : the visible pill — bg, padding, radius-4,
                       transitions colour + shadow.
*/

.ds-btn {
  display: inline-flex;
  padding: var(--space-0_5);
  margin: 0;
  border-radius: var(--radius-8);
  border-width: var(--stroke-1_5);
  border-style: solid;
  border-color: var(--value-transparent);
  background: var(--value-transparent);
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.2s ease, border-radius 0.2s ease, transform 0.15s ease;
}

.ds-btn:disabled,
.ds-btn[aria-disabled="true"] {
  cursor: not-allowed;
}

.ds-btn:hover:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(-0.0625rem);
}

.ds-btn:active:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(0.0625rem);
}

.ds-btn:focus-visible {
  outline: none;
  border-color: var(--btn-primary-focus-border);
  border-radius: var(--radius-4);
}

.ds-btn--full-width { width: 100%; }
.ds-btn--full-width .ds-btn__inner { width: 100%; }

.ds-btn__inner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: auto;
  border-radius: var(--radius-4);
  border-width: var(--stroke-none);
  border-style: solid;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-16);
  line-height: var(--line-default);
  white-space: nowrap;
  transition:
    background-color 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.2s ease,
    color 0.2s ease,
    border-color 0.2s ease,
    border-radius 0.2s ease;
}

.ds-btn:focus-visible .ds-btn__inner {
  border-radius: var(--radius-2);
}

/* --- Sizes (per components/button.md) -------------------------------- */
.ds-btn--large  .ds-btn__inner { padding: var(--space-4) var(--space-7); font-size: var(--type-ar-16); }
.ds-btn--medium .ds-btn__inner { padding: var(--space-3) var(--space-6); font-size: var(--type-ar-14); }
.ds-btn--small  .ds-btn__inner { padding: var(--space-2) var(--space-4); font-size: var(--type-ar-14); }

/* --- Variant: primary (filled blue, with shadow halo) ---------------- */
.ds-btn--primary .ds-btn__inner {
  background-color: var(--interactive-primary-default-bg);
  color: var(--interactive-primary-default-text);
  box-shadow: var(--btn-primary-shadow-default);
}
.ds-btn--primary:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--interactive-primary-hover-bg);
  box-shadow: var(--btn-primary-shadow-hover);
}
.ds-btn--primary:active:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--interactive-primary-pressed-bg);
  box-shadow: var(--btn-primary-shadow-pressed);
}
.ds-btn--primary:disabled .ds-btn__inner,
.ds-btn--primary[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--interactive-primary-disabled-bg);
  color: var(--interactive-primary-disabled-text);
  box-shadow: var(--value-none);
}

/* --- Variant: secondary (outlined blue) ------------------------------ */
.ds-btn--secondary .ds-btn__inner {
  background-color: var(--interactive-secondary-default-bg);
  color: var(--interactive-secondary-default-text);
  border-width: var(--stroke-1);
  border-color: var(--interactive-secondary-default-border);
}
.ds-btn--secondary:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  color: var(--interactive-secondary-hover-text);
  border-color: var(--interactive-secondary-hover-border);
}
.ds-btn--secondary:disabled .ds-btn__inner,
.ds-btn--secondary[aria-disabled="true"] .ds-btn__inner {
  color: var(--interactive-secondary-disabled-text);
  border-color: var(--interactive-secondary-disabled-border);
}

/* --- Variant: tertiary (text only, ghost) ---------------------------- */
.ds-btn--tertiary .ds-btn__inner {
  background-color: var(--interactive-tertiary-default-bg);
  color: var(--interactive-tertiary-default-text);
}
.ds-btn--tertiary:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  color: var(--interactive-tertiary-hover-text);
}
.ds-btn--tertiary:active:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  color: var(--interactive-tertiary-pressed-text);
}
.ds-btn--tertiary:disabled .ds-btn__inner,
.ds-btn--tertiary[aria-disabled="true"] .ds-btn__inner {
  color: var(--interactive-tertiary-disabled-text);
}

/* --- Variant: danger (filled red — radicalRed family) ----------------- */
.ds-btn--danger .ds-btn__inner {
  background-color: var(--interactive-danger-default-bg);
  color: var(--interactive-danger-default-text);
}
.ds-btn--danger:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--interactive-danger-hover-bg);
  color: var(--interactive-danger-hover-text);
}
.ds-btn--danger:disabled .ds-btn__inner,
.ds-btn--danger[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--interactive-danger-disabled-bg);
  color: var(--interactive-danger-disabled-text);
}

/* --- Variant: success (filled green — "Activate", "Approve") ---------
   SINGLE SOURCE OF TRUTH for the green CTA: a FLAT green-600 fill + a two-layer
   green-tinted drop-shadow (the Figma decision-bar look, node 2621:969), hover
   to green-500 with a deeper shadow, and a dimmed disabled. As of 2026-06-05
   panel-shell.css no longer overrides this (its premium gradient was removed)
   and the per-container copies under `.ds-step-actions` are gone, so this block
   alone styles every `.ds-btn--success` across the app — identically. Shadow
   tinted from the family token, never raw hex (0/6/6 @6% + 0/12/12 @8%;
   0.375rem = 6px, 0.75rem = 12px). Its amber twin `.ds-btn--warning` mirrors it
   exactly so the approve/request-changes pair always matches. */
.ds-btn--success .ds-btn__inner {
  background-color: var(--color-green-600);
  color: var(--color-green-50);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-green-600) 6%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-green-600) 8%, transparent);
}
.ds-btn--success:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-green-500);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-green-600) 8%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-green-600) 12%, transparent);
}
/* Disabled (Figma 2877:1652, 2026-06-13): a SOLID lighter fill — green-400
   surface + green-200 label/icon (currentColor) — with NO shadow at all.
   Replaces the old same-colour + 40%-opacity dim. */
.ds-btn--success:disabled .ds-btn__inner,
.ds-btn--success[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--color-green-400);
  color: var(--color-green-200);
  box-shadow: none;
}

/* --- Variant: warning (filled amber — THE orange CTA, e.g. "طلب تعديل") ---
   SINGLE SOURCE OF TRUTH for the orange button. Mirrors `.ds-btn--success`
   exactly (swap green→brightSun): a FLAT brightSun-600 fill + a two-layer
   brightSun-tinted drop-shadow (Figma node 2621:976), hover to brightSun-700
   with a deeper shadow, dimmed disabled. The icon inherits the light label
   colour via `currentColor` in the `.ds-btn__icon` mask.
   2026-06-05: consolidated. panel-shell.css no longer overrides this, and the
   old per-container duplicates (`.ds-step-actions .ds-btn--warning`,
   `.ds-cert-rc-foot__actions .ds-btn--warning`) + the dead `.ds-cert-rc-btn`
   one-off are gone. So EVERY orange button — decision bars, the cert
   request-changes footer, the qualifications footer, the dialog confirm — gets
   this one definition, hover + shadow included. */
.ds-btn--warning .ds-btn__inner {
  background-color: var(--color-brightSun-600);
  color: var(--color-brightSun-50);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-brightSun-600) 6%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-brightSun-600) 8%, transparent);
}
.ds-btn--warning:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-brightSun-500);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-brightSun-600) 8%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-brightSun-600) 12%, transparent);
}
/* Disabled (Figma 2877:1658, 2026-06-13): solid brightSun-400 surface +
   brightSun-200 label/icon, NO shadow — mirrors the success twin. */
.ds-btn--warning:disabled .ds-btn__inner,
.ds-btn--warning[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--color-brightSun-400);
  color: var(--color-brightSun-200);
  box-shadow: none;
}

/* The BLUE twin, same 600/500/400 ladder in cerulean. `ds_alert` names its
   action button after its own variant (`.ds-btn--{variant}`), so an alert in a
   variant with no button rule renders a TRANSPARENT control with black text —
   which is what «بانتظار الطبيب» shipped as the moment it stopped being a
   warning. A variant that can host an action must have its button defined, or
   the alert quietly loses the affordance it is built around. */
.ds-btn--info .ds-btn__inner {
  background-color: var(--color-cerulean-600);
  color: var(--color-cerulean-50);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-cerulean-600) 6%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-cerulean-600) 8%, transparent);
}
.ds-btn--info:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-cerulean-500);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-cerulean-600) 8%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-cerulean-600) 12%, transparent);
}
.ds-btn--info:disabled .ds-btn__inner,
.ds-btn--info[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--color-cerulean-400);
  color: var(--color-cerulean-200);
  box-shadow: none;
}

/* --- Button icon glyph (mask-based, takes `currentColor`) ------------ */
/* 2026-05-28 — replaces inline `<img src="...svg">` inside button
   labels. `<img>` loads the SVG as a sandboxed image so `fill`/`stroke`
   colours bake in from the SVG file itself; that's why the reject /
   edit / approve icons rendered at three different colours despite
   sitting in three identically-sized buttons. The mask approach
   re-paints the SVG silhouette with the button's text colour, so an
   icon on a green Approve button is green-50 white, the icon on a red
   Reject button is red-50 white, etc. — all driven by `currentColor`.
   Size is fixed at 20px (icon-size-20 token) so the three action
   buttons line up visually. */
.ds-btn__icon {
  display: inline-block;
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  background-color: currentColor;
  -webkit-mask-position: center;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-position: center;
  mask-repeat: no-repeat;
  mask-size: contain;
  flex-shrink: 0;
  vertical-align: middle;
}

/* --- Trailing-icon layout (exception, Figma 2700:4703) ------------------
   Default buttons put the icon on the LEADING (right, RTL) edge. This modifier
   reverses that: the icon sits on the TRAILING (left, RTL) edge with the label
   on the reading (right) edge. Used on the qualifications «send change requests»
   CTA, whose directional send glyph is also mirrored (transform: scaleX(-1) on
   the icon, set inline) so it still points outward. */
.ds-btn--icon-end .ds-btn__inner { flex-direction: row-reverse; }


/* ============================================================================
   FIELD — components/fields/README.md
   ============================================================================
   Anatomy:
     <div class="ds-field [ds-field--error|--warning|--readonly|--disabled]">
       <label class="ds-field__label">…</label>
       <div class="ds-field__ring">                <-- transparent border ring
         <div class="ds-field__card">              <-- bg + radius (the card)
           <div class="ds-field__row">             <-- icon + input + status
             [icon] <input class="ds-field__input"> [status]
           </div>
         </div>
       </div>
       <p class="ds-field__message">…</p>
     </div>

   Default state:
     ring   — 1.5px transparent border, padding-0_5, radius-4
     card   — bg=field-default-bg, padding=space-4, radius-4

   Focus-within:
     ring   — border becomes field-focused-border (blue-600)
     card   — radius drops to radius-2 (the inner radius nests inside the
              outer radius — see foundations/radius-and-stroke.md).

   The transparent default border means the focus ring appears with **zero
   layout shift** — same width before and after.
*/

.ds-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* spacing between ring and below-field message */
  width: 100%;
}

.ds-field__label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--field-default-label);
  /* `--line-100` (= 1) collapses leading so the label's box equals its
     font-size. With no extra leading above/below, the parent's flex `gap`
     (var(--space-4) = 1rem = 16px) IS the true visible distance between
     the label glyphs and the input row. */
  line-height: var(--line-100);
  margin: 0;
}

/* Required marker — the red asterisk ICON (asterisk-02.svg, masked → its red)
   pinned to the FAR edge (far-left in RTL) of the label row at the top of the
   field, 16×16. Only required-field labels become a space-between flex row
   (`:has`) so the marker travels to the far side regardless of label length;
   non-required labels are untouched. The literal "*" text node is hidden
   (font-size:0) — the mask draws the glyph. */
.ds-field__label:has(.ds-field__required) {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.ds-field__required {
  flex-shrink: 0;
  width: var(--icon-size-16);
  height: var(--icon-size-16);
  /* Hide the literal "*" glyph robustly (font-size:0 can be clamped by a
     browser minimum-font-size); the mask draws the red asterisk from the
     background fill — asterisk-02.svg's own red is radicalRed-600. */
  font-size: 0;
  color: transparent;
  line-height: 0;
  background-color: var(--color-radicalRed-600);
  -webkit-mask: url(/static/icons/asterisk-02.svg) center / contain no-repeat;
          mask: url(/static/icons/asterisk-02.svg) center / contain no-repeat;
  margin-inline-start: var(--space-1);
}

.ds-field__ring {
  padding: var(--space-0_5);
  border-radius: var(--radius-4);
  border-width: var(--stroke-1_5);
  border-style: solid;
  border-color: var(--value-transparent);
  background: var(--value-transparent);
  transition: border-color 0.15s ease;
}

/* Card stacks the label and the input row vertically. Gap = 12px
   (--space-3 = 0.75rem) per panel spec; padding stays 16px. Label sits
   INSIDE the card so focus/error borders wrap label + row as one unit.
   `height: 5.5rem` (88px) gives every field — text, dropdown, search —
   the same vertical footprint so they line up perfectly in a row.
   Breakdown: 16 padTop + 14 label + 12 gap + 24 row + 16 padBot = 82px,
   the remaining 6px is breathing room for variance in font metrics. */
.ds-field__card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);   /* 0.75rem = 12px */
  padding: var(--space-4);
  height: 5.5rem;        /* 88px — uniform across every field */
  box-sizing: border-box;
  border-radius: var(--radius-4);
  background-color: var(--field-default-bg);
  transition:
    background-color 0.15s ease,
    border-radius 0.15s ease;
}

.ds-field__ring:focus-within {
  border-color: var(--field-focused-border);
}
/* Inner card switches to a smaller radius whenever the outer ring shows a
   border (focus, error, warning, focused_filled). Matches the Figma
   two-layer ring+card geometry — outer radius 4, inner radius 2 once a
   1.5px border is painted on the outer. */
.ds-field__ring:focus-within .ds-field__card,
.ds-field--error .ds-field__card,
.ds-field--warning .ds-field__card,
.ds-field--focused-filled .ds-field__card,
.ds-field--focused_filled .ds-field__card {
  border-radius: var(--radius-2);
}

.ds-field__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* Figma fixes the input row at 24px (h-[24px] on the row in
     WorkplaceTextField / SubSpecialtyDropdownField). Setting an explicit
     min-height keeps every field the same height — text inputs without
     icons, inputs with icons, and dropdowns all line up at 24px row
     height regardless of content. */
  min-height: var(--icon-size-24);
}

.ds-field__input {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  background: var(--value-transparent);
  border: var(--value-none);
  outline: var(--value-none);
  padding: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  line-height: var(--line-default);
  color: var(--field-default-value);
}
.ds-field__input::placeholder {
  color: var(--field-default-placeholder);
  opacity: var(--opacity-80); /* Figma default placeholder is 80% (was 60%) */
}
/* STANDING RULE (Mohammed 2026-07-07): on an Arabic (RTL) panel EVERY text
   field's VALUE + PLACEHOLDER aligns right-to-left by default. The placeholder
   follows the input's text-align, so this one rule covers both. Genuinely
   latin/machine fields opt OUT with an explicit dir="ltr" (field keys, slugs,
   JSON editors, English-name inputs, db table/column, URLs) and are excluded
   here. Covers <input> and <textarea> — both carry .ds-field__input. */
html[dir="rtl"] .ds-field__input:not([dir="ltr"]) {
  direction: rtl;
  text-align: right;
}
/* User 2026-07-07: even the LTR *single-line* value INPUTS (English names,
   slugs, keys, URLs, db table/column) hug the RIGHT edge under their
   right-aligned labels, so an RTL form reads consistently — the value keeps
   typing left-to-right (dir="ltr" preserved) but sits on the right. Multi-line
   code/JSON lives in <textarea> and is deliberately NOT matched here, so it
   stays LTR left-aligned and reads naturally. */
html[dir="rtl"] input.ds-field__input[dir="ltr"] {
  text-align: right;
}
/* Disabled placeholder color is already dim — drop the opacity so it
   doesn't double-dim and disappear. Figma renders disabled placeholder
   at full opacity using blackHaze-200 directly. */
.ds-field--disabled .ds-field__input::placeholder {
  opacity: var(--opacity-100);
}

/* Native select dropdown — drop the OS chevron, render our own. */
.ds-field__input--select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("/static/icons/arrow-down-01.svg");
  background-repeat: no-repeat;
  background-position: right var(--space-1) center;
  background-size: var(--icon-size-16);
  padding-inline-end: var(--space-6);
  cursor: pointer;
}
[dir="rtl"] .ds-field__input--select {
  background-position: left var(--space-1) center;
}

/* --- Error -------------------------------------------------------------- */
.ds-field--error .ds-field__label              { color: var(--field-error-label); }
.ds-field--error .ds-field__ring               { border-color: var(--field-error-border); }
.ds-field--error .ds-field__card               { background-color: var(--field-error-bg); }
.ds-field--error .ds-field__input              { color: var(--field-error-value); }

/* --- Warning ------------------------------------------------------------ */
.ds-field--warning .ds-field__label            { color: var(--field-warning-label); }
.ds-field--warning .ds-field__ring             { border-color: var(--field-warning-border); }
.ds-field--warning .ds-field__card             { background-color: var(--field-warning-bg); }
.ds-field--warning .ds-field__input            { color: var(--field-warning-value); }

/* --- Success ------------------------------------------------------------ */
.ds-field--success .ds-field__label            { color: var(--field-success-label); }
.ds-field--success .ds-field__card             { background-color: var(--field-success-bg); }
.ds-field--success .ds-field__input            { color: var(--field-success-value); }
/* Figma success state has a wider gap (16px) between the leading tick
   status icon and the value+trailing-icon group. */
.ds-field--success .ds-field__row              { gap: var(--space-5); }
.ds-field--success .ds-field__row > :not(:first-child):not(.ds-field__status) {
  /* leave the trailing icon snug to the value */
  margin-inline-start: 0;
}

/* --- Info --------------------------------------------------------------- */
/* Previously missing entirely. Visual state with a below-field info
   message — the field itself stays neutral (blackHaze-50 bg, no border)
   but the FieldMessage below renders in cerulean. */
.ds-field--info .ds-field__label               { color: var(--field-info-label); }
.ds-field--info .ds-field__card                { background-color: var(--field-info-bg); }
.ds-field--info .ds-field__input               { color: var(--field-info-value); }

/* --- Focused + Filled --------------------------------------------------- */
/* User has typed something AND the field is still focused. Same chrome
   as `focused` (1.5px blue ring, radius-2 card) plus a clear-× affordance.
   Two class spellings supported so callers can use either dash or
   underscore (the CSS token uses underscore). */
.ds-field--focused-filled .ds-field__ring,
.ds-field--focused_filled .ds-field__ring     { border-color: var(--field-focused_filled-border); }
.ds-field--focused-filled .ds-field__card,
.ds-field--focused_filled .ds-field__card     { background-color: var(--field-focused_filled-bg); }
.ds-field--focused-filled .ds-field__input,
.ds-field--focused_filled .ds-field__input    { color: var(--field-focused_filled-value); }

/* --- Readonly ----------------------------------------------------------- */
.ds-field--readonly .ds-field__label           { color: var(--field-readonly-label); }
.ds-field--readonly .ds-field__card            { background-color: var(--field-readonly-bg); }
.ds-field--readonly .ds-field__input           { color: var(--field-readonly-value); cursor: default; }

/* --- Disabled ----------------------------------------------------------- */
/* Don't apply a global opacity — Figma dims disabled fields purely by
   token colour (blackHaze-300 label, blackHaze-200 placeholder). Adding
   opacity on top makes the field "double-dim" and harder to read. */
.ds-field--disabled .ds-field__label           { color: var(--field-disabled-label); }
.ds-field--disabled .ds-field__card            { background-color: var(--field-disabled-bg); cursor: not-allowed; }
.ds-field--disabled .ds-field__input           { color: var(--field-disabled-value); cursor: not-allowed; }


/* Inline status message under a field (FieldMessage — components/field-message.md).
   Matches the Figma WorkplaceTextField below-field message: a boxed alert
   (padding 16px + radius 4px) coloured per kind. Outer field already has
   2px breathing room from the ring padding, so the message sits flush
   against the input box visually.

   In Figma the message rendered for `--default`/`help` text has no
   background (transparent) — only error/warning/info/success show the
   coloured tint. */
.ds-field__message {
  display: flex;
  align-items: center;
  gap: var(--space-3); /* 12px between icon and text (Figma --spacing/3) */
  padding: var(--space-4); /* 16px Figma --sds-size-space-400 */
  border-radius: var(--radius-4);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  line-height: var(--line-default);
  color: var(--text-placeholder);
  margin: 0;
  background: var(--value-transparent);
}
.ds-field__message--error {
  background-color: var(--feedback-error-bg);
  color: var(--feedback-error-message);
}
.ds-field__message--warning {
  background-color: var(--feedback-warning-bg);
  color: var(--feedback-warning-message);
}
.ds-field__message--success {
  background-color: var(--feedback-success-bg);
  color: var(--feedback-success-message);
}
.ds-field__message--info {
  background-color: var(--feedback-info-bg);
  color: var(--feedback-info-message);
}


/* ============================================================================
   DROPDOWN — custom select with search + options panel
   Figma node 2318:4017 (SubSpecialtyDropdownField)

   Re-uses the .ds-field__ring + .ds-field__card geometry, then adds:
   - .ds-dropdown__trigger  — clickable wrapper around the ring (cursor:
                               pointer + opens the panel on click via JS)
   - .ds-dropdown__row      — overrides .ds-field__row to use
                               justify-content: space-between so the
                               chevron sits on one edge and the
                               value+icon group on the other.
   - .ds-dropdown__panel    — floating white card below the trigger
                               (absolute positioning, z-index dropdown).
   - .ds-dropdown__option   — each row in the options list (h-48,
                               clickable, hover/selected states).
   ============================================================================ */

.ds-dropdown {
  position: relative; /* anchor for the floating panel */
}

/* Hidden input that carries the form value — visually gone. */
.ds-dropdown > input[type="hidden"] {
  display: none;
}

/* Trigger wraps the ring. Makes the whole card clickable + focusable. */
.ds-dropdown__trigger {
  cursor: pointer;
  user-select: none;
}
.ds-dropdown__trigger:focus-visible {
  border-color: var(--field-focused-border);
}
.ds-dropdown__trigger[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: var(--opacity-60);
}

/* Row layout: chevron on one edge, value+icon on the other. Figma uses
   `justify-content: space-between` so the row stretches to the card width
   regardless of value text length. */
.ds-dropdown__row {
  justify-content: space-between;
  width: 100%;
}

/* Chevron — 24x24. Rotates 180° when the panel is open so the V flips up. */
.ds-dropdown__chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-24);
  height: var(--icon-size-24);
  flex-shrink: 0;
  transition: transform 0.18s ease;
}
.ds-dropdown.is-open .ds-dropdown__chevron {
  transform: rotate(180deg);
}

/* Value + trailing-icon group on the leading edge (RIGHT in RTL). */
.ds-dropdown__value-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 8px between value text and icon (Figma --spacing/3) */
}
.ds-dropdown__value {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--field-default-value);
  line-height: var(--line-default);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-dropdown__value--placeholder {
  opacity: var(--opacity-80); /* match input::placeholder opacity */
}
.ds-dropdown__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-24);
  height: var(--icon-size-24);
  flex-shrink: 0;
}

/* ---------------------- Floating panel ----------------------------------- */

.ds-dropdown__panel {
  position: absolute;
  top: calc(100% + var(--space-2)); /* 8px below trigger (matches Figma gap) */
  inset-inline: 0;
  z-index: var(--z-dropdown);
  background-color: var(--bg-card);
  border-radius: var(--radius-4);
  padding: var(--space-4); /* 16px */
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 12px between search, divider, options (Figma --sds-size-space-300) */
  /* Figma uses two layered drop-shadows for depth — first soft, then
     deeper for elevation. */
  box-shadow:
    0 6px 6px color-mix(in srgb, var(--color-blackHaze-1000) 6%, transparent),
    0 12px 12px color-mix(in srgb, var(--color-blackHaze-1000) 12%, transparent);
}
.ds-dropdown__panel[hidden] {
  display: none;
}

/* ---------------------- Search input inside panel ------------------------ */

/* Outer ring (1.5px blue border) — same focus-style chrome the input
   itself shows in the field. The search field is "always focused" in the
   Figma open state. */
.ds-dropdown__search-ring {
  border: var(--stroke-1_5) solid var(--field-focused-border);
  border-radius: var(--radius-2);
  padding: var(--space-0_5); /* 2px outer ring padding */
}
.ds-dropdown__search-row {
  display: flex;
  align-items: center;
  /* No space-between: we want search-icon + input adjacent on the
     leading edge (RIGHT in RTL), × pushed to the trailing edge. */
  height: 2.5rem; /* 40px — Figma h-[40px] */
  padding: 0 var(--space-4); /* 16px horizontal */
  background-color: var(--field-default-bg);
  gap: var(--space-2);
}
.ds-dropdown__search-clear {
  /* Hidden by default. Sibling-selector below reveals it as soon as
     the user has typed something into the input
     (`:not(:placeholder-shown)` matches an input whose own placeholder
     is no longer visible — i.e. it carries a value). */
  display: none;
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  cursor: pointer;
  flex-shrink: 0;
  /* Push the × to the far end (LEFT in RTL) regardless of how wide
     the typed text becomes. */
  margin-inline-start: auto;
}
.ds-dropdown__search-input:not(:placeholder-shown) ~ .ds-dropdown__search-clear {
  display: inline-flex;
}
.ds-dropdown__search-input {
  /* Size to content (not flex-grow) so the placeholder/text sits
     directly adjacent to the leading search icon. The whole row is
     still click-target via the surrounding ring (input itself is
     small but the input expands as the user types via auto-grow on
     `min-width: 0`). */
  flex: 0 1 auto;
  min-width: 0;
  background: var(--value-transparent);
  border: var(--value-none);
  outline: var(--value-none);
  padding: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--field-default-value);
  text-align: start; /* RIGHT in RTL — adjacent to the leading icon */
}
.ds-dropdown__search-input::placeholder {
  color: var(--field-default-placeholder);
  opacity: var(--opacity-80);
}
.ds-dropdown__search-icon {
  display: inline-flex;
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  flex-shrink: 0;
}

/* Divider line between search and options. Figma uses blue-800 at 8% alpha. */
.ds-dropdown__divider {
  height: 1px;
  background-color: var(--color-blue-800);
  opacity: 0.08;
  border-radius: var(--radius-4); /* token-only: system uses 8/4/2/0 */
  margin: 0 var(--space-4); /* 16px horizontal indent like Figma */
}

/* ---------------------- Options list ------------------------------------- */

/* Scroll host — caps the option-list height so a long dropdown (e.g. the 45
   validation rules) scrolls inside a fixed box instead of running tall.
   OverlayScrollbars overlays a themed bar on open (panel.js); native overflow
   is the graceful fallback. overscroll-behavior stops scroll-chaining to the
   page. `.ds-dropdown__options` keeps its own flex+gap, so wrapping it here
   doesn't disturb the option layout. */
.ds-dropdown__scroll {
  max-height: 20rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.ds-dropdown__options {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 12px between options (Figma --spacing/4) */
}

.ds-dropdown__option {
  /* Reset button styles — keep keyboard accessibility. */
  appearance: none;
  background: var(--value-transparent);
  border: var(--value-none);
  cursor: pointer;
  font: inherit;
  text-align: inherit;
  width: 100%;

  display: flex;
  align-items: center;
  /* `flex-start` packs items at the START of the main axis. In RTL
     that's the RIGHT edge — so the badge (first DOM child) sits at
     far right and the text follows directly to its left, separated
     only by `gap`. NOT `space-between` (which would push them to
     opposite edges with empty space in the middle). */
  justify-content: flex-start;
  gap: var(--space-3);
  height: 3rem; /* 48px — Figma h-[48px] */
  padding: var(--space-2) var(--space-4); /* 8px vertical, 16px horizontal */
  border-radius: var(--radius-2);
  color: var(--color-blue-900); /* Figma uses blue-900 for option text */
  transition: background-color 0.12s ease;
}
.ds-dropdown__option:hover,
.ds-dropdown__option:focus-visible {
  background-color: var(--color-blue-50);
  outline: var(--value-none);
}

/* Selected (active) row — Figma node 2321:5189. Uniform blue chrome
   regardless of the per-status palette: the row swaps to blue-50
   background and the label becomes Semi-Bold blue-600. `!important`
   is required to beat the inline `style="color: …"` that carries the
   per-status text colour in the unselected state (Figma node 2321:5198). */
.ds-dropdown__option--selected {
  background-color: var(--color-blue-50);
}
.ds-dropdown__option--selected .ds-dropdown__option-text {
  font-family: var(--font-activist-semibold) !important;
  color: var(--color-blue-600) !important;
}

.ds-dropdown__option-text {
  /* Size to content (don't grow) so the text sits directly adjacent
     to the badge instead of being pushed to the far edge. */
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Icon badge: blue-50 square with the icon inside. Each option in the
   Figma design carries its own small "tag" — gives the list visual
   rhythm and matches the field's leading-icon style. */
.ds-dropdown__option-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem; /* 28px — 20px icon + 4px padding each side */
  height: 1.75rem;
  border-radius: var(--radius-2);
  background-color: var(--color-blue-50);
  flex-shrink: 0;
}

/* ---------------------- Country flag chip ------------------------------
   Circular 24px flag image used by country dropdowns (license-authority
   filter + form). `object-fit: cover` crops the 4:3 flag into the circle;
   the thin outset ring keeps white-edged flags (Japan, etc.) legible on a
   light card. Shared by the option list and the trigger's leading marker. */
.ds-dropdown__option-flag,
.ds-dropdown__flag-img {
  width: var(--icon-size-24); /* 24px */
  height: var(--icon-size-24);
  border-radius: var(--radius-circle, 50%);
  object-fit: cover;
  flex-shrink: 0;
  background-color: var(--color-blackHaze-50);
  box-shadow: 0 0 0 var(--stroke-1) var(--border-divider);
}

/* ---------------------- Status swatch ----------------------------------
   When the caller provides `swatch="<css color>"` on an option (or
   "all" for the multi-colour aggregate), we render a solid coloured
   square INSTEAD of the icon badge. Used by the doctors-list status
   filter where each status carries the same colour as its
   `ds_doctor_status_badge` (success=green, error=red, …) and the
   "All" option pies all five colours together.
*/
/* Both option-swatch and trigger swatch are 24x24 per the user spec.
   Colours come from the Core.Color palette tokens (blue/green/pink/
   cerulean/brightSun/blackHaze/radicalRed) defined at the top of this
   file — so the swatch palette stays in lock-step with the rest of the
   design-system tokens. `inline-flex` so a centred ::after pseudo can
   host the "selected" tick glyph (see selected-state overrides below). */
.ds-dropdown__option-swatch,
.ds-dropdown__icon--swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-24);   /* 24px */
  height: var(--icon-size-24);  /* 24px */
  border-radius: var(--radius-2);
  flex-shrink: 0;
  /* Inset 1px border in body colour for a subtle outline that reads
     against both the white panel and the blue-50 hover background. */
  box-shadow: inset 0 0 0 var(--stroke-1) var(--border-divider);
}

/* Selected option — the marker (swatch OR icon-badge) flips to a
   saturated blue-600 fill with its glyph rendered in blue-50. Mirrors
   Figma node 2321:5189: the active row's leading badge inverts colour.
   `background` shorthand wins over the inline `background-color` AND
   clears the conic-gradient that the `--all` variant ships with.
   For the badge case we ALSO override the inline `background-color`
   that `ds_icon`'s mask span sets, hence `!important` on the inner rule. */

/* --- 1) Swatch variant (used by status dropdown; solid colour squares) --- */
.ds-dropdown__option--selected .ds-dropdown__option-swatch {
  background: var(--color-blue-600) !important;
  box-shadow: var(--value-none);
}
.ds-dropdown__option--selected .ds-dropdown__option-swatch::after {
  content: "";
  display: block;
  width: var(--icon-size-16);
  height: var(--icon-size-16);
  background-color: var(--color-blue-50);
  -webkit-mask: url("/static/icons/tick-02.svg") center / contain no-repeat;
          mask: url("/static/icons/tick-02.svg") center / contain no-repeat;
}

/* --- 2) Icon-badge variant (used by date dropdown — clock icon inside
   blue-50 square). Selected → badge becomes blue-600, icon recolours
   to blue-50 by overriding the mask-span's inline background-color. --- */
.ds-dropdown__option--selected .ds-dropdown__option-badge {
  background-color: var(--color-blue-600);
}
.ds-dropdown__option--selected .ds-dropdown__option-badge .ds-icon-mask {
  background-color: var(--color-blue-50) !important;
}

/* "All" — split the square into five wedges, one per status. Conic
   gradient gives a tight pie-chart look. Colours map 1:1 to the
   solid swatch each individual status uses (Core.Color -500 level). */
.ds-dropdown__option-swatch--all,
.ds-dropdown__icon--swatch--all {
  background: conic-gradient(
    var(--color-blackHaze-400)    0deg  72deg,   /* pending      */
    var(--color-cerulean-500)     72deg 144deg,  /* under_review */
    var(--color-green-500)       144deg 216deg,  /* approved     */
    var(--color-radicalRed-500)  216deg 288deg,  /* rejected     */
    var(--color-brightSun-500)   288deg 360deg   /* needs_edit   */
  );
}

.ds-dropdown__no-match {
  text-align: center;
  color: var(--text-onCard-secondary);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  padding: var(--space-4) 0;
  margin: 0;
}
.ds-dropdown__no-match[hidden] { display: none; }


/* ============================================================================
   CARD — surface for forms, lists, summaries
   ============================================================================
*/
.ds-card {
  background-color: var(--bg-card);
  border-radius: var(--radius-8);
  padding: var(--card-padding);
  box-shadow: 0 0.0625rem 0.125rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 2%, transparent);
}

.ds-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  color: var(--text-onCard-primary);
  margin: 0 0 var(--space-4);
}


/* ============================================================================
   ALERT — components/alert.md
   ============================================================================
*/
.ds-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius-4);
  border-width: var(--stroke-1);
  border-style: solid;
}
.ds-alert__icon { flex-shrink: 0; line-height: 0; }
.ds-alert__body { flex: 1 1 auto; min-width: 0; }
.ds-alert__title {
  margin: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  line-height: var(--line-default);
}
/* A numeral lifted out of the alert's sentence. Arabic-Indic glyphs sit
   markedly smaller than Latin ones at the same font-size, so the one number the
   reviewer scans for — how many fields are missing — reads as a footnote unless
   it is stepped up. `1em`-relative so it tracks whatever the title is set at. */
.ds-alert__num {
  font-family: var(--font-activist-bold);
  font-size: 1.45em;
  line-height: 1;
  vertical-align: -0.06em;
  padding-inline: 0.08em;
}
.ds-alert__desc {
  margin: var(--space-1) 0 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  line-height: var(--line-default);
}
.ds-alert__dismiss {
  flex-shrink: 0;
  background: var(--value-transparent);
  border: var(--value-none);
  padding: 0;
  cursor: pointer;
  line-height: 0;
  opacity: var(--opacity-80);
  transition: opacity 0.15s ease;
}
.ds-alert__dismiss:hover { opacity: var(--opacity-100); }

/* ── Floating toast notifications (#ds-toasts / .ds-toast) ──
   Page-level notifications («تم حفظ القرار», errors…) float CENTRED at the TOP
   of the viewport. Each toast IS a canonical .ds-alert — every colour comes
   from the --feedback-{variant}-* tokens — this layer only adds the floating
   container, the canonical two-layer shadow and enter/exit motion. The
   container is a manual `popover`: the TOP LAYER is the only way to stack
   above an open modal <dialog> (the cert modal is open when decision
   notifications fire — no z-index can beat the top layer); dsToast re-shows
   it per toast so it stays above the most recent dialog. The z-index only
   matters for the no-popover fallback. */
#ds-toasts {
  position: fixed;
  top: var(--space-5);
  inset-inline: 0;
  inset-block-end: auto;
  margin: 0 auto;
  width: max-content;
  max-width: min(92vw, 30rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  /* strip the popover UA defaults (opaque bg, border, padding, inset:0) */
  border: 0;
  padding: 0;
  background: transparent;
  overflow: visible;
  z-index: 1100;
}
#ds-toasts:popover-open { display: flex; }
.ds-toast {
  width: max-content;
  max-width: 100%;
  box-sizing: border-box;
  align-items: center;
  animation: ds-toast-in 0.28s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
/* canonical Two-lay-shdw (Figma) — lifts the card off the page. Doubled-class
   specificity so panel-shell's later .ds-alert micro-shadow doesn't win. */
.ds-alert.ds-toast,
.ds-toast {
  box-shadow:
    0 6px 12px rgba(23, 28, 24, 0.06),
    0 12px 24px rgba(23, 28, 24, 0.12);
}
.ds-toast.is-leaving { animation: ds-toast-out 0.22s ease-in both; }
/* A REPEATED message is refreshed in place instead of stacking a duplicate
   (window.dsToast de-duplicates). The pulse is the acknowledgement the reviewer
   was pressing for — without it the second press looks like nothing happened,
   which is what made the first version of this flood look reasonable. */
.ds-toast--repeat { animation: ds-toast-pulse 0.32s ease-out; }
@keyframes ds-toast-pulse {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.03); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ds-toast--repeat { animation: none; }
}
@keyframes ds-toast-in {
  from { opacity: 0; transform: translateY(-0.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes ds-toast-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-0.5rem); }
}
@media (prefers-reduced-motion: reduce) {
  .ds-toast, .ds-toast.is-leaving { animation: none; }
}


/* ============================================================================
   BADGE — status pill
   ============================================================================
*/
.ds-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  /* Nested-radius rule: container card uses --radius-8, so two levels
     inside (table row → status pill) drops to --radius-2. Keeps the
     8 → 4 → 2 → 0 inward cadence consistent across the panel. */
  border-radius: var(--radius-2);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  white-space: nowrap;
}


/* ============================================================================
   GLOBAL — keep the page from ever scrolling horizontally on small screens.
   Specific containers (.ds-table-wrap, etc.) own their own horizontal
   scroll; the page itself shouldn't shift sideways when one element
   overflows.
   ============================================================================
*/
html,
body {
  max-width: 100%;
  overflow-x: hidden;
}
/* Flex parents in `templates/base.html` (`#main-content` is `flex flex-col`,
   `<main>` is `flex-1`) default to `min-width: auto` on the cross axis,
   meaning they grow to fit the widest child. A wide table inside main
   would then drag the whole page wider than the viewport. Setting
   `min-width: 0` lets these containers stay viewport-bound and forces
   any overflow handling onto the table wrapper itself. */
#main-content,
#main-content > main {
  min-width: 0;
}

/* ============================================================================
   TABLE — wrapper for the panel's lists
   ============================================================================
*/
.ds-table-wrap {
  background-color: var(--bg-card);
  border-radius: var(--radius-8);
  /* Horizontal scroll when the table overflows the viewport (mobile).
     Vertical hidden keeps the rounded corners clipping the top/bottom
     edges of the table content. iOS gets momentum scrolling.
     `width: 100%` + `max-width: 100%` pin the wrapper to its parent
     so the inner <table>'s `min-width: 36rem` (mobile) overflows
     INSIDE the wrapper instead of pushing the wrapper itself wider
     than the viewport. Without these, flex parents (`<main class="flex-1">`)
     would grow to fit the table and the whole page would scroll
     horizontally — leaving the wrapper's `overflow-x: auto` dormant. */
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  box-shadow: 0 0.0625rem 0.125rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 2%, transparent);
}
.ds-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
}
/* Below the desktop sidebar breakpoint, force a comfortable minimum so
   tables don't squash into illegible columns on narrow phones — the
   wrapper above scrolls instead. 36rem ≈ 576px covers every panel
   list (4–7 columns); anything wider just scrolls farther. */
@media (max-width: 767px) {
  .ds-table {
    min-width: 36rem;
  }
}

/* Doctor detail page — two-column issues + preview layout. Below
   ~60rem (≈ 960px) the preview pane is too narrow to be useful, so
   we stack: issues on top, preview underneath. */
@media (max-width: 959px) {
  .ds-doctor-detail-grid {
    grid-template-columns: minmax(0, 1fr) !important;
  }
}
.ds-table thead th {
  padding: var(--space-3) var(--space-4);
  text-align: start;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: var(--text-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-block-end: var(--stroke-1) solid var(--border-divider);
  background-color: var(--bg-card);
}
.ds-table tbody td {
  padding: var(--space-3) var(--space-4);
  color: var(--text-onCard-primary);
  border-block-end: var(--stroke-1) solid var(--border-divider);
  vertical-align: middle;
}
.ds-table tbody tr:last-child td {
  border-block-end: var(--value-none);
}
.ds-table tbody tr:hover {
  background-color: var(--color-blackHaze-50);
}
.ds-table__cell--secondary { color: var(--text-onCard-secondary); }
.ds-table__cell--primary   { color: var(--text-onCard-primary); font-family: var(--font-activist-semibold); }
.ds-table__empty {
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--text-placeholder);
}


/* ============================================================================
   PAGE HEADER
   ============================================================================
*/
.ds-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-6);
}
.ds-page-header__title-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}
.ds-page-header__title {
  margin: 0;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-28);
  color: var(--text-onPage-primary);
  line-height: var(--line-default);
}
.ds-page-header__crumbs {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  flex-wrap: wrap;
}
.ds-page-header__crumb-link {
  color: var(--text-link);
  font-family: var(--font-activist-semibold);
  text-decoration: none;
}
.ds-page-header__crumb-current {
  color: var(--text-onPage-primary);
  font-family: var(--font-activist-semibold);
}
.ds-page-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}


/* ============================================================================
   CHECKBOX
   ============================================================================
*/
.ds-checkbox {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}
.ds-checkbox__input {
  width: 1rem;
  height: 1rem;
  margin-top: 0.125rem;
  accent-color: var(--brand-primary);
  flex-shrink: 0;
  cursor: inherit;
}
.ds-checkbox__label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  color: var(--text-onCard-primary);
}
.ds-checkbox__label-text {
  font-family: var(--font-activist-semibold);
}
.ds-checkbox__help {
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
}
.ds-checkbox--disabled { cursor: not-allowed; opacity: var(--opacity-60); }


/* ============================================================================
   SHELL LAYOUT — Sidebar + Topbar  (panel-redesign 2026-05-12)
   ============================================================================
   These styles refine the admin panel chrome to mirror the Activist-family
   visual rhythm of the doctor-facing FE (login-page project). The structural
   IDs `#sidebar`, `#sidebar-overlay`, `#sidebar-toggle` and the class hook
   `sidebar-open` are PRESERVED — they back panel.js's mobile-toggle logic.
   The dimension tokens `--sidebar-width`, `--topbar-height` are PRESERVED —
   base.html's main-content offset reads them.
*/

/* --- Sidebar shell ----------------------------------------------------- */
.ds-sidebar {
  /* layout + chrome — `position: fixed` + dimensions are still applied
     by base.html / sidebar.html inline so the transform animation keeps
     working in tandem with the mobile-toggle class swap. */
  display: flex;
  flex-direction: column;
  background-color: var(--bg-sidebar);
  border-inline-end: var(--stroke-1) solid var(--border-divider);
}

.ds-sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-inline: var(--space-5);
  height: var(--topbar-height);
  border-block-end: var(--stroke-1) solid var(--border-divider);
}
.ds-sidebar__brand-logo {
  width: 1.75rem;
  height: 2.4rem;
  flex-shrink: 0;
  display: block;
}
.ds-sidebar__brand-text {
  display: flex;
  flex-direction: column;
  gap: 0.075rem;
  min-width: 0;
  line-height: 1;
}
.ds-sidebar__brand-name {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  color: var(--brand-primary);
  line-height: 1;
}
.ds-sidebar__brand-sub {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  line-height: 1;
}

.ds-sidebar__nav {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--space-4) var(--space-3);
  scrollbar-width: thin;
  scrollbar-color: var(--color-blackHaze-200) var(--value-transparent);
}
.ds-sidebar__nav::-webkit-scrollbar { width: 0.375rem; }
.ds-sidebar__nav::-webkit-scrollbar-thumb {
  background: var(--color-blackHaze-200);
  border-radius: var(--radius-4);
}

.ds-sidebar__nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.ds-sidebar__section-heading {
  margin-block-start: var(--space-5);
  margin-block-end: var(--space-1);
  padding-inline: var(--space-3);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.2;
}
/* First section heading shouldn't push down — overrides the top margin. */
.ds-sidebar__section-heading:first-child {
  margin-block-start: 0;
}

.ds-sidebar__footer {
  border-block-start: var(--stroke-1) solid var(--border-divider);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
}

/* --- Side link (every sidebar row, nav + footer) ----------------------- */
.ds-side-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.625rem var(--space-3);
  border-radius: var(--radius-8);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--menu-action-default-text);  /* blue-900 — DS AccountMenu token */
  text-decoration: none;
  line-height: 1;
  transition:
    background-color 0.18s ease,
    color 0.18s ease,
    transform 0.18s ease;
  position: relative;
}
.ds-side-link__icon {
  flex-shrink: 0;
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--menu-action-default-icon);   /* blue-600 */
}
.ds-side-link__icon svg { width: 100%; height: 100%; }
.ds-side-link__label { min-width: 0; flex: 1 1 auto; }

.ds-side-link:hover:not(.ds-side-link--active):not(.ds-side-link--danger) {
  background-color: var(--menu-action-hover-bg);   /* blue-50 */
}
.ds-side-link:active:not(.ds-side-link--active):not(.ds-side-link--danger) {
  background-color: var(--menu-action-pressed-bg); /* blue-100 */
}
.ds-side-link:focus-visible {
  outline: none;
  background-color: var(--menu-action-focus-bg);
  box-shadow: inset 0 0 0 var(--stroke-1_5) var(--menu-action-focus-border);
}

.ds-side-link--active {
  background-color: var(--menu-action-selected-bg);  /* blue-100 */
  color: var(--menu-action-selected-text);           /* blue-800 */
}
.ds-side-link--active .ds-side-link__icon { color: var(--brand-primary); }

.ds-side-link--danger {
  color: var(--menu-logout-default-text);            /* radicalRed-500 */
}
.ds-side-link--danger .ds-side-link__icon {
  color: var(--menu-logout-default-icon);
}
.ds-side-link--danger:hover {
  background-color: var(--menu-logout-hover-bg);     /* radicalRed-50 */
}
.ds-side-link--danger:active {
  background-color: var(--menu-logout-pressed-bg);   /* radicalRed-100 */
}

/* --- Topbar shell ------------------------------------------------------ */
.ds-topbar {
  background-color: var(--bg-card);
  border-block-end: var(--stroke-1) solid var(--border-divider);
}
.ds-topbar__title-block {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.ds-topbar__title {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-16);
  color: var(--text-onCard-primary);
  line-height: 1.2;
  margin: 0;
}
.ds-topbar__subtitle {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  line-height: 1.2;
}

.ds-topbar__icon-btn {
  width: 2.5rem;
  height: 2.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: var(--value-transparent);
  border: var(--value-none);
  border-radius: var(--radius-4);
  color: var(--text-onCard-primary);
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.ds-topbar__icon-btn:hover { background-color: var(--color-blackHaze-50); }

/* Region badge — same blue-50 chip pattern as DS Header.lang-pill */
.ds-topbar__region {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.25rem var(--space-2);
  background-color: var(--color-blue-50);
  color: var(--brand-primary);
  border-radius: var(--radius-4);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
}

/* Locale switch — outlined chip */
.ds-topbar__lang {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.375rem var(--space-3);
  background-color: var(--value-transparent);
  color: var(--header-lang-label);
  border: var(--stroke-1) solid var(--border-divider);
  border-radius: var(--radius-4);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  text-decoration: none;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.ds-topbar__lang:hover {
  border-color: var(--header-lang-border);
  background-color: var(--color-blue-50);
}

/* --- User avatar + dropdown (zero-JS, via <details>) ------------------- */
.ds-user-menu {
  position: relative;
}
.ds-user-menu__trigger {
  list-style: none;          /* Firefox */
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.25rem;
  padding-inline-end: var(--space-2);
  border-radius: var(--radius-8);
  border: var(--stroke-1) solid var(--value-transparent);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.ds-user-menu__trigger::-webkit-details-marker { display: none; }
.ds-user-menu__trigger::marker { display: none; }
.ds-user-menu[open] > .ds-user-menu__trigger,
.ds-user-menu__trigger:hover {
  background-color: var(--color-blue-50);
  border-color: var(--border-divider);
}
.ds-user-menu__avatar {
  width: var(--avatar-size, 2.25rem);
  height: var(--avatar-size, 2.25rem);
  border-radius: 999px;
  background-color: var(--avatar-male-bg);
  color: var(--avatar-male-icon);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-14);
  line-height: 1;
  flex-shrink: 0;
}
.ds-user-menu__name {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--text-onCard-primary);
  line-height: 1.2;
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ds-user-menu__chevron {
  width: var(--icon-size-16);
  height: var(--icon-size-16);
  color: var(--avatar-arrow-color);
  transition: transform 0.2s ease;
}
.ds-user-menu[open] .ds-user-menu__chevron { transform: rotate(180deg); }

.ds-user-menu__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  inset-inline-end: 0;
  min-width: 14rem;
  background-color: var(--avatar-dropdown-bg);
  border-radius: var(--avatar-dropdown-radius);
  box-shadow: var(--avatar-dropdown-shadow);
  padding: var(--space-2);
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  gap: var(--space-0_5);
  animation: dsUserMenuIn 0.18s ease-out both;
}
@keyframes dsUserMenuIn {
  from { opacity: 0; transform: translateY(-0.25rem); }
  to   { opacity: 1; transform: translateY(0); }
}
.ds-user-menu__header {
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-block-end: var(--stroke-1) solid var(--avatar-dropdown-divider);
  margin-block-end: var(--space-2);
}
.ds-user-menu__header-name {
  display: block;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--text-onCard-primary);
  line-height: 1.2;
}
.ds-user-menu__header-email {
  display: block;
  margin-block-start: 0.125rem;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  line-height: 1.2;
  word-break: break-all;
}

/* ===== Row overflow ("More") menu — kebab in list-table action cells =====
   The Delete (destructive) action is tucked behind a ⋯ trigger. Uses the
   NATIVE Popover API so the panel renders in the top layer and the
   `.ds-table-wrap` overflow can't clip it; panel.js positions the panel
   under the trigger (no reliable CSS anchor-positioning yet). Light-dismiss
   + Esc come free with `popover`. */
.ds-row-menu {
  display: inline-flex;
}
.ds-row-menu__panel {
  /* reset UA popover chrome; JS sets position: fixed + top/left on open */
  margin: 0;
  /* Neutralise the UA `[popover]{inset:0}` — otherwise on an RTL page the
     leftover `right:0` WINS over JS's inline `left`, pinning the panel to the
     viewport's right edge instead of under a left-positioned trigger. With
     inset:auto, JS's top/left control the position in BOTH directions. */
  inset: auto;
  padding: var(--space-1);
  min-width: 9rem;
  background-color: var(--bg-card);
  border: var(--stroke-1) solid var(--border-divider);
  border-radius: var(--radius-8);
  box-shadow: var(--avatar-dropdown-shadow);
}
.ds-row-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-4);
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: start;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  color: var(--text-onCard-primary);
}
/* Tailwind's `@layer base { button{font-family:inherit} }` resolves a <button>
   row-item to SemiBold (an <a> stays Regular), so a mixed menu looks half-bold.
   Pin the family/weight with !important — the project convention for defeating
   the base resets (see panel-shell.css) — on the item AND its text span, so
   every row is ONE font regardless of tag. */
.ds-row-menu__item,
.ds-row-menu__item span {
  font-family: var(--font-activist-regular) !important;
  font-weight: 400 !important;
}
/* Hover: blue-50 tint + blue-600 ink (Mohammed 2026-07-07 — the subtle
   blackHaze-50 was near-invisible on the white menu card). Every row menu. */
.ds-row-menu__item:hover {
  background-color: var(--color-blue-50);
  color: var(--color-blue-600);
}
/* Delete option — SUBTLE danger: soft radicalRed-50 fill (always) + red-600
   ink, full width (the item is already width:100%). Hover deepens one step. */
.ds-row-menu__item--danger {
  color: var(--color-radicalRed-600);
  background-color: var(--color-radicalRed-50);
}
.ds-row-menu__item--danger:hover {
  background-color: var(--color-radicalRed-100);
  color: var(--color-radicalRed-700);
}
/* Delete option carries a «delete» glyph before its label — icon-first like
   every other row. Masked to `currentColor` so it inherits the danger ink
   (radicalRed-600, → -700 on hover). No trash-can in the icon set; `remove-square`
   is the closest OUTLINE «remove» mark (swap for a Figma trash export if added). */
.ds-row-menu__item--danger::before {
  content: "";
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/remove-square.svg) center / contain no-repeat;
  mask: url(/static/icons/remove-square.svg) center / contain no-repeat;
}

/* ===== Icon-only action button (ds_icon_btn macro) =========================
   Figma 2987:2300 (edit) / 3013:1024 (stop) / 2962:1089 (more). Single source
   of truth — one edit here updates every page using the macro. Outer frame:
   variant border + 2px pad + 4px radius. Inner box: variant-50 bg + 8px pad +
   2px radius. 16px glyph masked to the variant ink, so one icon serves any
   variant colour. */
.ds-iconbtn {
  display: inline-flex;
  box-sizing: border-box;
  inline-size: 2rem; /* 32px — Figma fixed size */
  block-size: 2rem;
  padding: var(--space-0_5); /* Figma spacing/1 = 2px */
  border: var(--stroke-1) solid transparent;
  border-radius: var(--radius-4); /* 4px */
  background: transparent;
  cursor: pointer;
  flex: none;
}
.ds-iconbtn__box {
  display: inline-flex;
  flex: 1 0 0;
  align-self: stretch;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-2); /* 2px */
}
.ds-iconbtn__glyph {
  inline-size: 1rem; /* 16px */
  block-size: 1rem;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.ds-iconbtn--primary {
  border-color: var(--color-blue-600);
}
.ds-iconbtn--primary .ds-iconbtn__box {
  background-color: var(--color-blue-50);
}
.ds-iconbtn--primary .ds-iconbtn__glyph {
  background-color: var(--color-blue-600);
}
.ds-iconbtn--danger {
  border-color: var(--color-radicalRed-600);
}
.ds-iconbtn--danger .ds-iconbtn__box {
  background-color: var(--color-radicalRed-50);
}
.ds-iconbtn--danger .ds-iconbtn__glyph {
  background-color: var(--color-radicalRed-600);
}
.ds-iconbtn--success {
  border-color: var(--color-green-600);
}
.ds-iconbtn--success .ds-iconbtn__box {
  background-color: var(--color-green-50);
}
.ds-iconbtn--success .ds-iconbtn__glyph {
  background-color: var(--color-green-600);
}
.ds-iconbtn:hover .ds-iconbtn__box {
  filter: brightness(0.96);
}
.ds-iconbtn:focus-visible {
  outline: var(--stroke-2, 0.125rem) solid var(--color-blue-600);
  outline-offset: 0.0625rem;
}
/* Solid fill (Figma 3015:1051 "play"/enable): the whole 32px button is the
   variant colour — no border, no inner tint, a light glyph. */
.ds-iconbtn--solid {
  padding: var(--space-2); /* 8px → 16px icon + 8px*2 = 32px */
  border: 0;
}
.ds-iconbtn--solid .ds-iconbtn__box {
  background-color: transparent;
}
.ds-iconbtn--solid.ds-iconbtn--primary {
  background-color: var(--color-blue-600);
}
.ds-iconbtn--solid.ds-iconbtn--primary .ds-iconbtn__glyph {
  background-color: var(--color-blue-50);
}
.ds-iconbtn--solid:hover {
  filter: brightness(0.96);
}

/* --- Page section header (smaller than ds-page-header — for inline use)  */
.ds-section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  color: var(--text-onCard-primary);
  line-height: 1.2;
}
.ds-section-title__icon {
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  color: var(--brand-primary);
  display: inline-flex;
}

/* --- Stat card (used by dashboard tiles) ------------------------------- */
.ds-stat-card {
  background-color: var(--bg-card);
  border-radius: var(--radius-8);
  padding: var(--space-5);
  box-shadow: 0 0.0625rem 0.125rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 2%, transparent);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.ds-stat-card:hover {
  transform: translateY(-0.125rem);
  box-shadow:
    0 0.5rem 1rem 0 color-mix(in srgb, var(--color-blue-600) 4%, transparent),
    0 0.125rem 0.25rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 3%, transparent);
}
.ds-stat-card__label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: var(--text-placeholder);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 0;
  line-height: 1.2;
}
.ds-stat-card__value {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-32);
  color: var(--text-onCard-primary);
  line-height: 1;
  margin: 0;
}
.ds-stat-card__accent {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-8);
  background-color: var(--color-blue-50);
  color: var(--brand-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.ds-stat-card__accent svg { width: 1.25rem; height: 1.25rem; }

/* Variants — re-tint accent tile by feedback tone */
.ds-stat-card--warning .ds-stat-card__accent {
  background-color: var(--feedback-warning-bg);
  color: var(--feedback-warning-icon);
}
.ds-stat-card--info .ds-stat-card__accent {
  background-color: var(--feedback-info-bg);
  color: var(--feedback-info-icon);
}
.ds-stat-card--success .ds-stat-card__accent {
  background-color: var(--feedback-success-bg);
  color: var(--feedback-success-icon);
}
.ds-stat-card--error .ds-stat-card__accent {
  background-color: var(--feedback-error-bg);
  color: var(--feedback-error-icon);
}

/* --- Mobile sidebar overlay tint --------------------------------------- */
#sidebar-overlay {
  /* The base.html supplies the layout (`fixed inset-0`, z-index from
     var(--z-overlay)). Just refine the visual to match the FE's dim. */
  background-color: color-mix(in srgb, var(--color-blackHaze-1000) 40%, transparent);  /* color-blackHaze-1000 @ 40% */
  backdrop-filter: blur(2px);
  transition: opacity 0.18s ease;
}

/* ============================================================================
   TOOLTIP — `[data-tip="…"]` utility
   ============================================================================
   Add `data-tip="text"` to any non-replaced element (span/button/a/div) and
   a styled bubble appears above it on hover/focus. CSS-only, zero JS, GPU
   transitions. Works in RTL and LTR — the bubble is horizontally centred
   on the host so writing-direction doesn't matter.

   - Host MUST be a non-replaced element (img can't host ::after — wrap it
     in a <span data-tip="…">).
   - Bubble = `::after` (text), arrow = `::before` (tiny rotated square).
   - Both are hidden by default and faded in via opacity transition.
   - z-index sits above table/card chrome but below modals.
*/
[data-tip] {
  position: relative;
}
[data-tip]::after,
[data-tip]::before {
  position: absolute;
  bottom: calc(100% + var(--space-2)); /* 8px gap above the host */
  left: 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: var(--z-dropdown, 50);
}
[data-tip]::after {
  content: attr(data-tip);
  transform: translateX(-50%) translateY(0.25rem);
  background-color: var(--color-blackHaze-900);
  color: var(--color-white-50);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  line-height: 1.4;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-4);
  white-space: nowrap;
  box-shadow: 0 0.25rem 0.75rem 0 color-mix(in srgb, var(--color-blackHaze-1000) 12%, transparent);
}
[data-tip]::before {
  content: "";
  width: var(--space-2);
  height: var(--space-2);
  bottom: calc(100% + var(--space-1));
  transform: translateX(-50%) rotate(45deg);
  background-color: var(--color-blackHaze-900);
}
[data-tip]:hover::after,
[data-tip]:hover::before,
[data-tip]:focus-visible::after,
[data-tip]:focus-visible::before {
  opacity: 1;
}
[data-tip]:hover::after,
[data-tip]:focus-visible::after {
  transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  [data-tip]::after,
  [data-tip]::before {
    transition: none;
  }
}

/* Verified-doctor wrapper — small inline-flex host so the badge stays
   inline with the name AND can carry the data-tip tooltip above. */
.ds-verified-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: help;
}

/* ============================================================================
   STEP ACCORDION — per-step review card (Figma node 2621:922 "مراجعة الخطوات")
   Used by `templates/doctors/detail.html` for the per-step review cards.

   Rebuilt 2026-05-30 to match the Figma "Step review" spec across its five
   states (pending → in-review → approved → needs-changes → rejected). All
   measurements map Figma pixel values onto the project token scale BY VALUE
   (note: Figma's Spacing/N is offset one step from --space-N, e.g. Figma
   Spacing/5 = 16px = --space-4). Colours come straight from the palette /
   feedback tokens, never raw hex.

   Header anatomy (RTL: reads right → left):
       [num/state chip] [title] ...flex gap... [status pill] [toggle box]
   Chip + title sit at the start edge (right); the status pill and the +/−
   toggle box sit at the end edge (left). The native <details> marker is
   hidden; the collapsed/open glyph swap is pure CSS via [open].
   ============================================================================ */

/* ── Shell: the real selection-ring border (NOT outline/absolute) ──────────
   Figma wraps every step in an outer frame that, WHEN OPEN, paints a 2px
   blue-600 ring at radius-8 with an 8px inset gap (p-2) before the white
   card (node 2621:951). We model that with a real `border` on a wrapper so
   the ring participates in layout (occupies the 8px), exactly per the design
   — no `outline`, no `position:absolute`. Collapsed steps carry a transparent
   2px border of the same width so the card never shifts when toggled. */
.ds-step-shell {
  border-radius: var(--radius-8);
  padding: 0;
  /* The collapsed shell carries NO border so the gap between step cards equals
     the list's 16px flex gap exactly (a real transparent border used to add
     2px×2 = 4px, pushing the visible gap to 20px). The open-state selection
     ring is a box-shadow — it paints but doesn't occupy layout, so neighbours
     stay a clean 16px apart. */
  transition: box-shadow 200ms ease, padding 200ms ease;
}
.ds-step-shell:has(> details[open]) {
  padding: var(--space-2);  /* 8px gap between the ring and the inner card */
  box-shadow: 0 0 0 var(--stroke-2) var(--color-blue-600);
}

/* The card itself: .ds-card supplies bg; here we pin radius + shadow to the
   Figma step-card spec. CLOSED card = radius-8 (node 2621:938); OPEN inner
   card = radius-4 (node 2621:952) nested inside the radius-8 ring. Both carry
   the two-layer cararra-950 drop shadow (0/6/6 @6% + 0/12/12 @12%). Padding is
   zeroed inline so the summary + body own their own insets. */
.ds-step-accordion {
  border-radius: var(--radius-8);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-cararra-950) 6%, transparent),
    0 0.75rem 0.75rem  color-mix(in srgb, var(--color-cararra-950) 12%, transparent);
  /* `interpolate-size` lets the open/close height tween below animate to/from
     the `auto` keyword (Chromium 129+). Inherited down to ::details-content. */
  interpolate-size: allow-keywords;
  transition: border-radius 200ms ease, box-shadow 200ms ease;
}
.ds-step-accordion[open] {
  border-radius: var(--radius-4);
}

/* ── Smooth open / close disclosure ────────────────────────────────────
   Native <details> snaps open instantly. We animate the reveal by tweening
   the ::details-content box from block-size:0 → auto (needs the
   `interpolate-size: allow-keywords` set on the host above) with a synced
   opacity fade and a small upward settle. `content-visibility … allow-discrete`
   keeps the body painted through the collapse so the CLOSE direction animates
   too — not just open. Engines without ::details-content (Firefox / Safari
   today) simply open instantly: no layout breakage, just no tween. The whole
   block is gated behind prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .ds-step-accordion::details-content {
    block-size: 0;
    opacity: 0;
    overflow: clip;
    transition:
      block-size 280ms cubic-bezier(0.25, 0.1, 0.25, 1),
      opacity 200ms ease,
      content-visibility 280ms allow-discrete;
  }
  .ds-step-accordion[open]::details-content {
    block-size: auto;
    opacity: 1;
  }
}

.ds-step-accordion__summary {
  list-style: none;
  /* Figma: header sits at a 20px inset (Frame 371 @ x/y 20). */
  padding: var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  cursor: pointer;
}
.ds-step-accordion__summary::-webkit-details-marker {
  display: none;
}

/* Start cluster (right in RTL): state chip + step title. */
.ds-step-accordion__lead {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-width: 0;
}
.ds-step-accordion__title {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-22);
  line-height: 1.3;
  color: var(--text-onCard-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* End cluster (left in RTL): status pill + optional issue counts + toggle. */
.ds-step-accordion__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* State chip — 32px square, holds the step number (pending/in-review) or a
   verdict glyph (approved/changes/rejected). Figma node 2621:949 = radius-4. */
.ds-step-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-32);
  height: var(--icon-size-32);
  border-radius: var(--radius-4);
  flex-shrink: 0;
  font-family: var(--font-activist-regular);
  font-size: 1.25rem; /* Figma chip numeral = 20px (no 20px type token) */
  line-height: 1;
  transition: border-radius 200ms ease;
}
/* When the step is OPEN, the number chip + toggle box tighten to radius-2
   (Figma open frame 2621:963 = radius-2; the toggle is squared to match per
   the 2026-05-30 request). `[open]` is on the inner <details>; chip + toggle
   are descendants, so this reaches them. */
.ds-step-accordion[open] .ds-step-chip,
.ds-step-accordion[open] .ds-step-accordion__toggle {
  border-radius: var(--radius-2);
}

/* Header divider — the 1px rule under the summary in an OPEN step. Figma node
   2621:967: blue-800 (#1a2db3) @ 8% opacity, inset 20px from each side (it
   spans the inner content width, not the full card). Implemented as a styled
   <div> inside the body so it lives in normal flow. */
.ds-step-divider {
  height: var(--stroke-1);
  margin-inline: var(--space-5);
  background-color: color-mix(in srgb, var(--color-blue-800) 8%, transparent);
}
.ds-step-chip__icon {
  width: var(--icon-size-24);
  height: var(--icon-size-24);
  background-color: currentColor;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* Status pill — bg + dot + label, all coloured per verdict family.
   Figma: padding 8/16, radius-4, 12px SemiBold label, 12px square dot. */
.ds-step-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-4);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
}
.ds-step-pill__dot {
  width: 0.75rem;  /* 12px */
  height: 0.75rem;
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
/* «استلام التعديلات» — the doctor answered a change request in this step and
   the answers are waiting to be judged. Sits BEFORE the verdict pill and is
   built to the pill's own geometry (same padding, radius, 12px semibold,
   line-height 1) so the header reads as one row of chips rather than a chip
   and a stranger. Success-toned: something ARRIVED; the pill beside it still
   says what is owed. */
.ds-step-recv {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-4);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
  color: var(--feedback-success-message);
  background: var(--feedback-success-bg);
  border: var(--stroke-1) solid var(--feedback-success-border);
}
.ds-step-recv__icon {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
}
/* Pending pill text is driven by WORK STATE, not open/closed: an untouched
   step shows "awaiting review"; once the reviewer has started it (≥1 field/cert
   approval or a saved change-request draft — server flags `is-in-progress`, the
   inline-verdict JS toggles it live) it shows "under review", whether open or
   closed. Both labels ship in the markup; CSS picks one via .is-in-progress. */
.ds-step-accordion.is-in-progress .ds-step-pill__label--idle { display: none; }
.ds-step-accordion:not(.is-in-progress) .ds-step-pill__label--active { display: none; }

/* Toggle box — 32px blue-50 square, radius-4, centred +/− glyph (blue-600).
   The glyph is the ACTUAL Figma icon (plus-sign / remove-01) rendered as a
   24px currentColor mask, not a text character — so it matches the design's
   stroke weight + cap exactly. MUST be the last child of
   .ds-step-accordion__meta so it lands at the outermost (visual-left) edge. */
.ds-step-accordion__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-32);
  height: var(--icon-size-32);
  background-color: var(--color-blue-50);
  border-radius: var(--radius-4);
  color: var(--color-blue-600);
  flex-shrink: 0;
  user-select: none;
  transition: background-color 160ms ease, border-radius 200ms ease;
}
.ds-step-accordion__toggle::before {
  content: "";
  width: var(--icon-size-24);
  height: var(--icon-size-24);
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-plus-sign.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-plus-sign.svg) center / contain no-repeat;
}
.ds-step-accordion[open]
  > .ds-step-accordion__summary
  .ds-step-accordion__toggle::before {
  -webkit-mask-image: url(/static/icons/fig-remove-01.svg);
  mask-image: url(/static/icons/fig-remove-01.svg);
}

/* ── Per-state colour map (verdict family) ─────────────────────────────
   pending  → blue (awaiting review)  /  cerulean (in progress = "under review")
   approved → green   ·  changes → brightSun  ·  rejected → radicalRed */
.ds-step--pending  .ds-step-pill      { background: var(--color-blue-50);  color: var(--color-blue-950); }
.ds-step--pending  .ds-step-pill__dot { background: var(--color-blue-500); }
/* In-progress pending step → "under review" cerulean. `.is-in-progress` lives on
   the inner <details> (server-set + JS-toggled live), while the state class is
   on the shell, so we reach through it. */
.ds-step--pending:has(> details.is-in-progress) .ds-step-pill      { background: var(--color-cerulean-50);  color: var(--color-cerulean-950); }
.ds-step--pending:has(> details.is-in-progress) .ds-step-pill__dot { background: var(--color-cerulean-500); }

.ds-step--approved .ds-step-pill      { background: var(--color-green-50);  color: var(--color-green-950); }
.ds-step--approved .ds-step-pill__dot { background: var(--color-green-500); }

.ds-step--changes  .ds-step-pill      { background: var(--color-brightSun-50);  color: var(--color-brightSun-950); }
.ds-step--changes  .ds-step-pill__dot { background: var(--color-brightSun-500); }

.ds-step--rejected .ds-step-pill      { background: var(--color-radicalRed-50);  color: var(--color-radicalRed-950); }
.ds-step--rejected .ds-step-pill__dot { background: var(--color-radicalRed-500); }

/* ── Canonical review status pill (component: ds_status_pill macro) ──────────
   Self-contained colour per state (no ancestor `.ds-step--*` / open-state
   dependency), so it renders correctly anywhere. Colours mirror the agreed
   step-pill palette; "under review" = cerulean (matching the OPEN/active step
   pill, which is the agreed "قيد المراجعة" look). */
.ds-status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  /* Square corners (radius 0) per user request — box and inner square. */
  border-radius: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
}
.ds-status-pill__dot {
  width: 0.75rem;  /* 12px */
  height: 0.75rem;
  border-radius: 0;
  flex-shrink: 0;
}
.ds-status-pill--review   { background: var(--color-cerulean-50);   color: var(--color-cerulean-950); }
.ds-status-pill--review   .ds-status-pill__dot { background: var(--color-cerulean-500); }
.ds-status-pill--awaiting { background: var(--color-blue-50);       color: var(--color-blue-950); }
.ds-status-pill--awaiting .ds-status-pill__dot { background: var(--color-blue-500); }
.ds-status-pill--approved { background: var(--color-green-50);      color: var(--color-green-950); }
.ds-status-pill--approved .ds-status-pill__dot { background: var(--color-green-500); }
.ds-status-pill--changes  { background: var(--color-brightSun-50);  color: var(--color-brightSun-950); }
.ds-status-pill--changes  .ds-status-pill__dot { background: var(--color-brightSun-500); }
.ds-status-pill--rejected { background: var(--color-radicalRed-50); color: var(--color-radicalRed-950); }
.ds-status-pill--rejected .ds-status-pill__dot { background: var(--color-radicalRed-500); }

/* ── Canonical review status pill (component: ds_status_pill macro) ──────────
   Self-contained colour per state (no ancestor `.ds-step--*` / open-state
   dependency), so it renders correctly anywhere. Colours mirror the agreed
   step-pill palette; "under review" = cerulean (matching the OPEN/active step
   pill, which is the agreed "قيد المراجعة" look). */
.ds-status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  /* Square corners (radius 0) per user request — box and inner square. */
  border-radius: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
}
.ds-status-pill__dot {
  width: 0.75rem;  /* 12px */
  height: 0.75rem;
  border-radius: 0;
  flex-shrink: 0;
}
.ds-status-pill--review   { background: var(--color-cerulean-50);   color: var(--color-cerulean-950); }
.ds-status-pill--review   .ds-status-pill__dot { background: var(--color-cerulean-500); }
.ds-status-pill--awaiting { background: var(--color-blue-50);       color: var(--color-blue-950); }
.ds-status-pill--awaiting .ds-status-pill__dot { background: var(--color-blue-500); }
.ds-status-pill--approved { background: var(--color-green-50);      color: var(--color-green-950); }
.ds-status-pill--approved .ds-status-pill__dot { background: var(--color-green-500); }
.ds-status-pill--changes  { background: var(--color-brightSun-50);  color: var(--color-brightSun-950); }
.ds-status-pill--changes  .ds-status-pill__dot { background: var(--color-brightSun-500); }
.ds-status-pill--rejected { background: var(--color-radicalRed-50); color: var(--color-radicalRed-950); }
.ds-status-pill--rejected .ds-status-pill__dot { background: var(--color-radicalRed-500); }

/* Chip fill + glyph colour. Figma binds the -500 family tone to every state
   chip (Pink/500, Green/500, BrightSun/500); glyph is the near-white -50. */
.ds-step--pending  .ds-step-chip { background: var(--color-pink-500);       color: var(--color-pink-50); }
/* Optical centering of the step NUMBER inside the pink chip. The Activist font
   reserves ~5px descent that digits don't use (their ink sits entirely above
   the baseline), so pure flex-centering floats the numeral high and the tight
   line box looks clipped. `text-box-trim` (the same mechanism Figma uses) trims
   the line box to the cap/baseline edges, so the digit's own box is what gets
   centred — exact, no clipping. Scoped to the pending chip only (it holds the
   number); the verdict chips hold a symmetric 24px icon and stay untouched. */
.ds-step--pending  .ds-step-chip {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
.ds-step--approved .ds-step-chip { background: var(--color-green-500);      color: var(--color-green-50); }
.ds-step--changes  .ds-step-chip { background: var(--color-brightSun-500);  color: var(--color-brightSun-50); }
.ds-step--rejected .ds-step-chip { background: var(--color-radicalRed-500); color: var(--color-radicalRed-50); }

/* Title takes a subtle verdict tint once a step is decided (Figma: the
   approved/needs-edit titles read green-/orange-tinted near-black). */
.ds-step--approved .ds-step-accordion__title { color: var(--color-green-950); }
.ds-step--changes  .ds-step-accordion__title { color: var(--color-brightSun-950); }
.ds-step--rejected .ds-step-accordion__title { color: var(--color-radicalRed-950); }

/* ── Submitted-data field cells (Figma open card: 3-up grid of blackHaze-50
   tiles, label over value, right-aligned in RTL). ──────────────────────── */
.ds-step-fields {
  display: grid;
  /* Figma node 2623:4516: a fixed 3-up grid (3 × ~316px cells, 16px gaps).
     `minmax(0,1fr)` lets long values ellipsize instead of blowing the track
     out. Collapses to 2-up / 1-up on tablet / phone below. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  margin: 0;
}
@media (max-width: 900px) {
  .ds-step-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .ds-step-fields { grid-template-columns: 1fr; }
}
.ds-step-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  background-color: var(--color-blackHaze-50);
  border-radius: var(--radius-2);
  min-width: 0;
}
/* Label/value spacing via line-height only — NOT text-box-trim. That property
   is Chromium/Safari-only, so it rendered these tiles differently in Firefox.
   The gap is a few px looser than the Figma cap-to-cap measure but uniform and
   identical across every browser (matches .ds-field-cell). */
.ds-step-field__label {
  margin: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  line-height: 1.2;
  color: var(--color-blackHaze-950);
  opacity: var(--opacity-70);
}
.ds-step-field__value {
  margin: 0;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  line-height: 1.2;
  color: var(--color-blackHaze-1000);
  word-break: break-word;
}
.ds-step-field__value--empty {
  /* Figma node 2623:4500 ("لم يدخل"): Regular weight (via the font family —
     no numeric font-weight), blackHaze-1000 @ 40% opacity — NOT italic. */
  font-family: var(--font-activist-regular);
  color: var(--color-blackHaze-1000);
  opacity: var(--opacity-40);
}

/* ── Field states: note / approved ─────────────────────────────────────
   Figma nodes 2641:5216 (note) + 2633:5046 (approved). The cell keeps the
   blackHaze-50 bg + radius-2, but a 20px status icon joins at the far LEFT
   and the value text recolours to the verdict family. Layout becomes
   row + space-between so the label/value stack stays at the start (right
   in RTL) and the icon pins to the end (left). */
.ds-step-field--note,
.ds-step-field--approved {
  /* row-reverse so the status icon pins to the LEFT and the label/value stack
     sits on the RIGHT (flush against the cell's right edge) — matching Figma
     nodes 2641:5216 / 2633:5046. Plain `row` on an RTL page put the icon on
     the right and the text on the left, which was backwards. */
  flex-direction: row-reverse;
  align-items: flex-start;
  justify-content: space-between;
}
/* The label/value stack regains its column flow inside the row. The status
   icon is a direct child (mask-driven, currentColor). */
.ds-step-field--note > .ds-step-field__body,
.ds-step-field--approved > .ds-step-field__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  /* RTL: flex-start = right edge; text-align start = right. Both pin the
     label/value to the right so the Arabic text reads flush-right. */
  align-items: flex-start;
  text-align: start;
}
.ds-step-field__status {
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
}
.ds-step-field--note   .ds-step-field__status {
  color: var(--color-brightSun-600);
  -webkit-mask-image: url(/static/icons/fig-alert-circle.svg);
  mask-image: url(/static/icons/fig-alert-circle.svg);
}
.ds-step-field--approved .ds-step-field__status {
  color: var(--color-green-600);
  -webkit-mask-image: url(/static/icons/fig-check.svg);
  mask-image: url(/static/icons/fig-check.svg);
}
/* Value recolour per state (Figma: note → brightSun-950, approved → green-950). */
.ds-step-field--note   .ds-step-field__value { color: var(--color-brightSun-950); }
.ds-step-field--approved .ds-step-field__value { color: var(--color-green-950); }

/* ── Needs-edit (changes_requested) step: de-emphasise every field that is NOT
   flagged for editing, so the orange "needs changes" cells read as the focus
   (Figma node 2641:5141). Non-flagged labels drop to 40% and their values to
   20%; the settled green check stays full-colour but grows to 24px. The
   flagged `.ds-step-field--note` cells keep full prominence. ── */
.ds-step--changes .ds-step-field:not(.ds-step-field--note) .ds-step-field__label {
  opacity: var(--opacity-40);
}
.ds-step--changes .ds-step-field--approved .ds-step-field__value {
  opacity: var(--opacity-20);
}
.ds-step--changes .ds-step-field__value--empty {
  opacity: var(--opacity-20);
}
.ds-step--changes .ds-step-field--approved .ds-step-field__status {
  width: var(--icon-size-24);
  height: var(--icon-size-24);
}

/* ── Decision-bar action button SIZING (Figma node 2621:969) ───────────────
   The COLOUR + hover + two-layer shadow for the green/amber CTAs now lives in
   the canonical `.ds-btn--success` / `.ds-btn--warning` variants (the single
   source of truth in the variants section above) — these per-container colour
   overrides were removed 2026-06-05.

   This block only owns the decision-bar SIZING that differs from the generic
   `.ds-btn--medium`: a flatter 12/24 padding + 4px inner radius, with
   !important to win over panel-shell's medium sizing.

   EVERY button of a step decision footer, and BOTH footer shapes (Mohammed,
   2026-07-26). It used to name `--success` / `--warning` under
   `.ds-step-actions` alone, which left two gaps a reviewer could see on one
   page: the SECONDARY «تعديل القرار» standing where the primary had been was
   46px next to its 50px neighbours, and `.ds-qual-foot` was not listed at all,
   so the very same «تأكيد الاعتماد» rendered 100×44 in the repeatable steps and
   140×50 in the scalar ones. One decision row, one button size.
   (In the rc footer it likewise covers every `.ds-btn` so the secondary
   «إلغاء» matches the warning «تأكيد».) */
.ds-step-actions .ds-btn .ds-btn__inner,
.ds-qual-foot .ds-btn .ds-btn__inner,
.ds-cert-rc-foot__actions .ds-btn .ds-btn__inner {
  padding: var(--space-3) var(--space-6) !important;   /* 12 / 24 */
  min-height: 0 !important;
  border-radius: var(--radius-4) !important;
}

/* ── Gold palette tokens ───────────────────────────────────────────────────
   The gold/* family isn't in the generated Tailwind palette, so the three
   tones are declared here (values straight from the Figma variables). Shared
   by the gold file-actions pill (.ds-pill--gold). The legacy "Add note"
   button (.ds-note-btn) that first introduced these was removed 2026-06-02. */
:root {
  --color-gold-50:  #ffffe7;
  --color-gold-100: #fffec1;
  --color-gold-300: #ffef41;
  --color-gold-600: #d19d00;
  --color-gold-950: #442504;
  --color-gold-1000: #0a0601;
}

/* ── Review-lock presence banner (Figma 2846:4821, blue rev 2026-06-12) ─────
   "X is reviewing this step" — a blue-framed bar: the reviewer's name + a
   hint + a 40px initials avatar on the reading (right, RTL) edge, and a
   take-over button on the far (left) edge. The button MORPHS from the blue
   «أخذ السيطرة» (lock) default into a green unlock square on success
   (panel.js adds .is-taken), then the banner fades out. While the banner is
   up, the step's content below it is dimmed to 40% and fully inert — see
   .ds-step-accordion.is-lock-dimmed below. */
.ds-step-lock-banner {
  margin: var(--space-3) var(--space-4) 0;
  border: var(--stroke-1) solid var(--color-blue-600);
  border-radius: var(--radius-8);          /* outer frame ≈ inner radius + inset */
  padding: var(--space-0_5);               /* 2px frame inset (Figma 3px incl. border) */
  /* Smooth removal: panel.js sets these to 0 to collapse + fade after takeover. */
  transition: opacity .45s ease, transform .45s ease;
}
.ds-step-lock-banner__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5);                 /* 16px */
  background: var(--color-blue-50);
  border-radius: var(--radius-4);
}
/* RIGHT (RTL start): avatar (rightmost) + the text block to its left. */
.ds-step-lock-banner__who {
  display: flex;
  align-items: center;
  gap: var(--space-4);                     /* 12px */
  min-width: 0;
}
.ds-step-lock-banner__avatar {
  flex-shrink: 0;
  inline-size: 2.5rem;                     /* 40px */
  block-size: 2.5rem;
  border-radius: var(--radius-4);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-blue-600);
  color: var(--color-blue-50);
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  line-height: 1;
  text-transform: uppercase;
  user-select: none;
}
.ds-step-lock-banner__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);                     /* 4px title↔sub (user 2026-06-13) */
  min-width: 0;
  text-align: right;
}
.ds-step-lock-banner__title {
  margin: 0;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  line-height: 1.2;
  color: color-mix(in srgb, var(--color-blue-1000) 80%, transparent);
}
.ds-step-lock-banner__title strong {       /* the reviewer's name */
  font-weight: inherit;
  color: var(--color-blue-600);
}
.ds-step-lock-banner__sub {
  margin: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  line-height: 1.3;
  color: color-mix(in srgb, var(--color-blue-1000) 80%, transparent);
}
.ds-step-lock-banner__sub strong {
  font-family: var(--font-activist-semibold);
  font-weight: inherit;
}

/* ── Locked-step content dim (40%) ──
   While ANOTHER reviewer holds the step, everything in the open accordion
   below the lock banner drops to 40% opacity and is fully non-interactive —
   reading is fine, touching is not. A successful take-over removes the class
   (panel.js) and the content eases back to 100%. The summary (title row),
   header divider and the banner itself stay at full opacity. */
.ds-step-accordion.is-lock-dimmed > *:not(summary):not(.ds-step-divider):not(.ds-step-lock-banner) {
  opacity: 0.4;
  pointer-events: none;
  user-select: none;
  transition: opacity .45s ease;
}
/* ── Cinematic "handed back to you" pulse ─────────────────────────────────
   The instant a take-over clears the dim (panel.js adds .is-just-unlocked,
   auto-removed after the pulse) the step breathes back to life: a soft green
   halo blooms and fades around the whole step while the content eases up into
   place. Slow, gentle, easy on the eyes — a cinematic reveal, not a harsh
   blink. The halo uses `filter` (no fill-mode) so it reverts cleanly and never
   clobbers the step's resting shadow. */
@keyframes ds-step-unlock-halo {
  0%   { filter: drop-shadow(0 0 0 rgba(34, 197, 94, 0)); }
  35%  { filter: drop-shadow(0 6px 18px rgba(34, 197, 94, 0.42)); }
  100% { filter: drop-shadow(0 0 0 rgba(34, 197, 94, 0)); }
}
@keyframes ds-step-unlock-settle {
  0%   { opacity: 0.4; transform: translateY(6px); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0); }
}
.ds-step-accordion.is-just-unlocked {
  animation: ds-step-unlock-halo 1.6s ease-out;
}
.ds-step-accordion.is-just-unlocked > *:not(summary):not(.ds-step-divider):not(.ds-step-lock-banner) {
  animation: ds-step-unlock-settle 1.2s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  .ds-step-accordion.is-just-unlocked,
  .ds-step-accordion.is-just-unlocked > * { animation: none !important; }
}
/* Keep the ease-in when the class is removed (transition must live on the
   element in BOTH states). */
.ds-step-accordion > *:not(summary):not(.ds-step-divider):not(.ds-step-lock-banner) {
  transition: opacity .45s ease;
}

/* LEFT (RTL end): the morphing take-over button. */
.ds-step-lock-banner__actions { flex-shrink: 0; }
.ds-step-lock-banner__takeover {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                     /* 8px */
  padding: var(--space-2) var(--space-5);  /* 8 / 16 — default (with label) */
  border: 0;
  border-radius: var(--radius-4);
  background: var(--color-blue-600);
  color: var(--color-blue-50);
  cursor: pointer;
  white-space: nowrap;
  /* Two-layer blue-tinted shadow (Figma drop-shadow on both states). */
  box-shadow: 0 6px 6px rgba(35, 76, 240, 0.06), 0 12px 12px rgba(35, 76, 240, 0.08);
  /* The morph: bg + padding + gap ease into the green square; lift on hover. */
  transition: background-color .35s ease, padding .4s ease, gap .4s ease,
              color .35s ease, box-shadow .35s ease, transform .25s ease;
}
/* Hover/focus: lighten to blue-500, lift, and PREVIEW the unlock (the lock
   springs open + a ring radiates). Scoped to :not(.is-taken) so the green
   is-taken square never re-triggers it. */
.ds-step-lock-banner__takeover:not(.is-taken):hover,
.ds-step-lock-banner__takeover:not(.is-taken):focus-visible {
  background: var(--color-blue-500);
  transform: translateY(-1px);
  box-shadow: 0 8px 10px rgba(35, 76, 240, 0.10), 0 18px 24px rgba(35, 76, 240, 0.14);
}
.ds-step-lock-banner__takeover:not(.is-taken):active {
  transform: translateY(0) scale(.97);
}
.ds-step-lock-banner__takeover-label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  line-height: 1.2;
  overflow: hidden;
  max-inline-size: 14ch;
  opacity: 1;
  transition: max-inline-size .4s ease, opacity .3s ease;
}
/* Icon stack: lock + unlock overlap and spring/cross-fade in place. */
.ds-step-lock-banner__takeover-ico-wrap {
  position: relative;
  inline-size: 1rem;                       /* 16px */
  block-size: 1rem;
  flex-shrink: 0;
}
/* The radiating "unlock" ring — bursts outward once on hover and on the
   confirmed take-over. */
.ds-step-lock-banner__takeover-ico-wrap::after {
  content: "";
  position: absolute;
  inset: -0.375rem;                        /* -6px */
  border-radius: 50%;
  border: 2px solid currentColor;
  opacity: 0;
  transform: scale(.4);
  pointer-events: none;
}
.ds-step-lock-banner__takeover:not(.is-taken):hover .ds-step-lock-banner__takeover-ico-wrap::after {
  animation: ds-unlock-ring .6s ease-out both;
}
.ds-step-lock-banner__takeover-ico {
  position: absolute;
  inset: 0;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
  transform-origin: 50% 62%;               /* pivot near the shackle base */
  /* Spring: whichever glyph becomes visible overshoots, then settles. */
  transition: opacity .3s ease, transform .5s cubic-bezier(.34, 1.56, .64, 1);
}
.ds-step-lock-banner__takeover-ico--lock {
  opacity: 1;
  transform: scale(1) rotate(0);
  -webkit-mask-image: url(/static/icons/fig-square-lock-02.svg);
  mask-image: url(/static/icons/fig-square-lock-02.svg);
}
.ds-step-lock-banner__takeover-ico--unlock {
  opacity: 0;
  transform: scale(.55) rotate(-18deg);
  -webkit-mask-image: url(/static/icons/fig-square-unlock-02.svg);
  mask-image: url(/static/icons/fig-square-unlock-02.svg);
}
/* Hover preview: lock drops out, unlock springs in. */
.ds-step-lock-banner__takeover:not(.is-taken):hover .ds-step-lock-banner__takeover-ico--lock,
.ds-step-lock-banner__takeover:not(.is-taken):focus-visible .ds-step-lock-banner__takeover-ico--lock {
  opacity: 0;
  transform: scale(.7) rotate(12deg) translateY(1px);
}
.ds-step-lock-banner__takeover:not(.is-taken):hover .ds-step-lock-banner__takeover-ico--unlock,
.ds-step-lock-banner__takeover:not(.is-taken):focus-visible .ds-step-lock-banner__takeover-ico--unlock {
  opacity: 1;
  transform: scale(1) rotate(0) translateY(-1px);
}
/* ── Success morph (panel.js adds .is-taken on a confirmed take-over) ── */
.ds-step-lock-banner__takeover.is-taken {
  background: var(--color-green-600);
  color: var(--color-green-50);
  gap: 0;                                  /* no inter-item gap to swallow */
  padding: var(--space-2);                 /* square 8px — icon only */
  cursor: default;
}
.ds-step-lock-banner__takeover.is-taken .ds-step-lock-banner__takeover-label {
  max-inline-size: 0;
  opacity: 0;
}
.ds-step-lock-banner__takeover.is-taken .ds-step-lock-banner__takeover-ico--lock {
  opacity: 0;
  transform: scale(.7) rotate(12deg);
}
/* The confirmed unlock: a powerful pop — overshoot scale + settle, ring burst. */
.ds-step-lock-banner__takeover.is-taken .ds-step-lock-banner__takeover-ico--unlock {
  opacity: 1;
  animation: ds-unlock-burst .55s cubic-bezier(.34, 1.56, .64, 1) both;
}
.ds-step-lock-banner__takeover.is-taken .ds-step-lock-banner__takeover-ico-wrap::after {
  animation: ds-unlock-ring .65s ease-out both;
}
@keyframes ds-unlock-ring {
  0%   { opacity: .55; transform: scale(.4); }
  70%  { opacity: .16; }
  100% { opacity: 0; transform: scale(1.7); }
}
@keyframes ds-unlock-burst {
  0%   { opacity: .2; transform: scale(.5) rotate(-22deg); }
  45%  { opacity: 1;  transform: scale(1.32) rotate(7deg); }
  70%  { opacity: 1;  transform: scale(.94) rotate(-3deg); }
  100% { opacity: 1;  transform: scale(1) rotate(0); }
}
/* Collapse-on-remove (panel.js adds .is-leaving after the green confirmation). */
.ds-step-lock-banner.is-leaving {
  opacity: 0;
  transform: translateY(-4px);
}
@media (prefers-reduced-motion: reduce) {
  .ds-step-lock-banner,
  .ds-step-lock-banner__takeover,
  .ds-step-lock-banner__takeover-label,
  .ds-step-lock-banner__takeover-ico { transition: none; }
  .ds-step-lock-banner__takeover-ico,
  .ds-step-lock-banner__takeover-ico-wrap::after { animation: none !important; }
  .ds-step-lock-banner__takeover:not(.is-taken):hover .ds-step-lock-banner__takeover-ico--lock,
  .ds-step-lock-banner__takeover:not(.is-taken):hover .ds-step-lock-banner__takeover-ico--unlock { transform: none; }
}
/* ── Phone (≤640px): the take-over button FILLS the banner box ──
   The reviewer line (avatar + text) and the button stack vertically, and the
   button spans the full width so it reads as a tidy, consistent block instead
   of a cramped inline chip squeezed to the edge. */
@media (max-width: 640px) {
  .ds-step-lock-banner__inner {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
  }
  /* Pin the reviewer avatar to the top-right corner of the banner instead of
     centering it against the two-line text. */
  .ds-step-lock-banner__who {
    align-items: flex-start;
  }
  .ds-step-lock-banner__actions {
    inline-size: 100%;
  }
  .ds-step-lock-banner__takeover {
    inline-size: 100%;
    justify-content: center;
  }
}

/* ============================================================================
   FILE-ACTIONS SECTION — Figma node 2654:5517 ("إجراءات الملف")
   A titled white card holding a right-aligned row of pill buttons: three blue
   (primary-outline) + one gold, all using the shared .ds-pill shape below.
   ============================================================================ */
.ds-file-actions__title {
  margin: 0 0 var(--space-5);     /* 20px gap to the card (Figma section gap-20) */
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);   /* 18px */
  line-height: 1.2;
  color: var(--text-onPage-primary);
  text-align: start;              /* right in RTL */
}
.ds-file-actions__card {
  background: var(--bg-card);
  border-radius: var(--radius-8);
  padding: var(--space-5);        /* 20px */
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-cararra-950) 6%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-cararra-950) 12%, transparent);
}
.ds-file-actions__row {
  display: flex;
  /* Same proven recipe as the header stat row: row-reverse + flex-end keeps the
     buttons clustered on the RIGHT (start) with the mock's left-to-right order
     (audit … note) preserved on this RTL page. */
  flex-direction: row-reverse;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-3);            /* 12px */
  flex-wrap: wrap;
}

/* ── Pill button (shared shape) ──────────────────────────────────────────────
   Colour comes from the --pill-* custom props the variant sets; the icon mask
   is set per-instance inline. Three nested boxes: 2px focus-ring pad → 1px
   inset-shadow ring → filled inner with label + icon. */
.ds-pill {
  display: inline-flex;
  padding: var(--space-0_5);      /* 2px */
  margin: 0;
  border: none;
  background: transparent;
  border-radius: var(--radius-4);
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s ease;
}
.ds-pill:hover  { transform: translateY(-0.0625rem); }
.ds-pill:active { transform: translateY(0.0625rem); }
.ds-pill:focus-visible { outline: none; }
.ds-pill__ring {
  display: inline-flex;
  padding: var(--space-0_5);      /* 2px */
  box-shadow: inset 0 0 0 var(--stroke-1) var(--pill-border);
  border-radius: var(--radius-4);
}
.ds-pill__inner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);            /* 8px */
  padding: var(--space-2) var(--space-4);  /* 8 / 16 */
  background: var(--pill-bg);
  border-radius: var(--radius-2);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);   /* 12px */
  line-height: 1;
  color: var(--pill-text);
  white-space: nowrap;
  transition: background-color 0.2s ease;
}
.ds-pill:hover .ds-pill__inner {
  background: color-mix(in srgb, var(--pill-border) 10%, var(--pill-bg));
}
.ds-pill__icon {
  width: var(--icon-size-16);     /* 16px */
  height: var(--icon-size-16);
  flex-shrink: 0;
  background-color: var(--pill-icon);
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
}
.ds-pill--blue {
  --pill-border: var(--color-blue-600);
  --pill-bg:     var(--color-blue-50);
  --pill-text:   var(--color-blue-600);
  --pill-icon:   var(--color-blue-600);
}
.ds-pill--gold {
  --pill-border: var(--color-gold-600);
  --pill-bg:     var(--color-gold-50);
  --pill-text:   var(--color-gold-950);
  --pill-icon:   var(--color-gold-600);
}

/* ============================================================================
   DOCTOR HEADER CARD — Figma node 2621:923
   Top-of-page identity card. Two rows separated by a hairline:
     row 1  [status pill][note badge] ……… [file# / name / email][photo]
     row 2  ……………………………… [gender][phone][nationality][submitted]
   RTL: the badges + stat tiles read from the right. Card shadow + radius match
   the open-step card (cararra-950 6%/12%, radius-8). All measurements map the
   Figma pixel values onto the token scale by value.
   ============================================================================ */
.ds-doc-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);          /* 20px */
  padding: var(--space-5);      /* 20px */
  background: var(--bg-card);
  border-radius: var(--radius-8);
  box-shadow:
    0 0.375rem 0.375rem color-mix(in srgb, var(--color-cararra-950) 6%, transparent),
    0 0.75rem  0.75rem  color-mix(in srgb, var(--color-cararra-950) 12%, transparent);
}
.ds-doc-header__top {
  display: flex;
  /* The Figma frame is authored LTR (badges at canvas-left, identity at
     canvas-right). On this RTL page the faithful reproduction needs the row
     reversed so identity + photo land on the RIGHT (matching both the mock and
     the previous header) and the status / note badges on the LEFT. */
  flex-direction: row-reverse;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.ds-doc-header__badges {
  display: flex;
  align-items: center;
  gap: var(--space-4);          /* 16px */
  flex-wrap: wrap;
}

/* Status pill (doctor.status) — bg-50 / text-950 / 16px dot-500 per family. */
.ds-doc-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);          /* 8px */
  padding: var(--space-2) var(--space-4);  /* 8 / 16 */
  border-radius: var(--radius-4);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14); /* 14px */
  line-height: 1;
  white-space: nowrap;
}
.ds-doc-pill__dot {
  width: var(--icon-size-16);   /* 16px */
  height: var(--icon-size-16);
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
.ds-doc-pill--blue      { background: var(--color-blue-50);       color: var(--color-blue-950); }
.ds-doc-pill--blue      .ds-doc-pill__dot { background: var(--color-blue-500); }
.ds-doc-pill--green     { background: var(--color-green-50);      color: var(--color-green-950); }
.ds-doc-pill--green     .ds-doc-pill__dot { background: var(--color-green-500); }
.ds-doc-pill--red       { background: var(--color-radicalRed-50); color: var(--color-radicalRed-950); }
.ds-doc-pill--red       .ds-doc-pill__dot { background: var(--color-radicalRed-500); }
.ds-doc-pill--cerulean  { background: var(--color-cerulean-50);   color: var(--color-cerulean-950); }
.ds-doc-pill--cerulean  .ds-doc-pill__dot { background: var(--color-cerulean-500); }
.ds-doc-pill--brightSun { background: var(--color-brightSun-50);  color: var(--color-brightSun-950); }
.ds-doc-pill--brightSun .ds-doc-pill__dot { background: var(--color-brightSun-500); }
.ds-doc-pill--neutral   { background: var(--color-blackHaze-50);  color: var(--color-blackHaze-950); }
.ds-doc-pill--neutral   .ds-doc-pill__dot { background: var(--color-blackHaze-500); }

/* (The header "Has note" badge was removed 2026-05-31 — the doctor-note
   indicator now lives only in the File-actions section's gold pill.) */

/* Edit-deadline countdown chip (kept from the old header — not in the Figma
   mock, but dropping the edit deadline is a real UX loss; it lives quietly in
   the badge cluster, only while the doctor is in needs_edit). */
.ds-doc-header__badges .ds-countdown { flex-shrink: 0; }

/* Identity cluster (end edge / left in RTL): file# + name/email, then photo. */
.ds-doc-header__identity {
  display: flex;
  flex-direction: row-reverse;  /* photo on the right, id text to its left */
  align-items: center;          /* name/email block vertically centred against the photo */
  justify-content: flex-end;
  gap: var(--space-5);          /* 20px */
  min-width: 0;
}
/* (.ds-doc-header__idtext + .ds-doc-fileno* were removed 2026-06-14 — رقم الملف
   moved out of the identity cluster into its own cerulean stat tile in row 2.) */
.ds-doc-nameblock {
  display: flex;
  flex-direction: column;
  align-items: flex-start;      /* RTL → right edge */
  gap: var(--space-1);          /* 4px */
  min-width: 0;
}
/* Name + email: spacing is controlled by line-height + the flex gap, NOT
   text-box-trim. text-box-trim only ships in Chromium/Safari (not Firefox), and
   on the email it shrank the line-box below 1em so `overflow: hidden` (needed
   for the ellipsis) clipped the Latin descenders (g / y / @) — the struck-out
   look that differed between browsers. line-height keeps the box ≥ 1em so
   descenders are never clipped, and renders identically everywhere. */
.ds-doc-name {
  margin: 0;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-24); /* 24px */
  line-height: 1;
  color: var(--text-onCard-primary);
}
.ds-doc-email {
  margin: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16); /* 16px */
  line-height: 1.2;             /* ≥ 1em so overflow:hidden never clips descenders */
  color: var(--text-onCard-secondary);
  opacity: var(--opacity-70);
  direction: ltr;
  text-align: end;
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ds-doc-photo {
  width: 4rem;                  /* 64px (Figma 2621:932) */
  height: 4rem;
  border-radius: var(--radius-4);  /* Figma 3.05px ≈ 4px */
  overflow: hidden;
  flex-shrink: 0;
  background: var(--color-blue-500);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ds-doc-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* No real photo → neutral frame + gendered avatar SVG (the blue-500 frame from
   the mock only reads well behind an actual cover photo). */
.ds-doc-photo--fallback { background: var(--color-blackHaze-100); }
.ds-doc-photo--fallback img { width: 65%; height: 65%; object-fit: contain; }

/* Hairline divider — blue-800 @ 8%, full content width. */
.ds-doc-header__divider {
  height: var(--stroke-1);
  width: 100%;
  background: color-mix(in srgb, var(--color-blue-800) 8%, transparent);
  border-radius: var(--radius-4);
}

/* Stat tiles row (Figma 2623:4524).
   This row is authored LTR in the design and stays LTR even on the RTL page:
   the tiles read gender → nationality → date → file LEFT-to-RIGHT, and the
   whole group is anchored to the RIGHT (under the photo) with the slack on the
   left. `direction: ltr` reproduces the canvas exactly — both the tile order
   AND each tile's internal [text][icon] arrangement — without any reversing. */
.ds-doc-header__stats {
  display: flex;
  direction: ltr;
  justify-content: flex-end;    /* group flush to the right edge */
  align-items: stretch;         /* all tiles share the tallest height */
  gap: var(--space-5);          /* 20px */
  flex-wrap: wrap;
}
/* Each tile is a [text-stack][24px glyph] row, 40px tall: text on the LEFT,
   glyph trailing on the RIGHT (Figma canvas order). */
.ds-doc-stat {
  display: flex;
  align-items: center;
  gap: var(--space-2);          /* 8px (Figma spacing/3) — date overrides to 12 */
  padding: var(--space-2) var(--space-4);  /* 8 / 16 */
  min-block-size: 2.5rem;       /* 40px (Figma tile height) */
  background: var(--color-blackHaze-50);
  border-radius: var(--radius-4);
}
.ds-doc-stat__text {
  display: flex;
  flex-direction: column;
  align-items: flex-end;        /* LTR row → right-align label + value (Figma) */
  gap: var(--space-1);          /* 4px (label ↔ value, user 2026-06-14) */
  min-width: 0;
}
.ds-doc-stat__icon {
  width: var(--icon-size-24);   /* 24px */
  height: var(--icon-size-24);
  flex-shrink: 0;
  display: block;
}
/* Date tile: 12px between value and calendar glyph (Figma spacing/4). */
.ds-doc-stat--date { gap: var(--space-3); }
/* Spacing via line-height only — NOT text-box-trim (that property ships in
   Chromium/Safari but not Firefox, so it made tiles render at different heights
   between browsers). The tile floats a few px taller than the Figma 40px but is
   now identical everywhere. */
.ds-doc-stat__label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12); /* 12px */
  line-height: 1;
  color: var(--color-blackHaze-950);
  opacity: var(--opacity-70);
}
.ds-doc-stat__value {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14); /* 14px */
  line-height: 1;
  color: var(--color-blackHaze-1000);
}
/* Gender value is tinted to match its glyph: female pink-600 / male blue-600. */
.ds-doc-stat--gender-female .ds-doc-stat__value { color: var(--color-pink-600); }
.ds-doc-stat--gender-male   .ds-doc-stat__value { color: var(--color-blue-600); }
/* Nationality flag glyph — 24px circle. */
.ds-doc-stat__flag {
  width: var(--icon-size-24);   /* 24px */
  height: var(--icon-size-24);
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  background: var(--color-blackHaze-100);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ds-doc-stat__flag img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* رقم الملف — cerulean tile: a copy button sits to the right of the value
   (leading), the QR glyph trails on the left (Figma 2891:1026). */
.ds-doc-stat--file {
  background: var(--color-cerulean-50);
}
.ds-doc-stat--file .ds-doc-stat__text { gap: var(--space-1); }  /* 4px (label ↔ value, user 2026-06-14) */
.ds-doc-stat--file .ds-doc-stat__label { color: var(--color-cerulean-950); }
.ds-doc-stat--file .ds-doc-stat__value {
  font-size: var(--type-ar-12); /* 12px (Figma) */
  color: var(--color-cerulean-600);
}
.ds-doc-stat__filegroup {
  display: flex;
  align-items: flex-end;        /* copy glyph baseline-aligns with the value */
  gap: var(--space-2);          /* 8px */
  min-width: 0;
}
.ds-doc-stat__copy {
  appearance: none;
  border: 0;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-2);
  transition: opacity var(--motion-fast, 120ms) ease;
}
.ds-doc-stat__copy:hover { opacity: var(--opacity-70); }
.ds-doc-stat__copy img { width: var(--icon-size-16); height: var(--icon-size-16); display: block; }
.ds-doc-stat__qr {
  width: var(--icon-size-24);   /* 24px */
  height: var(--icon-size-24);
  flex-shrink: 0;
  display: block;
}

/* ============================================================================
   DECISION DIALOGS — approve / request-changes / reject modals
   Used by `templates/doctors/detail.html`. Native HTML5 <dialog> opened via
   `dialog.showModal()` from panel.js. A coloured top accent stripe encodes
   the action family (Core.Color tokens):
       approve          → green-600
       request_changes  → brightSun-500
       reject           → red-600
   RTL: the dialog inherits page direction; logical properties + flexbox keep
   the title at the start (right in RTL) and the action buttons at the end
   (left in RTL).
   ============================================================================ */
.ds-decision-dialog {
  /* Centre EXACTLY in the viewport on both axes — the same top-layer recipe as
     .ds-cert-modal: `inset: 0` makes a viewport-sized box and `margin: auto`
     centres the fixed-width / content-height box within it (horizontally AND
     vertically). A page-level reset had forced `margin: 0`, which in RTL pinned
     the modal to the top-right corner. We avoid `top/left: 50% + translate`
     because a transformed DOM ancestor makes Chromium resolve those percentages
     against the ancestor's box instead of the viewport. */
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(92vw, 560px);
  max-height: 86vh;
  padding: 0;
  border: var(--stroke-1) solid var(--border-divider);
  border-radius: var(--radius-8, 12px);
  background: var(--bg-card, #fff);
  color: var(--text-onCard-primary);
  box-shadow: 0 24px 64px rgba(16, 24, 40, 0.24);
  overflow: hidden;
}
.ds-decision-dialog::backdrop {
  background: rgba(16, 24, 40, 0.55);
  backdrop-filter: blur(2px);
}
/* Lightweight enter animation (respects reduced motion below). */
.ds-decision-dialog[open] {
  animation: ds-decision-pop 140ms ease-out;
}
@keyframes ds-decision-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.ds-decision-dialog__inner {
  display: flex;
  flex-direction: column;
  max-height: 86vh;
  /* No default accent stripe — the plain blue top border read as ugly on
     generic pops. Only the SEMANTIC decision variants (approve/edit/reject)
     carry a meaningful coloured accent below. */
  border-block-start: 0;
}
.ds-decision-dialog--approve .ds-decision-dialog__inner {
  border-block-start: 3px solid var(--color-green-600);
}
.ds-decision-dialog--edit .ds-decision-dialog__inner {
  border-block-start: 3px solid var(--color-brightSun-500);
}
.ds-decision-dialog--reject .ds-decision-dialog__inner {
  border-block-start: 3px solid var(--color-red-600);
}

.ds-decision-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-3);
  /* The separator line is an inset ::after (NOT a full-bleed border-block-end):
     Figma 2817:1829 gives it horizontal padding so it stops short of the
     dialog edges, aligned with the header/body content padding. */
  position: relative;
}
.ds-decision-dialog__header::after {
  content: "";
  position: absolute;
  inset-inline: var(--space-4);     /* 16px right + left padding (Figma) */
  inset-block-end: 0;
  block-size: var(--stroke-1);
  background: var(--border-divider);
}
.ds-decision-dialog__title {
  margin: 0;
  /* Title = 18px bold (standard for the confirmation / decision dialogs). */
  font-size: var(--type-ar-18, 1.125rem);
  font-weight: 700;
  color: var(--text-onCard-primary);
  text-align: start;
}
/* Entity name highlighted inside a confirm/decision title (e.g. the authority
   name in "Really disable <name>?"). The title is already bold, so the name
   stands out via the danger ink — same idea as the unapprove pop's gold name.
   dsConfirm() builds this span when given `titleName`. */
.ds-decision-dialog__title-name {
  color: var(--color-radicalRed-600);
}
/* THE SUBJECT OF THE DIALOG — the step being decided, in a box of its own.
   The server-rendered twin of the approve pop's value box, and the same
   reasoning: a name set larger INSIDE the lead breaks the line instead of
   lifting the word, so prominence comes from space and a tint. Same geometry as
   `.ds-approve-pop__value` so the two confirmation surfaces read as one. */
.ds-decision-dialog__subject {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 3.5rem;                /* 56px */
  max-height: 9rem;
  overflow-y: auto;
  margin-block-start: var(--space-4);
  padding: var(--space-4);
  border-radius: var(--radius-8);
  background: var(--color-green-50);
}
.ds-decision-dialog__subject-text {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-24);
  line-height: 1.4;
  text-align: center;
  color: var(--color-green-950);
  overflow-wrap: anywhere;
}
/* ── Close button (canonical component: ds_close_button macro) ──
   A 32px transparent icon button with the unified close glyph (fig-close.svg —
   a clean outline X) in blue-300 (Figma 2705:4979 — identical to the
   certificate-modal header close). SINGLE source of truth so EVERY dismiss
   affordance looks the same: the certificate modal, the reason picker, the
   decision dialogs, and anywhere else a close is needed. Replaced the old
   per-dialog `.ds-decision-dialog__close` / `.ds-cert-modal__close` one-offs (the
   decision ones rendered cancel-02.svg as a faint hollow X in grey). */
.ds-close-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  padding: 0;
  border: none;
  border-radius: var(--radius-4);
  background: transparent;
  color: var(--color-blue-300);
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.ds-close-btn:hover {
  background: var(--color-blue-50);
  color: var(--color-blue-300);
}
.ds-close-btn__ico {
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-close.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-close.svg) center / contain no-repeat;
}

.ds-decision-dialog__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  overflow-y: auto;
}
.ds-decision-dialog__lead {
  margin: 0;
  font-size: var(--type-ar-13);
  line-height: 1.55;
  color: var(--text-onCard-secondary);
}
.ds-decision-dialog__empty {
  margin: 0;
  padding: var(--space-3);
  font-size: var(--type-ar-13);
  color: var(--text-placeholder);
  background: var(--bg-input);
  border-radius: var(--radius-4);
  text-align: center;
}

/* Firm-action banner (reject) — red. */
.ds-decision-dialog__danger-banner {
  padding: var(--space-2) var(--space-3);
  font-size: var(--type-ar-13);
  font-weight: 600;
  line-height: 1.5;
  color: var(--color-red-700);
  background: var(--color-red-50);
  border: var(--stroke-1) solid var(--color-red-200);
  border-radius: var(--radius-4);
}
/* Override warning — brightSun (a prior verdict exists). */
.ds-decision-dialog__override-warn {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  font-size: var(--type-ar-12);
  line-height: 1.5;
  color: var(--color-brightSun-950);
  background: var(--color-brightSun-50);
  border: var(--stroke-1) solid var(--color-brightSun-200);
  border-radius: var(--radius-4);
}

/* Reason group (a fieldset per field in edit / single group in reject). */
.ds-reason-group {
  margin: 0;
  padding: var(--space-2) var(--space-3) var(--space-1);
  border: var(--stroke-1) solid var(--border-divider);
  border-radius: var(--radius-4);
}
.ds-reason-group__legend {
  padding: 0 var(--space-1);
  font-size: var(--type-ar-12);
  font-weight: 700;
  color: var(--text-placeholder);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Selectable reason row. */
.ds-reason-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2);
  border-radius: var(--radius-4);
  cursor: pointer;
  transition: background-color 120ms ease;
}
.ds-reason-row:hover {
  background: var(--bg-input);
}
.ds-reason-row__check {
  margin-block-start: 0.15rem;
  width: 1.05rem;
  height: 1.05rem;
  flex-shrink: 0;
  accent-color: var(--color-blue-600);
  cursor: pointer;
}
.ds-reason-row__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.ds-reason-row__title {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  font-size: var(--type-ar-14);
  font-weight: 600;
  color: var(--text-onCard-primary);
}
.ds-reason-row__msg {
  font-size: var(--type-ar-13);
  line-height: 1.5;
  color: var(--text-onCard-secondary);
  white-space: pre-wrap;
}
.ds-reason-row__fix {
  font-size: var(--type-ar-12);
  line-height: 1.45;
  color: var(--text-placeholder);
}

.ds-decision-dialog__custom {
  padding-block-start: var(--space-1);
}
.ds-decision-dialog__note-label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--type-ar-13);
  font-weight: 600;
  color: var(--text-onCard-primary);
}
.ds-decision-dialog__note-label textarea {
  width: 100%;
  resize: vertical;
  font-family: inherit;
  font-weight: 400;
}

.ds-decision-dialog__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: flex-end;
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-block-start: var(--stroke-1) solid var(--border-divider);
  background: var(--bg-subtle, var(--bg-input));
}

/* ── Compact confirm variant (cert-approve confirmation) ──
   A small, centred yes/no box: narrower than the wide step dialogs, title +
   text centred, NO header close X (footer «إلغاء» is the dismiss), and the
   two footer buttons stretch to fill the box edge-to-edge. */
.ds-decision-dialog--confirm {
  width: min(92vw, 24rem);              /* 384px — snug around the two buttons */
}
/* No coloured top accent stripe on the compact confirm box (the green/amber
   3px border-block-start reads as noise on a small yes/no box). The wide step
   dialogs keep theirs. */
.ds-decision-dialog--confirm .ds-decision-dialog__inner { border-block-start: 0; }
.ds-decision-dialog--confirm .ds-decision-dialog__header {
  justify-content: center;
  text-align: center;
}
.ds-decision-dialog--confirm .ds-decision-dialog__title { text-align: center; }
.ds-decision-dialog--confirm .ds-decision-dialog__lead,
.ds-decision-dialog--confirm .ds-decision-dialog__override-warn {
  text-align: center;
}
/* Footer buttons fill the width equally (no flex-end alignment). */
.ds-decision-dialog--confirm .ds-decision-dialog__footer {
  flex-wrap: nowrap;
  justify-content: stretch;
}
.ds-decision-dialog--confirm .ds-decision-dialog__footer .ds-btn { flex: 1 1 0; }
.ds-decision-dialog--confirm .ds-decision-dialog__footer .ds-btn .ds-btn__inner {
  width: 100%;
  justify-content: center;
}

/* ── Typed-confirmation layer (sensitive actions) ──
   Lives inside the shared #ds-confirm box; hidden unless dsConfirm() gets a
   `typedPhrase`. The hint names the exact phrase (bold) the reviewer must type;
   the confirm button stays disabled until the input matches. */
.ds-decision-dialog__typed { display: flex; }
.ds-decision-dialog__typed[hidden] { display: none; }   /* hidden-attr guard */
.ds-decision-dialog__typed {
  flex-direction: column;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
  text-align: start;
}
.ds-decision-dialog__typed-hint {
  font-size: var(--type-ar-14);
  color: var(--text-onCard-secondary);
}
.ds-decision-dialog__typed-hint strong {
  font-family: var(--font-activist-bold);
  color: var(--text-onCard-primary);
}
.ds-decision-dialog__typed-input { text-align: center; }

/* ── Reject confirmation pop (Figma 2858:1166) ──
   Application-level reject from a resubmitted (gold) field. A self-contained
   p-20 / gap-20 card on WHITE — no top accent stripe, no header separator, no
   footer divider/tint (the generic .ds-decision-dialog shell paints all three,
   so they're stripped here). Reuses the shell + .ds-btn; driven by dsReject(). */
.ds-decision-dialog--reject-confirm { width: min(94vw, 24rem); }   /* 384px (Figma 2858:1166) */
.ds-decision-dialog--reject-confirm .ds-decision-dialog__inner {
  border-block-start: 0;              /* no coloured top stripe */
  gap: var(--space-5);               /* 20px stack (Figma gap-20) */
  padding: var(--space-5);           /* 20px around everything (Figma p-20) */
  align-items: center;
}
/* The shell's footer carries a top border + grey fill; stripped here. (The
   header/body wrappers aren't used — the card lays out its own badge / text /
   verify stack via .ds-reject-pop__main below.) */
.ds-decision-dialog--reject-confirm .ds-decision-dialog__footer {
  padding: 0;
  border-block-start: 0;             /* NO divider line */
  background: transparent;           /* NO grey tint */
  gap: var(--space-3);               /* 12px (Figma footer gap-12) */
  flex-wrap: nowrap;
  justify-content: stretch;
  width: 100%;
  /* LTR-arranged like the Figma (2858:1233): «تأكيد الرفض» on the LEFT
     (x=0), «إلغاء» on the RIGHT (x=178). Source order [confirm, cancel] +
     direction:ltr lands the primary on the left; the Arabic glyphs inside each
     button still shape RTL. The cancel's [label, ✕] also reads label-then-✕. */
  direction: ltr;
}
/* Alert badge: rose square (p-8) + 32px radicalRed alert-02 glyph. */
.ds-reject-pop__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);            /* 8px */
  border-radius: var(--radius-4);
  background: var(--color-radicalRed-50);
}
.ds-reject-pop__badge-icon {
  display: block;
  inline-size: 2rem;   /* 32px */
  block-size: 2rem;
  background-color: var(--color-radicalRed-600);
  -webkit-mask: url(/static/icons/fig-alert-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-02.svg) center / contain no-repeat;
}
/* Main stack (Figma 2863:1411): text-group → verify-group, gap-20. */
.ds-reject-pop__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);               /* 20px */
  width: 100%;
}
/* Text group (2863:1408): title (centred) + lead (right-aligned), gap-16. */
.ds-reject-pop__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);               /* 16px */
  width: 100%;
}
.ds-reject-pop__title { text-align: center; color: var(--color-radicalRed-950); }
/* Body (Figma 2858:1381, text-right): the doctor name blue, the clause bold. */
.ds-reject-pop__lead {
  margin: 0;
  width: 100%;
  text-align: right;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  line-height: 1.5;
  color: var(--color-radicalRed-1000);
}
.ds-reject-pop__lead strong { font-family: var(--font-activist-semibold); }
.ds-reject-pop__name { color: var(--color-blue-600); }
/* Verify group (Figma 2859:1403): phrase chip → field, gap-12. */
.ds-reject-pop__verify {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);               /* 12px */
  width: 100%;
}
/* Phrase chip (Figma 2859:1392): rose box holding the hint + the phrase-to-copy
   stacked (gap-12), p-16, right-aligned — the divider is gone. */
.ds-reject-pop__phrase {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);               /* 12px */
  padding: var(--space-4);            /* 16px */
  border-radius: var(--radius-4);
  background: var(--color-radicalRed-50);
  width: 100%;
  box-sizing: border-box;
}
.ds-reject-pop__hint {
  margin: 0;
  text-align: right;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--color-radicalRed-1000);   /* #0a0004 */
}
.ds-reject-pop__phrase-word {
  margin: 0;
  text-align: right;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  color: var(--color-radicalRed-600);     /* #ed1156 */
}
/* The labelled input REUSES the canonical .ds-field component (label + ring +
   card + row + input). Only the trailing file-x-corner glyph is added here,
   tinted with the field's own icon token. */
.ds-reject-pop__field-ico {
  display: inline-block;
  inline-size: 1.5rem;   /* 24px (Figma 2864:1428) */
  block-size: 1.5rem;
  flex-shrink: 0;
  background-color: var(--field-default-icon);
  -webkit-mask: url(/static/icons/fig-square-unlock-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-square-unlock-02.svg) center / contain no-repeat;
}
/* Cancel button's trailing ✕ (20px, inherits the button's blue text). */
.ds-reject-pop__cancel-ico {
  display: inline-block;
  inline-size: 1.25rem;   /* 20px */
  block-size: 1.25rem;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-cancel-01.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-cancel-01.svg) center / contain no-repeat;
}
/* Footer buttons fill the row equally. The cancel «إلغاء» is the plain canonical
   .ds-btn--secondary (no restyle). The primary «تأكيد الرفض» reuses the .ds-btn
   shell but takes the design's EXACT fill + shadow: the Figma uses FLAT
   radicalRed-600 #ed1156 (NOT the panel danger's true-red #dc2626 gradient) with
   a radicalRed-tinted two-layer drop-shadow (2858:1235). The scoped selector +
   !important beat panel-shell's own !important danger gradient. Only the enabled
   state is recoloured — disabled keeps the muted panel look (gate cue). */
.ds-decision-dialog--reject-confirm .ds-decision-dialog__footer .ds-btn { flex: 1 1 0; }
.ds-decision-dialog--reject-confirm .ds-decision-dialog__footer .ds-btn .ds-btn__inner {
  width: 100%;
  justify-content: center;
}
/* The primary deliberately does NOT use .ds-btn--danger: the panel-shell danger
   variant is a true-red (#dc2626) GRADIENT that wins the cascade over any scoped
   override (layer quirk). Instead it's a bare .ds-btn shell + this class, so no
   panel-shell variant rule competes — a FLAT radicalRed-600 #ed1156 fill with
   the Figma red two-layer shadow (2858:1235). Disabled = dimmed (the typed gate
   cue). */
.ds-reject-pop__confirm .ds-btn__inner {
  background-color: var(--color-radicalRed-600);   /* #ed1156 */
  color: var(--color-radicalRed-50);               /* #fff0f2 */
  /* !important: panel-shell parks a transparent two-layer box-shadow on
     .ds-btn__inner with !important (transition placeholder), which beats a plain
     declaration. */
  box-shadow:
    0 6px 6px rgba(237, 17, 86, 0.06),
    0 12px 12px rgba(237, 17, 86, 0.08) !important;
}
/* Interactive states (Figma 3024:1298 danger): fill-only shift — hover -500,
   pressed -700; text + shadow stay constant. disabled → -400 fill + -200 label
   (keeps the shadow, per the design). Guarded :not(:disabled). */
.ds-reject-pop__confirm:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-radicalRed-500);
}
.ds-reject-pop__confirm:active:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-radicalRed-700);
}
.ds-reject-pop__confirm:disabled .ds-btn__inner,
.ds-reject-pop__confirm[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--color-radicalRed-400);
  color: var(--color-radicalRed-200);
}
/* Cancel «إلغاء» (Figma 2858:1242): a blue-600 border on the OUTER .ds-btn (its
   2px pad becomes the inset gap) + a blue-50 inner fill + blue text/✕ — NOT the
   panel .ds-btn--secondary (grey border + white). Same tactic as the primary:
   a bare .ds-btn shell + this class, so no panel-shell variant rule competes. */
:is(#ds-reject, #ds-approve, #ds-unapprove, #ds-stop, #cert-history-pop) .ds-reject-pop__cancel {
  border-color: #234cf0 !important;         /* blue-600 (base .ds-btn forces transparent !important) */
  border-width: var(--stroke-1) !important;
  border-radius: var(--radius-4) !important;   /* panel-shell forces radius-8 */
}
:is(#ds-reject, #ds-approve, #ds-unapprove, #ds-stop, #cert-history-pop) .ds-reject-pop__cancel .ds-btn__inner {
  background-color: #eff4ff;   /* blue-50 */
  color: #234cf0;              /* blue-600 */
  border-radius: var(--radius-2);
  box-shadow: none !important;              /* drop the __inner placeholder shadow */
}
/* Hover / pressed (Figma 3024:1282 secondary): fill-only shift blue-50 → 100 →
   200; the border + text stay blue-600. */
:is(#ds-reject, #ds-approve, #ds-unapprove, #ds-stop, #cert-history-pop) .ds-reject-pop__cancel:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: #dae6ff;   /* blue-100 */
}
:is(#ds-reject, #ds-approve, #ds-unapprove, #ds-stop, #cert-history-pop) .ds-reject-pop__cancel:active:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: #bdd4ff;   /* blue-200 */
}

/* ── Approve confirmation pop (green/success variant of the rich confirm) ──
   Reuses the reject pop's layout + .ds-reject-pop__main/__text/__lead; only the
   badge, title and primary button are re-themed green. No phrase chip, no
   typed gate. */
.ds-approve-pop__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);            /* 8px */
  border-radius: var(--radius-4);
  background: var(--color-green-50);
}
.ds-approve-pop__badge-icon {
  display: block;
  inline-size: 2rem;   /* 32px */
  block-size: 2rem;
  background-color: var(--color-green-600);
  -webkit-mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
}
.ds-approve-pop__title { text-align: center; color: var(--color-green-950); }
/* Approve message is a short centred line (no doctor name like the reject lead). */
.ds-approve-pop__lead { text-align: center; }
/* The SUBJECT of the question — the step being approved — rides in the box
   below, not inside the sentence. It was briefly set inline at 22px bold: a
   word two sizes larger dropped into a 16px centred line reads as a BROKEN
   line, not as emphasis (the baseline steps and the leading opens around that
   one word), and Arabic makes it worse because the eye catches instead of
   reading through. Prominence comes from SPACE, not from inflating a run of
   text — the pop already owns a display-size box for exactly this. */
/* ── The VALUE under review ────────────────────────────────────────────────
   The one fact the approve decision turns on, at display size. A tinted box
   rather than a bare line: it has to read as the SUBJECT of the sentence
   above it, not as more prose. `min-height` keeps the dialog from resizing
   between a one-word value and an empty one; `max-height` + scroll keeps a
   long value (an address, a multi-line note) from pushing the footer off a
   short viewport — the confirm button must never be the thing that scrolls
   out of reach. `overflow-wrap: anywhere` because the value is DATA: an
   unbroken 40-character licence number would otherwise widen the dialog. */
.ds-approve-pop__value {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 3.5rem;                /* 56px */
  max-height: 9rem;                  /* 144px — ~4 lines at 24px */
  overflow-y: auto;
  padding: var(--space-4);           /* 16px */
  border-radius: var(--radius-8);
  background: var(--color-green-50);
  text-align: center;
}
.ds-approve-pop__value-text {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-24);
  line-height: 1.4;
  color: var(--color-green-950);
  overflow-wrap: anywhere;
}
/* A document has no scalar value — the box names its FILE instead, and an
   absent one is stated in the secondary ink so it never reads as a value. */
.ds-approve-pop__value--muted {
  background: var(--color-blackHaze-50);
}
.ds-approve-pop__value--muted .ds-approve-pop__value-text {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-18);
  color: var(--text-onCard-secondary);
}
/* Primary «تأكيد الاعتماد» — FLAT green-600 + a green two-layer shadow. Bare
   .ds-btn shell + this class (no .ds-btn--success) so panel-shell doesn't
   compete. Disabled state isn't used (no gate), so no dimmed rule needed. */
.ds-approve-pop__confirm .ds-btn__inner {
  background-color: var(--color-green-600);   /* #00b48d */
  color: var(--color-white-50);
  box-shadow:
    0 6px 6px rgba(0, 180, 141, 0.06),
    0 12px 12px rgba(0, 180, 141, 0.08) !important;
}
/* Hover: fill-only shift to green-500 (no Figma success spec — same -600→-500
   pattern as the other variants; shadow stays constant). */
.ds-approve-pop__confirm:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-green-500);
}

/* ── Disable / stop confirmation pop (Figma 3019:1073) ──
   The PINK (danger) member of the rich-confirm family. Reuses the reject pop's
   shell + .ds-reject-pop__badge (rose square + radicalRed alert-02, identical to
   the Figma badge), .ds-reject-pop__title (centred radicalRed-950), the danger
   name highlight (.ds-decision-dialog__title-name = radicalRed-600), the pink
   .ds-reject-pop__confirm and the blue .ds-reject-pop__cancel. The ONLY new
   piece: the lead is centred + dimmed to 70% (Figma 3019:1080). */
.ds-stop-pop__lead {
  text-align: center;
  opacity: 0.7;
}
/* Trim the line-height leading off the title + lead — the Figma's OWN technique
   on node 3019:1073 (text-box-trim: trim-both; text-box-edge: cap alphabetic) —
   so the stack gaps land on the glyph edges exactly as designed: badge↔title 20,
   title↔lead 16, lead↔footer 20, instead of the line-height-inflated visual
   gaps. Scoped to #ds-stop so the reject/approve/unapprove siblings keep their
   spacing. Graceful: browsers without text-box-* ignore it (slightly looser). */
#ds-stop .ds-reject-pop__title,
#ds-stop .ds-stop-pop__lead {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

/* ── Unapprove / withdraw-approval pop (Figma 2868:1437) ──
   Editing a FIELD or STEP after it was approved — the GOLD (warning) member of
   the rich-confirm family. Reuses the reject pop's shell + structural classes
   (.ds-decision-dialog--reject-confirm, .ds-reject-pop__main/__text/__lead/
   __verify/__phrase/__hint/__phrase-word/__field-ico/__cancel); this block only
   re-themes the badge / title / lead and adds the two pieces the design
   introduces: the state-transition strip and the numbered note rows. Driven by
   window.dsUnapprove() (typed gate «سحب الاعتماد»). */
/* Badge: gold-50 square (p-8) + 32px gold-600 alert-02 glyph (Figma 2868:1438). */
.ds-unapprove-pop__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);            /* 8px */
  border-radius: var(--radius-4);
  background: var(--color-gold-50);
}
.ds-unapprove-pop__badge-icon {
  display: block;
  inline-size: 2rem;   /* 32px */
  block-size: 2rem;
  background-color: var(--color-gold-600);
  -webkit-mask: url(/static/icons/fig-alert-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-02.svg) center / contain no-repeat;
}
/* Text group: the design stacks title+lead at gap-12 (the reject's is 16). */
.ds-unapprove-pop__text { gap: var(--space-3); }
/* Title «إعادة X إلى المراجعة» (2868:1443): gold-950 base, the subject gold-600,
   the trailing «المراجعة» blue-700 #1836dd. Centred like the family's titles. */
.ds-unapprove-pop__title { text-align: center; color: var(--color-gold-950); }
.ds-unapprove-pop__title-name { color: var(--color-gold-600); }
.ds-unapprove-pop__title-review { color: var(--color-blue-700); }
/* Lead (2868:1444): near-black ink, centred under the title; the doctor name is
   BOLD black (unlike the reject lead's blue name). */
.ds-unapprove-pop__lead { text-align: center; color: var(--color-gold-1000); }
/* Scoped under the lead to outrank `.ds-reject-pop__lead strong` (semibold). */
.ds-unapprove-pop__lead .ds-unapprove-pop__name {
  font-family: var(--font-activist-bold);
  color: var(--color-gold-1000);
}
/* State-transition strip (2871:1500): blue-50 band (px-16 py-8, gap-20, square
   corners) centred to its content — «معتمد» ▪green ≪ ▪blue «في انتظار المراجعة».
   Each state = 12px swatch + 12px semibold label (swatch FIRST in source so RTL
   parks it on the right of its label, matching the Figma x-coords). */
.ds-unapprove-pop__strip {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Design update (Figma 2871:1500, size-full): the strip FILLS the pop width
     — a full-bleed blue band — with its content kept centred. Was align-self:
     center (hugging its content). */
  align-self: stretch;
  gap: var(--space-5);               /* 20px */
  padding: var(--space-2) var(--space-4);   /* 8px 16px */
  background: var(--color-blue-50);
}
.ds-unapprove-pop__state {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);               /* 8px */
}
.ds-unapprove-pop__swatch {
  inline-size: 0.75rem;   /* 12px */
  block-size: 0.75rem;
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
.ds-unapprove-pop__state-label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  white-space: nowrap;
}
.ds-unapprove-pop__state--approved .ds-unapprove-pop__swatch { background: var(--color-green-500); }
.ds-unapprove-pop__state--approved .ds-unapprove-pop__state-label { color: var(--color-green-950); }
.ds-unapprove-pop__state--pending .ds-unapprove-pop__swatch { background: var(--color-blue-500); }
.ds-unapprove-pop__state--pending .ds-unapprove-pop__state-label { color: var(--color-blue-950); }
/* 20px chevrons-left between the states — points at the NEW state (left in RTL).
   On an LTR page the new state sits right, so the glyph flips. */
.ds-unapprove-pop__arrow {
  display: inline-block;
  inline-size: 1.25rem;   /* 20px */
  block-size: 1.25rem;
  flex-shrink: 0;
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/fig-chevrons-left.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-chevrons-left.svg) center / contain no-repeat;
}
html[dir="ltr"] .ds-unapprove-pop__arrow { transform: scaleX(-1); }
/* Numbered consequence notes (2870:1479, gap-4): gold-100 rows (px-16 py-8,
   radius-4, gap-12) — a 24px gold-300 number chip (square, brightSun-950 16px
   semibold digit) leading on the right, then 14px gold-950 text. */
.ds-unapprove-pop__notes {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);               /* 4px */
  width: 100%;
}
.ds-unapprove-pop__note {
  display: flex;
  align-items: center;
  gap: var(--space-3);               /* 12px */
  padding: var(--space-2) var(--space-4);   /* 8px 16px */
  border-radius: var(--radius-4);
  background: var(--color-gold-100);
}
.ds-unapprove-pop__note-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1.5rem;   /* 24px */
  block-size: 1.5rem;
  flex-shrink: 0;
  background: var(--color-gold-300);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-16);
  color: var(--color-brightSun-950);
}
.ds-unapprove-pop__note-text {
  margin: 0;
  text-align: start;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  line-height: 1.4;
  color: var(--color-gold-950);
}
/* Phrase chip re-theme: the rose box goes gold (2868:1446) and the hint is
   SEMIBOLD here (2868:1447) where the reject's is regular. */
#ds-unapprove .ds-reject-pop__phrase { background: var(--color-gold-50); }
#ds-unapprove .ds-reject-pop__hint {
  font-family: var(--font-activist-semibold);
  color: var(--color-gold-1000);
}
#ds-unapprove .ds-reject-pop__phrase-word { color: var(--color-gold-600); }
/* Primary «تأكيد السحب» (2868:1458): the project PRIMARY blue — but FLAT
   blue-600 #234cf0 + the Figma blue two-layer shadow, NOT panel-shell's
   .ds-btn--primary gradient (which wins any scoped override). Same tactic as
   the family: bare .ds-btn shell + this class. Disabled = the typed-gate cue. */
.ds-unapprove-pop__confirm .ds-btn__inner {
  background-color: var(--color-blue-600);   /* #234cf0 */
  color: var(--color-blue-50);               /* #eff4ff */
  box-shadow:
    0 6px 6px rgba(35, 76, 240, 0.06),
    0 12px 12px rgba(35, 76, 240, 0.08) !important;
}
/* Interactive states (Figma 3024:1286 primary): fill-only shift — hover -500,
   pressed -700; text + shadow stay constant. disabled → -400 fill + -200 label
   (keeps the shadow). Guarded :not(:disabled). */
.ds-unapprove-pop__confirm:hover:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-blue-500);
}
.ds-unapprove-pop__confirm:active:not(:disabled):not([aria-disabled="true"]) .ds-btn__inner {
  background-color: var(--color-blue-700);
}
.ds-unapprove-pop__confirm:disabled .ds-btn__inner,
.ds-unapprove-pop__confirm[aria-disabled="true"] .ds-btn__inner {
  background-color: var(--color-blue-400);
  color: var(--color-blue-200);
}

/* ── Edit-history pop (#cert-history-pop, «السجل») ──
   Replaces the old inline «عند الطلب» line inside the resubmitted cell.
   Composed from the rich-pop family shell (.ds-decision-dialog--reject-confirm
   + .ds-reject-pop__main/__text + the shared blue cancel); this block only
   adds the gold clock badge, the gold-highlighted title and the before/after
   entry tiles. */
.ds-hist-pop__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);            /* 8px */
  border-radius: var(--radius-4);
  background: var(--color-gold-50);
}
.ds-hist-pop__badge-icon {
  display: block;
  inline-size: 2rem;   /* 32px */
  block-size: 2rem;
  background-color: var(--color-gold-600);
  -webkit-mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
}
.ds-hist-pop__title { text-align: center; color: var(--color-gold-950); }
.ds-hist-pop__title-name { color: var(--color-gold-600); }
/* The before/after entries are CANONICAL field cells (.ds-field-cell row +
   status footer — typography, the 12px cap-trimmed label↔value gap and the
   full-width footer strip all come from the component). This block only adds
   the decided-style 2px frame + the footer themes: BEFORE = the gold flagged
   past (gold-600 frame, gold-100 strip, clock glyph), AFTER = the resubmitted
   present awaiting review (blue-500 frame, blue-50 strip, info glyph). */
.ds-hist-pop__entries {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);               /* 12px */
  width: 100%;
}
/* Doubled-class specificity: the base .ds-field-cell rule (transparent border)
   sits LATER in this file and would otherwise win the tie. */
.ds-field-cell.ds-hist-pop__cell { padding: var(--space-0_5); }   /* 2px decided-frame inset */
.ds-field-cell.ds-hist-pop__cell--before { border-color: var(--color-gold-600); }
.ds-field-cell.ds-hist-pop__cell--after  { border-color: var(--color-blue-500); }
.ds-hist-pop__cell--before .ds-field-cell__value { color: var(--color-gold-950); }
.ds-hist-pop__cell--before .ds-field-cell__status {
  background: var(--color-gold-100);
}
.ds-hist-pop__cell--after .ds-field-cell__status {
  background: var(--color-blue-50);
}
.ds-hist-pop__cell .ds-field-cell__status-text {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
}
.ds-hist-pop__cell--before .ds-field-cell__status-text { color: var(--color-gold-600); }
.ds-hist-pop__cell--after .ds-field-cell__status-text { color: var(--color-blue-600); }
.ds-hist-pop__cell .ds-field-cell__status-icon {
  inline-size: 1rem;     /* 16px (the resub footer's clock size) */
  block-size: 1rem;
  flex-shrink: 0;
}
.ds-hist-pop__cell--before .ds-field-cell__status-icon {
  background-color: var(--color-gold-600);
  -webkit-mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
}
.ds-hist-pop__cell--after .ds-field-cell__status-icon {
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/fig-information-square.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-information-square.svg) center / contain no-repeat;
}
/* The change-request reason strip — the CANONICAL flagged-cell footer look
   (Figma 2817:1633): pale brightSun band (min 40px), 20px alert-circle parked
   far right, 14px semibold brightSun-600 reason text. These rules sit AFTER
   the --before footer theme above (same specificity) so they win the tie for
   this one strip. */
.ds-hist-pop__cell--before .ds-hist-pop__reason-strip {
  background: var(--color-brightSun-50);
  min-block-size: var(--space-8);
}
.ds-hist-pop__cell--before .ds-hist-pop__reason-strip .ds-field-cell__status-text {
  font-size: var(--type-ar-14);
  color: var(--color-brightSun-600);
}
.ds-hist-pop__cell--before .ds-hist-pop__reason-strip .ds-field-cell__status-icon {
  inline-size: 1.25rem;   /* 20px (Figma 2823:1029) */
  block-size: 1.25rem;
  background-color: var(--color-brightSun-600);
  -webkit-mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
}

/* Field card: Figma 2859:1395 uses a 16px label→row gap (the canonical card's
   default is 12px). The card keeps its canonical 88px height (the component
   standard; the Figma 79px would need clipping the label leading and isn't worth
   fighting the shared component over). */
:is(#ds-reject, #ds-unapprove) .ds-field__card { gap: var(--space-4); }

@media (prefers-reduced-motion: reduce) {
  .ds-decision-dialog[open] { animation: none; }
}

/* ============================================================
   Qualification card (doctor detail → qualifications step)
   Figma node 2667:5888. Each qualification renders as a card with
   a header (degree badge + entity number on the right, preview-
   certificate link on the left), a hairline divider, and a 2×2
   grid of white field cells.
   ============================================================ */
/* Spacing note: the project's --space-N scale is OFFSET from Figma's
   Spacing/N tokens. Map by PIXEL value, not by the number suffix:
   Figma 4px=--space-1, 8px=--space-2, 12px=--space-3, 16px=--space-4,
   20px=--space-5, 24px=--space-6, 32px=--space-7. */
.ds-qual-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);          /* Figma 16px */
  padding: var(--space-4);      /* Figma 16px */
  background: var(--color-blackHaze-100);
  border-radius: var(--radius-2);
}

.ds-qual-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  flex-wrap: wrap;
}

/* Degree badge group: number chip + degree chip. RTL flex → first
   child (number) lands on the far right, degree chip to its left.
   Figma gap between the two chips = 8px. */
.ds-qual-card__badge {
  display: inline-flex;
  align-items: stretch;
  gap: var(--space-2);          /* Figma 8px */
}

.ds-qual-card__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  padding: var(--space-3);      /* Figma 12px */
  background: var(--color-blackHaze-50);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-18);
  color: var(--color-blackHaze-950);
  opacity: 0.7;
}

.ds-qual-card__degree {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                       /* Figma 8px */
  padding: var(--space-3) var(--space-6);    /* Figma 12px / 24px */
  background: var(--color-blackHaze-50);
}

.ds-qual-card__degree-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  background-color: var(--color-blackHaze-950);
  -webkit-mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
}

.ds-qual-card__degree-text {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);          /* Figma 4px */
  font-size: var(--type-ar-18);
  white-space: nowrap;
}

.ds-qual-card__degree-cap {
  font-family: var(--font-activist-semibold);
  color: var(--color-blackHaze-950);
  opacity: 0.7;
}

.ds-qual-card__degree strong {
  font-family: var(--font-activist-bold);
  font-weight: normal;
  color: var(--color-blackHaze-1000);
}

/* Preview-certificate link: blue chip with a blue ring (Figma's
   border + 2px gap rendered as an outline). Figma inner padding 8px/16px,
   gap 8px. */
.ds-qual-card__preview {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                       /* Figma 8px */
  padding: var(--space-2) var(--space-4);    /* Figma 8px / 16px */
  background: var(--color-blue-50);
  color: var(--color-blue-600);
  border: var(--stroke-1) solid var(--color-blue-600);
  border-radius: var(--radius-2);
  outline: 2px solid var(--color-blue-50);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.ds-qual-card__preview:hover {
  background: var(--color-blue-100);
}

.ds-qual-card__preview-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-view.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-view.svg) center / contain no-repeat;
}

.ds-qual-card__preview--disabled {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
  filter: grayscale(1);
}

.ds-qual-card__divider {
  width: 100%;
  height: 1px;
  background: var(--color-blackHaze-800);
  opacity: 0.08;
  border-radius: 4px;
}

.ds-qual-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);          /* Figma 16px (row + column) */
  width: 100%;
  margin: 0;
}

.ds-qual-cell {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);          /* Figma 12px */
  padding: var(--space-4);      /* Figma 16px */
  background: var(--color-blackHaze-50);
  box-shadow: 0 6px 6px rgba(7, 8, 10, 0.04);
}

.ds-qual-cell__label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--color-blackHaze-950);
  opacity: 0.7;
}

.ds-qual-cell__value {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  color: var(--color-blackHaze-1000);
}

/* ── Qualification status row (Figma 2700:4549 / 4591 / 4646) ──
   Each certificate renders as a compact row whose colour reflects its A7 review
   status (awaiting / approved / needs_edit); the field details live in the
   certificate-review modal (opened via the preview button). Page is RTL: the
   title group hugs the right edge, the actions (status pill + preview) the left. */
.ds-qual-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  /* The document row's own content already measures this; declaring it makes
     the height a NAMED value other components can match instead of guessing
     (see `.ds-empty-state`, which stands in for these rows). A min-size never
     shrinks a row whose content grew — a wrapped title still expands. */
  min-block-size: var(--ds-doc-row-block-size);
  border-radius: var(--radius-2);
  background: var(--color-blackHaze-100);
}
.ds-qual-row--approved { background: var(--color-green-100); }
.ds-qual-row--needs_edit {
  background: var(--color-brightSun-100);
  /* Figma 2700:4645: 2px brightSun-600 ring with a 2px card-coloured inset
     between the border and the amber fill. */
  border: var(--stroke-2) solid var(--color-brightSun-600);
  box-shadow: inset 0 0 0 var(--stroke-2) var(--bg-card);
}
/* `rejected` is legacy / out-of-band only (no product path writes it for a
   qualification), but a row carrying it must still read as rejected, not as a
   neutral pending row — radicalRed mirror of the needs_edit treatment. */
.ds-qual-row--rejected {
  background: var(--color-radicalRed-50);
  border: var(--stroke-2) solid var(--color-radicalRed-600);
  box-shadow: inset 0 0 0 var(--stroke-2) var(--bg-card);
}
.ds-qual-row--rejected .ds-qual-row__badge {
  background: var(--color-radicalRed-50);
  color: color-mix(in srgb, var(--color-radicalRed-700) 70%, transparent);
}
.ds-qual-row--rejected .ds-qual-row__titlebox { background: var(--color-radicalRed-50); }
.ds-qual-row--rejected .ds-qual-row__title-pre { color: var(--color-radicalRed-700); }
.ds-qual-row--rejected .ds-qual-row__title-main { color: var(--color-radicalRed-700); }
/* Title group (right edge): index/check badge + title box (8px gap). */
.ds-qual-row__titlegroup {
  display: inline-flex;
  align-items: stretch;
  gap: var(--space-2);
  min-width: 0;
}
.ds-qual-row__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  padding: var(--space-3);
  background: var(--color-blackHaze-50);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-18);
  color: color-mix(in srgb, var(--color-blackHaze-950) 70%, transparent);
  flex-shrink: 0;
}
.ds-qual-row--needs_edit .ds-qual-row__badge {
  background: var(--color-brightSun-50);
  color: color-mix(in srgb, var(--color-brightSun-950) 70%, transparent);
}
.ds-qual-row--approved .ds-qual-row__badge { background: var(--color-green-500); }
.ds-qual-row__check {
  width: 20px;
  height: 20px;
  /* The badge is 24px wide with 12px padding (content box = 0), so without
     flex-shrink:0 this empty masked span collapses to 0 and the check vanishes. */
  flex-shrink: 0;
  background-color: var(--color-green-50);
  -webkit-mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
}
.ds-qual-row__titlebox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* 12px all sides (horizontal was 24px / --space-6). */
  padding: var(--space-3);
  background: var(--color-blackHaze-50);
  min-width: 0;
}
.ds-qual-row--approved .ds-qual-row__titlebox { background: var(--color-green-50); }
.ds-qual-row--needs_edit .ds-qual-row__titlebox { background: var(--color-brightSun-50); }
.ds-qual-row__icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  background-color: var(--color-blackHaze-500);
  -webkit-mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
}
.ds-qual-row--approved .ds-qual-row__icon { background-color: var(--color-green-500); }
.ds-qual-row--needs_edit .ds-qual-row__icon {
  background-color: var(--color-brightSun-500);
  -webkit-mask-image: url(/static/icons/fig-alert-01.svg);
  mask-image: url(/static/icons/fig-alert-01.svg);
}
.ds-qual-row__title {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  font-size: var(--type-ar-18);
  white-space: nowrap;
  min-width: 0;
  /* A document row's height must not depend on WHICH SCRIPT its title happens
     to contain. With `normal` the line box is the tallest metrics of every font
     the text touched, and Activist carries no Latin glyphs — so a single latin
     run (a passport number, an English university, a latin-digit date) falls
     back to Noto Sans Arabic, whose metrics are ~2.1× em. Measured at 18px:
     arabic-only 22px, one latin run 38px, which grew the title box 46→62 and
     the row 78→94 — one row out of line with every other card on the page while
     `--ds-doc-row-block-size` still claimed 78. An explicit line-height makes
     the box a function of the design, not of the doctor's data. */
  line-height: var(--line-120);
}
.ds-qual-row__title-pre {
  font-family: var(--font-activist-semibold);
  opacity: 0.7;
  color: var(--color-blackHaze-950);
}
.ds-qual-row__title-main {
  font-family: var(--font-activist-bold);
  color: var(--color-blackHaze-1000);
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-qual-row--approved .ds-qual-row__title-pre { color: var(--color-green-950); }
.ds-qual-row--approved .ds-qual-row__title-main { color: var(--color-green-1000); }
.ds-qual-row--needs_edit .ds-qual-row__title-pre { color: var(--color-brightSun-950); }
.ds-qual-row--needs_edit .ds-qual-row__title-main { color: var(--color-brightSun-1000); }
/* Actions (left edge): status pill + preview ring button. */
.ds-qual-row__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}
.ds-qual-row__preview {
  display: inline-flex;
  padding: var(--space-0_5);
  border: var(--stroke-1) solid var(--color-blue-600);
  border-radius: var(--radius-4);
  background: transparent;
  color: var(--color-blue-600);
  cursor: pointer;
}
.ds-qual-row__preview-fill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--color-blue-50);
  border-radius: var(--radius-2);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  white-space: nowrap;
  transition: background-color 0.15s ease;
}
.ds-qual-row__preview:hover .ds-qual-row__preview-fill { background: var(--color-blue-100); }
.ds-qual-row__preview-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url(/static/icons/fig-view.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-view.svg) center / contain no-repeat;
}
/* Per-row status now uses the canonical `ds_status_pill` component
   (.ds-status-pill--awaiting/approved/changes) — no bespoke pill here. */

.ds-qual-cell__value--empty {
  font-family: var(--font-activist-regular);
  color: var(--color-blackHaze-1000);
  opacity: var(--opacity-40);
}

/* Single-column field grid on narrow viewports. */
@media (max-width: 640px) {
  .ds-qual-grid {
    grid-template-columns: 1fr;
  }
  /* Phone: stack the qualification certificate row into a 2-row card
     (Figma 2737:1316). Row 1 = title group (badge + title box); row 2 =
     full-width actions. The «معاينة الشهادة» button HUGS its content (does not
     stretch) and stays pinned to the start (left, RTL) edge, while the status
     pill stays pinned to the leading (right) edge — space-between holds the gap.
     DOM order is unchanged, so the badge/check stay on the right as on desktop. */
  .ds-qual-row {
    flex-direction: column;
    align-items: flex-start;     /* RTL → both rows hug the right edge */
    justify-content: flex-start;
    gap: var(--space-4);         /* 16px between the title row and actions row */
  }
  .ds-qual-row__titlegroup {
    max-width: 100%;             /* let a long title ellipsis instead of overflowing */
  }
  .ds-qual-row__actions {
    width: 100%;                 /* actions span the full card width… */
    justify-content: space-between; /* …pill pinned right, hugged button left */
  }
  .ds-qual-row__preview {
    flex: 0 0 auto;              /* hug content — no stretching, consistent size */
  }
}

/* ============================================================
   Certificate review modal (doctor detail → qualification cert)
   Design-system tokens only; RTL. Spacing maps by PIXEL value to
   the project scale (16px=--space-4, 12px=--space-3, 8px=--space-2).
   ============================================================ */
.ds-cert-modal {
  /* native <dialog> opened via showModal(): centre EXACTLY in the viewport on
     both axes using the spec-canonical top-layer recipe — `inset: 0` defines a
     box equal to the viewport (the initial containing block for a top-layer
     element) and `margin: auto` then centres the fixed-width / fit-content box
     within it horizontally AND vertically.

     We deliberately AVOID `left/top: 50% + translate(-50%)`: in Chromium a
     `<dialog>` whose DOM ancestor carries a `transform` (the page's fade-in
     wrapper) resolves `position:fixed` percentages against THAT ancestor's box
     instead of the viewport, which pushed the modal off-centre toward the
     content column. `margin:auto` is immune to that. */
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(1100px, 94vw);
  max-width: 94vw;
  /* Fill the viewport vertically, leaving a fixed gap above and below.
     32px top + 32px bottom on desktop (16px each on phone/tablet — see the
     max-width: 1024px query below). `margin:auto` then centres this
     fixed-height box on both axes within the `inset:0` viewport box. */
  height: calc(100dvh - 64px);
  max-height: calc(100dvh - 64px);
  padding: 0;
  border: none;
  /* Modal box corner: 8px (--radius-8) per design. */
  border-radius: var(--radius-8);
  background: var(--bg-card);
  color: var(--text-onCard-primary);
  box-shadow: 0 12px 24px rgba(23, 28, 24, 0.12), 0 6px 12px rgba(23, 28, 24, 0.06);
  overflow: hidden;
}
.ds-cert-modal::backdrop {
  background: rgba(3, 3, 10, 0.55);
  backdrop-filter: blur(3px);
}

/* ── Open / close animation ──
   Layered, premium "expand from the centre" reveal — three coordinated beats:
     1. the backdrop focuses in: opacity + a blur(0 → 3px) so the page behind
        softens into place rather than just dimming;
     2. the panel grows from a vertically-compressed, faded state (scaleY around
        its own centre → spreads toward the top AND bottom) on a soft expo
        decelerate (cubic-bezier(0.16, 1, 0.3, 1));
     3. the content settles a beat later, rising a few px as it fades in — the
        opacity LEADS the scale, so the brief vertical "squish" plays out while the
        content is still transparent and is never seen.
   Pure CSS via @starting-style + `allow-discrete`, so the SAME motion plays in
   reverse on close (no JS; every open re-triggers it). Browsers without
   @starting-style open/close instantly — graceful degradation. transform-origin
   stays the default centre; `margin:auto` centring is untouched (scale only). */
.ds-cert-modal {
  transition:
    opacity 240ms cubic-bezier(0.4, 0, 0.2, 1),
    transform 420ms cubic-bezier(0.16, 1, 0.3, 1),
    overlay 420ms ease allow-discrete,
    display 420ms ease allow-discrete;
}
@starting-style {
  .ds-cert-modal[open] {
    opacity: 0;
    transform: scaleY(0.85);
  }
}
.ds-cert-modal:not([open]) {
  opacity: 0;
  transform: scaleY(0.85);
}
.ds-cert-modal::backdrop {
  transition:
    opacity 340ms ease,
    backdrop-filter 340ms ease,
    overlay 340ms ease allow-discrete,
    display 340ms ease allow-discrete;
}
@starting-style {
  .ds-cert-modal[open]::backdrop {
    opacity: 0;
    backdrop-filter: blur(0);
  }
}
.ds-cert-modal:not([open])::backdrop {
  opacity: 0;
  backdrop-filter: blur(0);
}
/* Beat 3: content rises + fades in just after the panel begins to expand. Entry
   only (@starting-style) — on close the panel's own scale+fade carries it out. */
.ds-cert-modal__inner {
  transition:
    opacity 300ms ease 70ms,
    transform 360ms cubic-bezier(0.16, 1, 0.3, 1) 70ms;
}
@starting-style {
  .ds-cert-modal[open] .ds-cert-modal__inner {
    opacity: 0;
    transform: translateY(12px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ds-cert-modal,
  .ds-cert-modal::backdrop,
  .ds-cert-modal__inner { transition: none; }
}

/* ── Certificate-to-certificate swap ──
   Navigating between certificates swaps the content INSTANTLY (no blank frame →
   no flicker) and then lets the title box, status pill and data grid settle UP
   into place. Transform-only — never an opacity dip — so nothing can flash. The
   JS seeds the start offset under `.is-cert-noanim` (transition suppressed),
   forces a reflow, then drops `.is-cert-enter` so the offset eases to rest. The
   document viewer is excluded (it repaints its own bytes). Reduced-motion users
   skip it entirely (the JS does an instant swap). */
.ds-cert-modal__titlebox,
.ds-cert-modal__head .ds-status-pill,
.ds-cert-data {
  transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);
}
.ds-cert-modal.is-cert-enter .ds-cert-modal__titlebox,
.ds-cert-modal.is-cert-enter .ds-cert-modal__head .ds-status-pill,
.ds-cert-modal.is-cert-enter .ds-cert-data {
  transform: translateY(10px);
}
.ds-cert-modal.is-cert-noanim .ds-cert-modal__titlebox,
.ds-cert-modal.is-cert-noanim .ds-cert-modal__head .ds-status-pill,
.ds-cert-modal.is-cert-noanim .ds-cert-data {
  transition: none;
}

.ds-cert-modal__inner {
  display: flex;
  flex-direction: column;
  /* Figma 2705:4976: the modal is a 20px-padded flex column whose sections are
     separated by 20px gaps (no full-bleed dividers). */
  gap: var(--space-5);
  padding: var(--space-5);
  height: 100%;
  box-sizing: border-box;
}

/* ── 1. Header ── */
.ds-cert-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-shrink: 0;
}
/* Title box (Figma 2705:4985): 40px-tall blackHaze-50 chip, 12px inline pad. */
.ds-cert-modal__titlebox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 40px;
  padding-inline: var(--space-3);
  background: var(--color-blackHaze-50);
  border-radius: var(--radius-4);
  min-width: 0;
}
.ds-cert-modal__cap {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  /* Figma mortarboard stroke = blackHaze-500 (#667991), not blue. */
  background-color: var(--color-blackHaze-500);
  -webkit-mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-mortarboard-02.svg) center / contain no-repeat;
}
/* Title = light «شهادة» prefix + bold degree, baseline-aligned, 18px. */
.ds-cert-modal__title {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  margin: 0;
  min-width: 0;
  font-size: var(--type-ar-18);
  line-height: 1.3;
  white-space: nowrap;
}
.ds-cert-modal__title-pre {
  font-family: var(--font-activist-semibold);
  color: var(--color-blackHaze-950);
  opacity: 0.7;
}
.ds-cert-modal__title-main {
  font-family: var(--font-activist-bold);
  color: var(--color-blackHaze-1000);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Title box tints with the certificate's review state — needs-edit → brightSun
   (Figma 2729:1091), approved → green (Figma 2729:1103); awaiting / under-review
   keep the neutral blackHaze default above. Both the box fill AND the title text
   recolour (main = -1000, light «شهادة» prefix = -950). certificate-modal.js
   toggles the modifier per certificate via [data-cert-titlebox]. */
.ds-cert-modal__titlebox--changes { background: var(--color-brightSun-50); }
.ds-cert-modal__titlebox--changes .ds-cert-modal__title-main { color: var(--color-brightSun-1000); }
.ds-cert-modal__titlebox--changes .ds-cert-modal__title-pre  { color: var(--color-brightSun-950); }
.ds-cert-modal__titlebox--approved { background: var(--color-green-50); }
.ds-cert-modal__titlebox--approved .ds-cert-modal__title-main { color: var(--color-green-1000); }
.ds-cert-modal__titlebox--approved .ds-cert-modal__title-pre  { color: var(--color-green-950); }
/* In request-changes mode the title box always carries the brightSun "changes"
   tint (Figma 2716:5086), regardless of the certificate's own verdict state. */
.ds-cert-modal.is-rc-mode .ds-cert-modal__titlebox { background: var(--color-brightSun-50); }
.ds-cert-modal.is-rc-mode .ds-cert-modal__title-main { color: var(--color-brightSun-1000); }
.ds-cert-modal.is-rc-mode .ds-cert-modal__title-pre  { color: var(--color-brightSun-950); }
/* brightSun chip inside the title box (Figma 2717:5149) — visible only in
   request-changes mode, where it replaces the header status pill. */
.ds-cert-modal__rc-chip { display: none; }
.ds-cert-modal.is-rc-mode .ds-cert-modal__rc-chip {
  display: block;
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  border-radius: var(--radius-2);
  background: var(--color-brightSun-500);
}
.ds-cert-modal.is-rc-mode .ds-cert-modal__head-actions .ds-status-pill { display: none; }
/* This modal's status pill uses a 4px corner (Figma) vs the 8px global pill.
   The coloured square sits on the LEADING (right, in RTL) edge then the text —
   the canonical pill order (dot before label), matching the row pills. */
.ds-cert-modal__head .ds-status-pill {
  border-radius: var(--radius-4);
}
.ds-cert-modal__head .ds-status-pill__dot {
  border-radius: var(--radius-2);
}
.ds-cert-modal__head-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  flex-shrink: 0;
}
/* The certificate-modal header close now uses the canonical close component
   (ds_close_button + .ds-close-btn--accent for the blue tint). The old
   .ds-cert-modal__close / .ds-cert-close-ico rules were removed 2026-06-05 —
   the glyph + behaviour live in `.ds-close-btn` (single source of truth). */

/* Certificate status pill now uses the canonical `.ds-status-pill` component
   (see ds_status_pill macro + its rules near the step-pill block). The old
   `.ds-cert-status` wrapper was removed — the macro emits a single self-styled
   pill, so no extra wrapper rule is needed here. */

/* ── 2. Document viewer ──
   `.ds-cert-viewer` is the NON-scrolling frame: it positions the floating
   controls (badge / page-nav / toolbar) and clips overflow, but never scrolls
   itself — so those controls stay FIXED while the document is panned. The
   inner `.ds-cert-viewer__scroll` is the only scroll surface. */
.ds-cert-viewer {
  position: relative;
  flex: 1 1 auto;
  /* No min-height of its own — it FILLS its parent (the frame in the modal, or
     the 352px rc-card preview slot). A min-height here would make the viewer
     overflow a shorter parent and the frame's overflow:hidden would clip the
     floating toolbar out of sight. The usable floor lives on the parent. */
  min-height: 0;
  overflow: hidden;
  /* Black viewer frame (per user preference) with a 4px corner. */
  background: var(--color-blackHaze-1000);
  border-radius: var(--radius-4);
}
/* ── Preview-box frame ──
   Wraps the document viewer + an optional change-request footer. A transparent
   flex passthrough by default; when the uploaded certificate FILE itself is
   flagged it takes the field-cell flagged treatment — a gold-600 frame + a pale
   brightSun footer carrying the reason (the 2817:1712 state applied to the
   preview box). */
.ds-cert-viewer-frame {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  /* Usable floor for the viewer so its floating toolbar is never squeezed out.
     Capped at 45vh so a SHORT window (landscape phone, split screen) shrinks
     the frame instead of overflowing the modal — the viewer fills whatever the
     frame is, so the toolbar always stays inside the visible black box. */
  min-height: min(20rem, 45vh);
  border: var(--stroke-1) solid transparent;
  border-radius: var(--radius-4);
  overflow: hidden;   /* clip the viewer's top + footer's bottom to the frame */
}
/* The frame owns the corners now so the footer's bottom corners clip cleanly;
   reparented out (into the rc attachment card) the viewer regains its own 4px. */
.ds-cert-viewer-frame > .ds-cert-viewer { border-radius: 0; }
.ds-cert-viewer-frame--flagged {
  /* The preview-box flagged frame is brightSun-600 (Figma 2824:1054) — distinct
     from the field-cell's gold-600 — with a 2px inset (spacing/1) between the
     border and the black viewer / brightSun footer. */
  border-color: var(--color-brightSun-600);
  padding: var(--space-0_5);
}
.ds-cert-viewer-frame__status[hidden] { display: none; }
.ds-cert-viewer-frame__status {
  display: flex;
  align-items: center;
  justify-content: flex-start;       /* rtl → content hugs the right edge */
  gap: var(--space-2);
  min-block-size: var(--space-8);    /* 40px (matches the field-cell footer) */
  padding: var(--space-2) var(--space-4);
  background: var(--color-brightSun-50);
  color: var(--color-brightSun-600);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
}
.ds-cert-viewer-frame__status-text {
  /* (text-box-trim removed — Chromium/Safari-only; the flex row already
     vertically centres this text, so it reads identically in every browser.) */
}
.ds-cert-viewer-frame__status-icon {
  inline-size: 1.25rem;
  block-size: 1.25rem;
  flex-shrink: 0;
  background-color: var(--color-brightSun-600);
  -webkit-mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
}
/* In request-changes mode the viewer is reparented into the attachment card, so
   this (now viewer-less) frame must not show in its normal slot. */
.ds-cert-modal.is-rc-mode .ds-cert-viewer-frame { display: none; }
.ds-cert-viewer__scroll {
  position: absolute;
  inset: 0;
  overflow: auto;
  /* Keep wheel/drag scrolling contained (don't bubble to the page/modal). */
  overscroll-behavior: contain;
  display: flex;
  /* `safe center` centres the document when it fits but switches to
     start-alignment once it overflows — without `safe`, flex centring pushes
     the overflowing top/left edges OUT of the scroll range, making the top of
     a zoomed document unreachable. */
  align-items: safe center;
  justify-content: safe center;
}
/* Grab cursor only when the content actually overflows (zoomed in); JS toggles
   `is-pannable` on the SCROLL layer. While dragging, force grabbing. */
.ds-cert-viewer__scroll.is-pannable { cursor: grab; }
.ds-cert-viewer__scroll.is-grabbing,
.ds-cert-viewer__scroll.is-grabbing * { cursor: grabbing !important; }
.ds-cert-viewer__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  /* `margin:auto` centres the stage within the scroll box when the document is
     smaller, while still allowing it to grow past the box (and scroll) when
     zoomed. */
  margin: auto;
  min-height: 320px;
  padding: var(--space-6);
}
/* Image surface: JS sizes it explicitly (width/height in px) for a contain-fit
   at zoom 1, then GROWS it on zoom so it genuinely overflows the viewer and the
   scroll container can pan it (rotation applied via transform). `flex-shrink:0`
   stops the flex stage from squashing it back to fit. The CSS max-* are a
   pre-JS fallback; JS sets max:none once it takes over sizing. */
.ds-cert-viewer__img {
  max-width: 100%;
  max-height: 64vh;
  object-fit: contain;
  flex-shrink: 0;
  border-radius: var(--radius-2);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}
/* Canvas surface (PDF): JS sizes it explicitly (canvas.style.width/height) to
   a contain-fit of the viewer — `max-height` is unreliable on <canvas> given a
   fixed intrinsic size, so no CSS max here. Zoom grows the canvas and the
   viewer's overflow:auto scrolls. */
.ds-cert-viewer__canvas {
  display: block;
  margin: auto;
  flex-shrink: 0;
  border-radius: var(--radius-2);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}
/* JS toggles the surfaces via the `hidden` attribute; the explicit display
   above would defeat it in engines where the UA [hidden] rule isn't
   !important (e.g. older Safari) — same guard as .ds-cert-namepop[hidden]. */
.ds-cert-viewer__canvas[hidden] { display: none; }
.ds-cert-viewer__empty {
  color: var(--color-blackHaze-400);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
}
/* File-name popover — toggled by the toolbar's file button. Floats just ABOVE
   the centred toolbar (near the bottom) so it never overlaps the document the
   way the old top-corner badge did. Centred with the same RTL-safe recipe as the
   toolbar. */
.ds-cert-namepop {
  position: absolute;
  /* sit above the 44px-tall toolbar (36px btn + 2×4px pad) + an 8px gap */
  inset-block-end: calc(var(--space-4) + 44px + var(--space-2));
  inset-inline-start: 50%;
  transform: translateX(50%); /* RTL-safe centring (page dir=rtl), matches toolbar */
  max-width: 80%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-2);
  background: rgba(3, 3, 10, 0.82);
  color: var(--color-blackHaze-50);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}
.ds-cert-namepop[hidden] { display: none; }
.ds-cert-viewer__pages[hidden] { display: none; }   /* hidden-attr guard (see canvas note) */
.ds-cert-viewer__pages {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-start: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1);
  /* Light floating pill so the blue square page-arrows + dark page count read
     cleanly over the dark document viewer. 8px corner. */
  border-radius: var(--radius-8);
  background: var(--bg-card);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
}
.ds-cert-viewer__pageind {
  color: var(--color-blackHaze-950);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  min-width: 44px;
  text-align: center;
}

/* Floating toolbar */
.ds-cert-toolbar {
  position: absolute;
  inset-block-end: var(--space-4);
  inset-inline-start: 50%;
  transform: translateX(50%); /* RTL-safe centring (page dir=rtl) */
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  border-radius: 999px;
  background: rgba(3, 3, 10, 0.72);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}

/* Icon buttons (toolbar + nav) */
.ds-cert-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--color-blackHaze-50);
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.15s ease;
}
.ds-cert-iconbtn:hover { background: rgba(255, 255, 255, 0.14); }
/* File-name toggle: highlighted while the name popover is showing. */
.ds-cert-iconbtn[aria-pressed="true"] { background: rgba(255, 255, 255, 0.24); }
.ds-cert-iconbtn--sm { width: 28px; height: 28px; }

/* Arrow navigation buttons — both the PDF page-nav (top of the document
   viewer, `--sm`) and the certificate nav (footer, `--ghost`). Square with
   4px (--radius-4) corners. ENABLED = blue-600 arrow on a blue-50 fill;
   DISABLED (at the first/last page, or first/last certificate) = grey, so the
   reviewer can see at a glance which direction is still available. The grey
   uses a real colour (not just an opacity fade) per the design. */
.ds-cert-iconbtn--sm,
.ds-cert-iconbtn--ghost {
  border-radius: var(--radius-4);
  background: var(--color-blue-50);
  color: var(--color-blue-600);
}
.ds-cert-iconbtn--sm:hover,
.ds-cert-iconbtn--ghost:hover {
  background: var(--color-blue-100);
}
.ds-cert-iconbtn--sm[disabled],
.ds-cert-iconbtn--ghost[disabled] {
  background: var(--color-blackHaze-100);
  color: var(--color-blackHaze-500);
  pointer-events: none;
}
/* Toolbar buttons (zoom / rotate / fullscreen) keep the round dark style. */
.ds-cert-iconbtn[disabled] { opacity: 0.4; pointer-events: none; }

.ds-cert-ico {
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
}
.ds-cert-ico--zoom-in   { -webkit-mask-image: url(/static/icons/fig-zoom-in.svg);    mask-image: url(/static/icons/fig-zoom-in.svg); }
.ds-cert-ico--zoom-out  { -webkit-mask-image: url(/static/icons/fig-zoom-out.svg);   mask-image: url(/static/icons/fig-zoom-out.svg); }
.ds-cert-ico--rotate    { -webkit-mask-image: url(/static/icons/fig-rotate.svg);     mask-image: url(/static/icons/fig-rotate.svg); }
.ds-cert-ico--fullscreen{ -webkit-mask-image: url(/static/icons/fig-fullscreen.svg); mask-image: url(/static/icons/fig-fullscreen.svg); }
.ds-cert-ico--file      { -webkit-mask-image: url(/static/icons/fig-files-01.svg);   mask-image: url(/static/icons/fig-files-01.svg); }
.ds-cert-ico--arrow-up  { -webkit-mask-image: url(/static/icons/arrow-up-01.svg);    mask-image: url(/static/icons/arrow-up-01.svg); width: 16px; height: 16px; }
.ds-cert-ico--chevron-right { -webkit-mask-image: url(/static/icons/chevron-right.svg); mask-image: url(/static/icons/chevron-right.svg); width: 18px; height: 18px; }
.ds-cert-ico--chevron-left  { -webkit-mask-image: url(/static/icons/chevron-left.svg);  mask-image: url(/static/icons/chevron-left.svg);  width: 18px; height: 18px; }

/* ── 3. Data section ──
   Scrollable region: on tall viewports the document viewer (flex-grow:1) takes
   the slack and this sits at its natural height. On short viewports (phone /
   tablet, or a PDF with many fields) the data block shrinks and scrolls
   INTERNALLY instead of pushing the footer — and its decision buttons — off the
   bottom of the fixed-height modal. `min-height:0` lets a flex child shrink
   below its content so `overflow-y:auto` can engage. */
.ds-cert-data {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.ds-cert-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  /* Figma 2705:4993/4994: 16px gap between cells. */
  gap: var(--space-4);
  margin: 0;
}

/* ════════════════════════════════════════════════════════════════════════
   Request-changes mode (.ds-cert-modal.is-rc-mode)
   The reviewer flags certificate fields + picks a reason for each. The data
   grid + normal footer are swapped for a toggle-card panel + rc footer; the
   document viewer above stays visible for comparison. Light theme with the
   app's brightSun "changes" accent. DS tokens, RTL.
   ════════════════════════════════════════════════════════════════════════ */
.ds-cert-modal.is-rc-mode .ds-cert-data,
.ds-cert-modal:not(.is-rc-mode) .ds-cert-rc,
.ds-cert-modal:not(.is-rc-mode) .ds-cert-rc-foot {
  display: none;
}
.ds-cert-modal.is-rc-mode .ds-cert-modal__foot:not(.ds-cert-rc-foot) {
  display: none;
}

/* ── Request-changes entrance choreography ───────────────────────────────────
   Entering request-changes mode reveals a whole new block of content at once;
   building it in instantly reads as abrupt. Instead the freshly-shown content
   eases in IN ORDER, at a DELIBERATE pace the eye can actually follow, so a
   first-time reviewer comprehends the build order:
     1. the header retints (a colour transition below — no opacity flash on the
        persistent header bar) + the chip pops; a 220ms lead lets act 1 read,
     2. the full-width attachment/preview card rises first as an "establishing
        shot" (same keyframe, but a slower 520ms settle — see the --full
        override) so it reads as the top section / act 2's anchor,
     3. each field card follows on a steady, trackable 130ms beat (the eye lands
        on each arriving field rather than seeing one blurred wave),
     4. the footer with its actions settles LAST, detached by --rc-foot-gap.
   The card tween uses a longer-tailed easeOutQuint (0.22,1,0.32,1) so each card
   visibly decelerates and "lands" — that perceptible settle is the comprehension
   cue, with no overshoot (y never exceeds 1, so it stays calm/professional).
   certificate-modal.js stamps each card with its index `--rc-i` and the modal
   with the total `--rc-n`, then adds the transient `.is-rc-entering` class
   (cleared once the sequence ends so it replays on every re-entry). CSS turns
   those indices into staggered animation-delays. Gated behind no-preference, so
   reduced-motion reviewers get the content instantly with no movement.
   Wall-clock ≈ 700ms + N·130ms (≈1.48s for a 6-card modal). */
@media (prefers-reduced-motion: no-preference) {
  .ds-cert-modal.is-rc-entering {
    --rc-cards: 220ms;       /* header retint + chip read before card 0 begins */
    --rc-stagger: 130ms;     /* even, trackable beat between consecutive cards */
    --rc-foot-gap: 120ms;    /* extra gap detaching the footer as the final act */
  }
  .ds-cert-modal.is-rc-entering .ds-cert-rc-card {
    animation: dsRcCardIn 420ms cubic-bezier(0.22, 1, 0.32, 1)
               calc(var(--rc-cards) + var(--rc-i, 0) * var(--rc-stagger)) both;
  }
  /* The full-width attachment/preview card is the "establishing shot": it keeps
     the same dsRcCardIn keyframe and its i=0 (220ms) delay, but settles slower
     and calmer so it reads as the top section before the field cadence. This
     override MUST follow the base card rule — equal specificity (both 3 classes)
     means source order wins, so these longhands override the shorthand's
     duration + timing-function while inheriting its delay, keyframe and fill. */
  .ds-cert-modal.is-rc-entering .ds-cert-rc-card--full {
    animation-duration: 520ms;
    animation-timing-function: cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .ds-cert-modal.is-rc-entering .ds-cert-rc-foot {
    animation: dsRcFootIn 360ms cubic-bezier(0.22, 1, 0.32, 1)
               calc(var(--rc-cards) + var(--rc-n, 0) * var(--rc-stagger) + var(--rc-foot-gap)) both;
  }
  /* the brightSun chip pops in with the retint (no layout shift — it is the
     only genuinely new element in the otherwise-persistent header). */
  .ds-cert-modal.is-rc-entering .ds-cert-modal__rc-chip {
    animation: dsRcChipIn 320ms cubic-bezier(0.16, 1, 0.3, 1) 40ms both;
  }
}
@keyframes dsRcCardIn {
  from { opacity: 0; transform: translateY(22px) scale(0.975); }
  to   { opacity: 1; transform: none; }
}
@keyframes dsRcFootIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
@keyframes dsRcChipIn {
  from { opacity: 0; transform: scale(0); }
  to   { opacity: 1; transform: scale(1); }
}
/* Smooth the header retint when toggling request-changes mode. The header bar
   is persistent (it never leaves the screen), so it must NOT fade — only its
   fill + title colours ease over. Extends the titlebox's existing transform
   transition (cert-to-cert swap) with background-color; the title spans get a
   matching colour transition. */
.ds-cert-modal__titlebox {
  transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1),
              background-color 280ms cubic-bezier(0.16, 1, 0.3, 1);
}
.ds-cert-modal__title-main,
.ds-cert-modal__title-pre {
  transition: color 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

.ds-cert-rc {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Two-column field grid (Figma 2716:5102); collapses to one column on phones. */
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* stretch so both cards in a row share the same (tallest) height — keeps the
     grid even when one card is expanded for its reason picker. */
  align-items: stretch;
  gap: var(--space-5);
}
@media (max-width: 640px) {
  .ds-cert-rc { grid-template-columns: 1fr; }
}

/* One field card (Figma 2742:1095 enabled / 2743:1033 disabled): a 2px frame —
   neutral blackHaze when off, brightSun-600 when on — wrapping a tinted header
   (square toggle on the trailing/left edge, label+value on the reading/right
   edge) and a 40px body row (off → "لم يفعل الحقل"; on → reason picker, then the
   chosen reason with a pencil-edit affordance). */
.ds-cert-rc-card {
  border: var(--stroke-1) solid var(--color-blackHaze-100);
  border-radius: var(--radius-4);
  background: transparent;
  /* 2px frame (Figma spacing/1 = 2px). NOTE: --space-1 is 4px (the project's
     --space-N is offset one step from Figma's spacing/N — see the
     figma-spacing-scale-offset note), so the 2px frame uses an explicit
     0.125rem, NOT --space-1, which rendered a too-thick 4px frame. */
  padding: 0.125rem;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  /* Compact, content-driven height: a fixed 92px header + a 40px reason/off row.
     The reason picker is now a separate dialog (the card never expands inline),
     so every field card is naturally the same height — no fixed height needed. */
  transition: border-color 0.15s ease;
}
.ds-cert-rc-card.is-on {
  border-color: var(--color-brightSun-600);
}
/* The certificate-file (attachment) card leads the list and spans both columns.
   Unlike the fixed-height field cards it grows to fit the embedded document
   viewer (Figma 2761:1110), so its height is content-driven. */
.ds-cert-rc-card--full {
  grid-column: 1 / -1;
  height: auto;
  /* Override the base `overflow: hidden` (which the fixed-height field cards
     need for their scrollable picker). With it hidden, this card's min-size is
     0 and the grid collapses its row, clipping the viewer; visible lets the
     content (header + viewer + body) size the row. */
  overflow: visible;
}
/* Slot that hosts the reparented .ds-cert-viewer between the header and the
   reason body. The viewer brings its own black frame + min-height. */
.ds-cert-rc-card__preview {
  display: flex;
  flex-direction: column;
  width: 100%;
  box-sizing: border-box;
  /* 16px frame around the viewer (Figma 2764:1123) so the black preview box is
     inset from the card edges rather than full-bleed. */
  padding: var(--space-4);
  /* Reserve a DEFINITE height (viewer 320px + 2×16px padding) so the grid sizes
     the attachment-card row correctly — the viewer's content is absolutely
     positioned, so without this the row would collapse and clip everything. */
  min-height: 352px;
}
.ds-cert-rc-card__preview .ds-cert-viewer {
  flex: 1 1 auto;
  width: 100%;
}
.ds-cert-rc-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* FIXED 59px head (Figma 2817:1784 — the card is 103px = 59 head + 40 body +
     4 frame). 16px horizontal inset; vertical inset is 0 because the panel
     dropped `text-box-trim` (cross-browser parity), so the browser's natural
     line-height makes label+value taller than Figma's trimmed 27px — centring
     it (align-items: center) in the fixed 59px keeps the row at EXACTLY 59px
     instead of letting padding push it to ~92px. */
  height: 59px;
  padding: 0 var(--space-4);
  box-sizing: border-box;
  flex-shrink: 0;                          /* never compress when the body grows */
  border-radius: var(--radius-2);
  background: var(--color-blackHaze-50);
  transition: background-color 0.15s ease;
}
.ds-cert-rc-card.is-on .ds-cert-rc-card__head {
  background: var(--color-brightSun-100);
}
.ds-cert-rc-card__cell {
  display: flex;
  flex-direction: column;
  /* 4px label → value. The panel dropped `text-box-trim`, so each line already
     carries its full line-height; a 4px gap + that line-height reads like the
     Figma's trimmed 12px spacing without the cells growing too tall. */
  gap: var(--space-1);
  min-width: 0;
  text-align: right;
}
.ds-cert-rc-card__label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  /* line-height 1.2 — matches the .ds-field-cell («معتمد») label so the
     label↔value spacing distribution is identical (the 4px cell gap was already
     the same; the default line-height was over-spacing the two lines). */
  line-height: 1.2;
  opacity: 0.7;
  color: var(--color-blackHaze-950);
}
.ds-cert-rc-card__value {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  line-height: 1.2;
  color: var(--color-blackHaze-1000);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ds-cert-rc-card.is-on .ds-cert-rc-card__label { color: var(--color-brightSun-950); }
.ds-cert-rc-card.is-on .ds-cert-rc-card__value { color: var(--color-brightSun-1000); }

/* Square segmented toggle (Figma 2817:1819). A 46×26 track with a 20px
   rounded-square knob: off → brightSun-50 track + brightSun-600 border + pale
   brightSun-200 knob parked at the START (right in RTL, 3px from the edge); on →
   brightSun-600 track + brightSun-50 knob slid to the END (left). */
.ds-cert-rc-toggle {
  position: relative;
  flex-shrink: 0;
  box-sizing: border-box;
  width: 46px;                             /* Figma 2817:1785 (46×26, 3px pad) */
  height: 26px;
  padding: 0;
  border: var(--stroke-1) solid var(--color-brightSun-600);
  border-radius: var(--radius-4);
  background: var(--color-brightSun-50);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.ds-cert-rc-toggle__knob {
  position: absolute;
  /* Figma 2817:1819 parks the 20px knob 3px from the toggle's OUTER edge. With
     box-sizing: border-box the 1px border already eats 1px, so the offset from
     the padding box is 2px (2px + 1px border = the 3px visual gap). */
  top: 2px;
  inset-inline-start: 2px;                 /* off → start (right in RTL) */
  width: 20px;
  height: 20px;
  border-radius: var(--radius-2);
  background: var(--color-brightSun-200);
  transition: inset-inline-start 0.15s ease, background-color 0.15s ease;
}
.ds-cert-rc-card.is-on .ds-cert-rc-toggle {
  background: var(--color-brightSun-600);
  border-color: var(--color-brightSun-600);
}
.ds-cert-rc-card.is-on .ds-cert-rc-toggle__knob {
  inset-inline-start: calc(100% - 22px);   /* on → slides to end (left), 2px gap */
  background: var(--color-brightSun-50);
}

/* Card body — fills the remaining card height (so sibling cards stay level) and
   vertically centres its single visible child (off note / chosen row / picker). */
.ds-cert-rc-card__body {
  display: flex;
  flex-direction: column;
  padding: 0 var(--space-4);    /* 16px horizontal — matches the reason box (Figma 2761:1065) */
}
.ds-cert-rc-card__off {
  display: flex;
  align-items: center;
  justify-content: flex-start;   /* start = far right in RTL */
  height: 40px;                  /* fixed so every collapsed card matches */
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--color-blackHaze-300);
  text-align: right;
}
.ds-cert-rc-card.is-on .ds-cert-rc-card__off { display: none; }

/* ── A request ALREADY WITH THE DOCTOR ─────────────────────────────────────
   NO special card. It renders exactly as the flagged card it is — same tint,
   same chosen-reason row — and only its two ACTS are locked (buildRcCard
   disables the toggle and the reason row). A bespoke block was tried here and
   removed: a card that looks different for a reason the reviewer cannot act on
   just makes the panel noisier, and the whole point is that this IS a flagged
   field, merely not theirs to change any more. All this needs is that a locked
   control LOOK locked. */
.ds-cert-rc-toggle[disabled],
.ds-cert-rc-card__chosen[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;              /* the tip lives on the enabled host */
}

/* Collapsed chosen reason: reason text on the reading (right) edge, pencil-edit
   on the trailing (left) edge. The whole row re-opens the picker. */
.ds-cert-rc-card__chosen {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  height: 40px;                  /* fixed so every collapsed card matches */
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: right;
}
.ds-cert-rc-card.is-on:not(.is-picking) .ds-cert-rc-card__chosen { display: flex; }
.ds-cert-rc-card__chosen-text {
  flex: 1;
  min-width: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--color-brightSun-600);
  opacity: 0.7;                 /* Figma 2761:1069 */
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-cert-rc-card__chosen-ico {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/fig-pencil-edit-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-pencil-edit-02.svg) center / contain no-repeat;
}

/* Reasons picker — revealed when the card is on AND being edited (Figma:
   "اختر سبب التعديل من الأسباب التي تظهر"). After a preset reason is picked the
   card collapses to .ds-cert-rc-card__chosen above; the pencil re-opens it. */
.ds-cert-rc-card__reasons {
  display: none;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  overflow-y: auto;             /* scroll inside the fixed card height */
  overscroll-behavior: contain;
}
.ds-cert-rc-card.is-on.is-picking .ds-cert-rc-card__reasons { display: flex; }
/* Reason option (Figma 2775:1044 default / 2761:1058 selected). A blue-50 fill
   tile: a square check box on the reading (right) edge with the reason text to
   its left. The radio is DOM-first, so plain `row` lands it on the RTL start
   (right); the text (flex:1, right-aligned) fills the space to its left.
   Selected → bold blue text, a filled box, and a blue-600 ring set 2px off the
   fill (the gap shows the dialog background). */
.ds-cert-rc-reason {
  display: flex;
  flex-direction: row;                   /* RTL: DOM-first radio → right edge, text → its left */
  align-items: center;
  gap: var(--space-3);                   /* 12px (Figma spacing/4) */
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-4);               /* 16px (Figma spacing/5) */
  border: 0;
  border-radius: var(--radius-4);
  background: var(--color-blue-50);
  cursor: pointer;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--color-blue-900);
  text-align: right;
}
/* Selected ring: 2px gap (dialog bg) then a 1px blue-600 line, drawn as a double
   box-shadow so it adds no layout and follows the radius. The "+ other" row's
   ring comes from its wrapper instead (it must also enclose the textarea). */
.ds-cert-rc-reason.is-selected:not(.ds-cert-rc-reason--other) {
  box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 3px var(--color-blue-600);
}
.ds-cert-rc-reason.is-selected .ds-cert-rc-reason__text {
  font-family: var(--font-activist-bold);
  color: var(--color-blue-600);
}
/* Square check box (Figma 2761:1059): a 24px blue-100 holder whose 2px border
   stays blue-100 while its padding-box background fills blue-600 when checked —
   yielding a 20px inner square (radius 2px) without a pseudo-element (which a
   replaced <input> would not render). */
.ds-cert-rc-reason__radio {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: var(--stroke-2) solid var(--color-blue-100);
  border-radius: var(--radius-4);
  background-color: var(--color-blue-100);
  background-clip: padding-box;
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.ds-cert-rc-reason__radio:checked {
  background-color: var(--color-blue-600);
}
.ds-cert-rc-reason__text { flex: 1; min-width: 0; }

/* "+ other" + its free-text field share one bordered frame (Figma 2776:1158):
   the wrapper paints the selection ring around BOTH when the other radio is on. */
.ds-cert-rc-otherwrap {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  border-radius: var(--radius-4);
}
/* Same ring as a selected preset, but around the option + textarea together.
   box-shadow adds no layout, so the unselected row stays aligned with the
   presets and there is no shift when "other" is picked. */
.ds-cert-rc-otherwrap:has(.ds-cert-rc-reason--other.is-selected) {
  box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 3px var(--color-blue-600);
}
/* Free-text field (Figma 2776:1164): a blackHaze-50 input. The caret colour is
   owned by the faux-caret component below (transparent while empty, blue once it
   holds text) so the faux/real carets never both show. */
.ds-cert-rc-other {
  min-height: 48px;
  padding: var(--space-4);
  border: 0;
  border-radius: var(--radius-4);
  background: var(--color-blackHaze-50);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--color-blackHaze-800);   /* typed value text (Figma 2784:1118 #394353) */
  resize: vertical;
  text-align: right;
}
.ds-cert-rc-other:focus { outline: none; }
.ds-cert-rc-other::placeholder { color: var(--text-placeholder); }

/* ── Faux caret (reusable component, Figma 2777:1182) ──
   A blinking 2px×16px blue bar parked at a text field's reading start (right in
   RTL) WHENEVER the field is empty — a "type here" affordance that stays even on
   focus, so it reappears the moment the value is cleared. While empty the real
   caret is suppressed (transparent) so the two never overlap; once the field
   holds text the faux caret hides and the real blue caret takes over.

   Markup:
     <div class="ds-caret-field">
       <textarea|input class="ds-caret-field__input …"></…>
       <span class="ds-faux-caret" aria-hidden="true"></span>
     </div>
   JS (panel.js) toggles `.has-text` on the wrapper from the input's value.
   Per-instance overrides: --ds-caret-top / --ds-caret-inset (default 16px). */
.ds-caret-field { position: relative; width: 100%; }
.ds-caret-field__input { width: 100%; box-sizing: border-box; margin: 0; }
.ds-faux-caret {
  position: absolute;
  top: var(--ds-caret-top, var(--space-4));            /* 16px — first line */
  inset-inline-start: var(--ds-caret-inset, var(--space-4)); /* 16px from reading start */
  width: var(--stroke-2);                              /* 2px */
  height: var(--type-ar-16);                           /* 16px */
  background: var(--color-blue-600);
  pointer-events: none;
  animation: ds-caret-blink 1.06s step-end infinite;
}
/* Faux caret hidden once the field has text, or while the input is hidden. */
.ds-caret-field.has-text .ds-faux-caret,
.ds-caret-field__input[hidden] ~ .ds-faux-caret { display: none; }
/* Real caret: transparent while empty (faux shows instead), blue once typing. */
.ds-caret-field:not(.has-text) .ds-caret-field__input { caret-color: transparent; }
.ds-caret-field.has-text .ds-caret-field__input { caret-color: var(--color-blue-600); }
@keyframes ds-caret-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ds-faux-caret { animation: none; } }

/* ── Field reason picker (reuses the .ds-decision-dialog shell + ds-btn) ──
   New chrome (Figma 2761:1026): a cream field chip (label over value) on the
   reading edge of the header, a cerulean info note, then the blue reason list.
   The dialog drops the coloured top accent stripe — a plain white card. */
[data-reason-picker] .ds-decision-dialog__inner { border-block-start: 0; }
[data-reason-picker] .ds-decision-dialog__footer { background: var(--bg-card); flex-wrap: nowrap; }
/* "Save reason" grows to fill the footer's free space at every width; "Cancel"
   keeps hugging its content. The inner pill is stretched too (it is width:auto
   by default), so the visible blue button — not just the hit area — fills. */
[data-reason-picker] [data-reason-picker-save] { flex: 1 1 auto; min-width: 0; }
[data-reason-picker] [data-reason-picker-save] .ds-btn__inner { width: 100%; }
[data-reason-picker] [data-reason-picker-cancel] { flex: 0 0 auto; }

/* Header field chip (Figma 2774:1027): the flagged field's label over its entered
   value on a cream brightSun-100 tile. */
.ds-reason-picker__fieldchip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;     /* RTL cross-start = right → label/value hug the right edge */
  gap: var(--space-2);         /* tight label↔value gap (Figma's trimmed 12px ≈ 8px untrimmed) */
  width: 100%;                 /* fills the header (Figma 2774:1027 size-full) */
  box-sizing: border-box;
  padding: var(--space-4);
  background: var(--color-brightSun-100);
  border-radius: var(--radius-2);
  text-align: right;
}
.ds-reason-picker__fieldchip-label {
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-16);
  line-height: 1;              /* trim leading so the two lines sit tight, as in Figma */
  color: var(--color-brightSun-950);
  opacity: 0.7;
}
.ds-reason-picker__fieldchip-value {
  max-width: 100%;
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  line-height: 1;
  color: var(--color-brightSun-1000);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Info note (Figma 2761:1037): cerulean banner — info glyph + text on the reading
   edge, a dismiss X opposite. */
.ds-reason-picker__note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-cerulean-50);
  border-radius: var(--radius-2);
}
.ds-reason-picker__note[hidden] { display: none; }
.ds-reason-picker__note-main {
  display: inline-flex;
  align-items: center;       /* DOM [icon][text] → icon on reading (right) edge */
  gap: var(--space-2);
  min-width: 0;
}
.ds-reason-picker__note-ico {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  background-color: var(--color-cerulean-500);
  -webkit-mask: url(/static/icons/information-square.svg) center / contain no-repeat;
  mask: url(/static/icons/information-square.svg) center / contain no-repeat;
}
.ds-reason-picker__note-text {
  margin: 0;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  color: var(--color-cerulean-950);
  text-align: right;
}
.ds-reason-picker__note-dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.ds-reason-picker__note-dismiss-ico {
  width: 20px;
  height: 20px;
  background-color: var(--color-cerulean-950);
  opacity: 0.45;
  -webkit-mask: url(/static/icons/cancel-01.svg) center / contain no-repeat;
  mask: url(/static/icons/cancel-01.svg) center / contain no-repeat;
  transition: opacity 120ms ease;
}
.ds-reason-picker__note-dismiss:hover .ds-reason-picker__note-dismiss-ico { opacity: 0.75; }

/* Search field (Figma 2787:1248): a full-width blackHaze-50 box — search glyph
   on the reading (right) edge, input filling to its left. Same font (Activist
   Regular 16px) and 16px padding as the header chip (Figma 2780:1034). */
.ds-reason-picker__search {
  display: flex;
  flex-direction: row;        /* RTL: DOM-first icon → right edge, input fills its left */
  align-items: center;
  gap: var(--space-2);        /* 8px (Figma spacing/3) */
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-4);    /* 16px — matches the header chip */
  background: var(--color-blackHaze-50);
  border-radius: var(--radius-4);
  /* Faux-caret component overrides: park the caret at the input's text-start
     (past the 24px search glyph) and centre it in the 56px field. */
  --ds-caret-inset: calc(var(--space-4) + var(--space-6) + var(--space-2)); /* 16 + 24 + 8 = 48px */
  --ds-caret-top: var(--space-5);                                           /* 20px */
}
.ds-reason-picker__search-ico {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  background-color: var(--color-blackHaze-500);
  -webkit-mask: url(/static/icons/search-01.svg) center / contain no-repeat;
  mask: url(/static/icons/search-01.svg) center / contain no-repeat;
}
.ds-reason-picker__search-input {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-16);
  color: var(--color-blackHaze-800);
  text-align: right;
}
.ds-reason-picker__search-input:focus { outline: none; }
.ds-reason-picker__search-input::placeholder { color: var(--color-blackHaze-800); opacity: 0.8; }
/* Placeholder is the at-rest hint only — on focus it yields to the live caret so
   the two never show together (the caret appears solely when the field is focused). */
.ds-reason-picker__search-input:focus::placeholder { color: transparent; }
/* Clear button (Figma 2790:1064): the canonical close component on the trailing
   (left) edge, shown only when the search holds a value. Sized to 24px so it
   matches the search glyph and keeps the field at 56px (no jump on type). */
.ds-reason-picker__search .ds-close-btn { width: 24px; height: 24px; flex-shrink: 0; }
.ds-reason-picker__search:not(.has-text) .ds-close-btn { display: none; }
/* The search caret is a focus affordance, not a persistent hint: it shows ONLY
   while the search input is focused (per user preference) — at rest the search
   shows no caret. And while the search IS focused it owns the single live caret,
   so the "write-your-own" field's persistent caret yields to it. Together these
   guarantee at most one faux caret blinks at a time. */
.ds-reason-picker__search-input:not(:focus) ~ .ds-faux-caret { display: none; }
[data-reason-picker]:has(.ds-reason-picker__search-input:focus) .ds-cert-rc-otherwrap .ds-faux-caret {
  display: none;
}
/* A preset reason hidden by the search filter. */
.ds-cert-rc-reason.is-filtered-out,
.ds-cert-rc-otherwrap.is-filtered-out { display: none; }

/* The reparented reasons block is a fixed-height scroller hidden inside the card;
   in the picker it renders as a normal full list (12px gaps, rings not clipped). */
.ds-reason-picker__slot .ds-cert-rc-card__reasons {
  display: flex !important;
  flex: 0 1 auto;
  min-height: 0;
  gap: var(--space-3);
  overflow: visible;
  padding: 0;
}

/* rc footer — counter on the reading edge, cancel + confirm opposite. */
.ds-cert-rc-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-shrink: 0;
}
.ds-cert-rc-foot__count {
  margin: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: color-mix(in srgb, var(--text-onCard-primary) 60%, transparent);
}
/* Submit-failure note shown in place of the counter (certificate-modal.js
   showRcError) — must read as an error, not a status. */
.ds-cert-rc-foot__count.is-error {
  color: var(--color-radicalRed-600);
}
.ds-cert-rc-foot__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}
/* (Removed 2026-06-05) The `.ds-cert-rc-btn*` one-off confirm/cancel buttons
   were DEAD CSS — no markup or JS referenced them. The cert request-changes
   footer (_certificate_review_modal.html) uses the canonical `.ds-btn--warning`
   (confirm) + `.ds-btn--secondary` (cancel), so the orange look now comes from
   the single `.ds-btn--warning` source like everywhere else. */

/* ── STEP DECISION FOOTER — ONE geometry for both shapes ───────────────────
   A step's decision row comes in two shapes: `.ds-step-actions` (the scalar
   bar) and `.ds-qual-foot` (the repeatable step's dynamic footer, one button
   driven by the per-instance verdicts). They are the SAME row to a reviewer, so
   they are laid out here TOGETHER — one rule, one place. Keeping the scalar
   one's geometry in an inline `style=` (as it was until 2026-07-26) is exactly
   how the two drifted apart: same button, two sizes and two positions on one
   page.

   THE ARRANGEMENT (Mohammed, 2026-07-26): actions grouped at the TRAILING edge
   (visual left in RTL), the hint on the READING edge. Never `space-between`:
   that makes the position depend on whether a hint happens to render, so the
   lone «تعديل القرار» of a SETTLED step slid to the opposite edge from the
   «تأكيد الاعتماد» it replaced — the button moved when the state changed. The
   auto margin pins the hint instead, so an absent hint moves nothing. */
.ds-step-actions,
.ds-qual-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-4);
}
/* Takes the reading edge and pushes every action away from it. Direction-
   agnostic: `inline-end` is the left in RTL and the right in LTR. */
.ds-qual-foot__hint {
  margin: 0;
  margin-inline-end: auto;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: color-mix(in srgb, var(--text-onCard-primary) 65%, transparent);
}
.ds-step-actions form,
.ds-qual-foot form { margin: 0; }
@media (max-width: 640px) {
  .ds-step-actions,
  .ds-qual-foot { flex-direction: column; align-items: stretch; }
  /* `.ds-tip-host` is in the list because a lone DISABLED button is wrapped in
     one to carry its reason — without it that button alone stays narrow. */
  .ds-step-actions > .ds-btn,
  .ds-step-actions > .ds-tip-host,
  .ds-step-actions form,
  .ds-step-actions form .ds-btn,
  .ds-step-actions > .ds-tip-host > .ds-btn,
  .ds-qual-foot > .ds-btn,
  .ds-qual-foot > .ds-tip-host,
  .ds-qual-foot form,
  .ds-qual-foot form .ds-btn,
  .ds-qual-foot > .ds-tip-host > .ds-btn { width: 100%; }
}
/* ── Per-field verdict buttons (reusable component: ds_verdict_buttons) ──
   The ✓ approve / ⚠ request-changes square pair a reviewer uses to decide one
   field (Figma 2834:4613 rest / 2833:4571 + inside 2834:4619 hover). Lives
   inside a DEFAULT ds-field-cell row (2817:1693) but is its own component so
   the step panels can reuse it.
   Rest: 35px outline square — 1.5px tinted border, radius 4, 16px outline
   glyph in the same tint (fig-check / fig-alert-01, the 16px Figma variants
   are the existing 20-viewBox assets ×0.8 exactly).
   Hover/focus: the border stays; a 28px tinted fill (2px inset, radius 2)
   pops in behind the glyph, which flips to white.
   Source order is flag → approve so in RTL the ⚠ lands on the right and the
   ✓ on the far left (same convention as .ds-decision-btns). */
.ds-verdict-btns {
  display: flex;
  align-items: center;
  gap: var(--space-3);              /* 12px (Figma 2833:1173) */
}
.ds-verdict-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.1875rem;           /* 35px (Figma 2833:4571) */
  block-size: 2.1875rem;
  padding: 0;
  border: var(--stroke-1_5) solid;
  border-radius: var(--radius-4);
  background: transparent;
  cursor: pointer;
}
/* «لا قرار على مستندٍ لم يرفعه الطبيب» — a disabled verdict pair reads
   muted and inert (no hover fill; the title explains why). */
.ds-verdict-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.ds-verdict-btn:disabled::before {
  display: none;
}
/* The hover fill: inset 2px (Spacing/1) inside the border → a 28px square. */
.ds-verdict-btn::before {
  content: "";
  position: absolute;
  inset: var(--space-0_5);
  border-radius: var(--radius-2);
  background: transparent;
}
.ds-verdict-btn__icon {
  position: relative;               /* paints above the ::before fill */
  inline-size: 1rem;                /* 16px glyph */
  block-size: 1rem;
  flex-shrink: 0;
}
.ds-verdict-btn--approve { border-color: var(--color-green-600); }
.ds-verdict-btn--approve .ds-verdict-btn__icon {
  background-color: var(--color-green-600);
  -webkit-mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
}
.ds-verdict-btn--approve:hover::before,
.ds-verdict-btn--approve:focus-visible::before { background: var(--color-green-600); }
.ds-verdict-btn--flag { border-color: var(--color-brightSun-600); }
.ds-verdict-btn--flag .ds-verdict-btn__icon {
  background-color: var(--color-brightSun-600);
  -webkit-mask: url(/static/icons/fig-alert-01.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-01.svg) center / contain no-repeat;
}
.ds-verdict-btn--flag:hover::before,
.ds-verdict-btn--flag:focus-visible::before { background: var(--color-brightSun-600); }
/* Reject (Figma 2851:1055): the negative verdict on a RESUBMITTED (gold) cell —
   a radicalRed-600 outline square with a cancel-01 (✕) glyph, replacing the ⚠
   request-changes button (the doctor already had a correction round). Rejecting
   a resubmitted field rejects the whole APPLICATION (the reviewer confirms via
   the dedicated reject pop). Same hover treatment as the other verdict btns. */
.ds-verdict-btn--reject { border-color: var(--color-radicalRed-600); }
.ds-verdict-btn--reject .ds-verdict-btn__icon {
  background-color: var(--color-radicalRed-600);
  -webkit-mask: url(/static/icons/fig-cancel-01.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-cancel-01.svg) center / contain no-repeat;
}
.ds-verdict-btn--reject:hover::before,
.ds-verdict-btn--reject:focus-visible::before { background: var(--color-radicalRed-600); }
.ds-verdict-btn:hover .ds-verdict-btn__icon,
.ds-verdict-btn:focus-visible .ds-verdict-btn__icon {
  background-color: var(--color-white-50);
}

/* ── Data field cell (reusable component: ds_field_cell macro) ──
   A labelled value tile with three states (Figma 2817:1693 / :1702 / :1712,
   updated 2026-06-11):
     • default  — the grey label/value row with the ✓/⚠ verdict buttons on the
                  far LEFT (RTL end); value ink blackHaze-950.
     • approved — value green-950 + a green status footer ("معتمد" + check)
                  under the row, wrapped in a blackHaze-50 frame (2px inset).
     • flagged  — change requested: a gold frame (2px inset), the value
                  recoloured brightSun-950, and a brightSun footer carrying
                  the reason + alert glyph.
   Decided footers carry a blue pencil-edit button on the far LEFT
   (.ds-field-cell__edit) that re-opens the per-field decision.
   SINGLE source of truth: the `ds_field_cell` macro (server) and
   certificate-modal.js renderGrid (client) emit these exact classes, so one
   edit here updates every consumer.
   RTL: the label/value BODY hugs the RIGHT edge of the row; the verdict
   buttons and the footer pencil land on the far LEFT (`space-between` /
   margin-inline-start:auto, emitted AFTER the body / status content). */
.ds-field-cell {
  display: flex;
  flex-direction: column;
  /* Default has no visible frame; the approved/flagged modifiers paint the
     border and the 2px (Spacing/1) inset shows it around the row + footer. */
  border: var(--stroke-1) solid transparent;
  border-radius: var(--radius-0);
}
/* The grey field row: label + value (+ the verdict buttons on the left in the
   default state). `space-between` parks the body right (RTL) and the actions
   far left; with the body alone it degrades to flex-start — identical to the
   decided states' Figma layout. */
.ds-field-cell__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-blackHaze-50);
  /* Canonical row height = 67px in EVERY state (Figma: approved 2817:1703,
     default 2817:1693, resubmitted 2851:1050 — all 67px). The action states
     (default/resubmitted) reach 67px naturally: 35px verdict buttons + 16px
     padding. The text-only decided states (approved/flagged) have no buttons,
     so this floor lifts them to the same 67px and the grid stays uniform.
     min-block-size (a FLOOR, not a hard height) preserves that uniformity while
     still letting content grow the row instead of clipping. */
  min-block-size: 4.1875rem;   /* 67px */
}
/* Verdict-buttons slot inside the default row (a .ds-verdict-btns group). */
.ds-field-cell__actions { flex-shrink: 0; }
.ds-field-cell__body {
  display: flex;
  flex-direction: column;
  /* Label↔value gap = 4px (user 2026-06-13, was 12px) — applies to every
     field-cell state: default, approved, flagged, resubmitted. */
  gap: var(--space-1);
  /* RTL cross-start = right: right-align both the label and the value (a column
     `flex-end` would resolve to the LEFT in RTL, leaving the short label off the
     right edge). `text-align:right` belts-and-braces for any wrapped lines. */
  align-items: flex-start;
  text-align: right;
  min-width: 0;
}
.ds-field-cell__label {
  font-family: var(--font-activist-semibold);
  /* Figma label = 14px. */
  font-size: var(--type-ar-14);
  line-height: 1.2;
  /* Spacing via line-height only — NOT text-box-trim (Chromium/Safari-only, so
     it rendered differently in Firefox). The row's 67px min-block-size floor
     holds the height regardless, so this only relaxes the label↔value gap a few
     px and now reads identically in every browser. */
  color: var(--color-blackHaze-950);
  opacity: 0.7;
}
/* «اختياري» — small muted tag INSIDE the label line of an optional cell
   (decidable, never gate-counted). Plain inline text — no pill background —
   so the label's line box and the cell's fixed row height stay intact.
   Emitted by ds_field_cell (optional=true) and renderGrid (f.required===false)
   alike. */
.ds-field-cell__optional {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-onCard-secondary);
  margin-inline-start: var(--space-1);
  white-space: nowrap;
}
/* ═══ Empty state (canonical component — ds_empty_state in _ds.html) ═══
   A flat, centered, dashed box for "nothing here yet" areas: quiet fill,
   hairline dashed frame (the universal empty-state affordance), copy dead-
   centre. Flat by design-system rule — no invented shadows. */
.ds-empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Stands in for a document row, so it stands the SAME height — one named
     size for both (Mohammed, 2026-07-24). Was 7.5rem, which towered over the
     cards it replaces. */
  min-block-size: var(--ds-doc-row-block-size);
  padding: var(--space-5);
  background: var(--color-blackHaze-50);
  border: var(--stroke-1) dashed var(--border-divider);
  border-radius: var(--radius-8);
  color: var(--text-onCard-secondary);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  line-height: 1.6;
}
.ds-field-cell__value {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
  line-height: 1.2;
  /* Figma 2817:1693 (2026-06-11 update): DEFAULT ink = blackHaze-950 (was
     green-950); approved keeps green-950, flagged recolours brightSun-950 —
     see the state modifiers below. */
  color: var(--color-blackHaze-950);
}

/* In-place verdict-change settle: a brief fade after the inline JS swaps a
   cell's state (approve / withdraw) without a page reload — gentle feedback,
   no layout shift. (respects reduced motion below via the global guard.) */
@keyframes ds-cell-settle {
  from { opacity: 0.5; }
  to   { opacity: 1; }
}
.ds-field-cell--settle { animation: ds-cell-settle 300ms ease-out; }

/* Status footer, shared by approved + flagged (Figma 2817:1707 / :1732): a
   full-width strip under the row; content hugs the RIGHT edge in RTL. */
.ds-field-cell__status {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);                      /* 8px (Spacing/3) */
  padding: var(--space-2) var(--space-4);   /* 8px / 16px */
  font-size: var(--type-ar-14);
  /* ONE height, whatever the reason says. A change-request reason is authored
     text and can be any length, so the footer used to grow to two lines (67px
     against the 36px of «معتمد نهائيّاً») and cells in the same grid stopped
     lining up. The strip is chrome — icon, reason, pencil — and chrome does not
     resize with its content; the reason is read in full from the tooltip, and
     in the request-changes pop where it is edited. */
  block-size: 2.25rem;                      /* 36px */
  overflow: hidden;
}
.ds-field-cell__status-text {
  min-width: 0;
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-field-cell__status-icon {
  inline-size: 1.25rem;   /* 20px */
  block-size: 1.25rem;
  flex-shrink: 0;
}
/* Has the DOCTOR answered this change request? A very small marker at the far
   end of the status strip, beside the locked pencil. It inherits the palette of
   the waiting box it replaces (2026-07-28) — the box was a second list of the
   step's own cells printed underneath them, so the fact moved onto the cell the
   request is about.
   `flex: 0 0 auto` + `nowrap` keep it out of the reason's ellipsis, and nothing
   here may grow the strip past its fixed 36px. */
.ds-field-cell__reply {
  flex: 0 0 auto;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  line-height: 1.4;
  padding: 2px var(--space-2);
  border-radius: var(--radius-4);
  white-space: nowrap;
  border: var(--stroke-1) solid transparent;
  cursor: default;
}
.ds-field-cell__reply--waiting {
  color: var(--feedback-warning-message);
  background: var(--feedback-warning-bg);
  border-color: var(--feedback-warning-border);
}
.ds-field-cell__reply--responded {
  color: var(--feedback-success-message);
  background: var(--color-blackHaze-0, #fff);
  border-color: var(--feedback-success-border);
}
.ds-field-cell__reply--compare {
  color: var(--feedback-info-message);
  background: var(--feedback-info-bg);
  border-color: var(--feedback-info-border);
}
/* Verdict-edit pencil (Figma 2835:4641 / :4645, 2026-06-11): a 20px blue-600
   pencil-edit-02 glyph on the far LEFT of the status strip (RTL far end —
   margin-inline-start:auto, emitted AFTER the icon + text) that re-opens the
   per-field decision. */
.ds-field-cell__edit {
  margin-inline-start: auto;
  inline-size: 1.25rem;   /* 20px */
  block-size: 1.25rem;
  flex-shrink: 0;
  padding: 0;
  border: 0;
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/fig-pencil-edit-02.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-pencil-edit-02.svg) center / contain no-repeat;
  cursor: pointer;
}

/* ── Approved (Figma 2817:1702): blackHaze-50 frame (2px inset) + green-100
   footer with a green-600 "معتمد" label and a green check on the far-right
   edge (icon emitted first → rtl `flex-start` parks it rightmost); the value
   keeps its green-950 ink. */
.ds-field-cell--approved {
  border-color: var(--color-blackHaze-50);
  /* 2px (Spacing/1) inset between the frame and the row + footer (Figma
     p-2px on the decided wrappers; the default cell has none). */
  padding: var(--space-0_5);
}
.ds-field-cell--approved .ds-field-cell__value { color: var(--color-green-950); }
.ds-field-cell--approved .ds-field-cell__status {
  background: var(--color-green-100);
  color: var(--color-green-600);
  font-family: var(--font-activist-bold);
}
.ds-field-cell--approved .ds-field-cell__status-icon {
  background-color: var(--color-green-600);
  -webkit-mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-check.svg) center / contain no-repeat;
}

/* ── Flagged / change-requested (Figma 2817:1712): gold-600 frame, the value
   recoloured brightSun-950, and a PALE brightSun-50 footer (fixed 40px) carrying
   the reason in brightSun-600 + an alert glyph on the far-right edge (updated
   2817:1712 — the alert moved out of the row into the footer, so the flagged
   footer now mirrors the approved one: icon emitted FIRST → rtl `flex-start`
   parks it rightmost). */
.ds-field-cell--flagged {
  border-color: var(--color-gold-600);
  padding: var(--space-0_5);   /* same 2px decided-frame inset as approved */
}
.ds-field-cell--flagged .ds-field-cell__value { color: var(--color-brightSun-950); }
.ds-field-cell--flagged .ds-field-cell__status {
  /* Reason + alert + edit-pencil strip = 36px (user 2026-06-16; was 40px =
     --space-8 / Figma 2817:1732 h-40). min-block-size so a long wrapped reason
     can still grow past it. */
  min-block-size: 2.25rem;
  background: var(--color-brightSun-50);
  color: var(--color-brightSun-600);
  font-family: var(--font-activist-semibold);
}
.ds-field-cell--flagged .ds-field-cell__status-icon {
  /* Figma asset stroke = brightSun-600 (#dd7302), not the gold frame colour. */
  background-color: var(--color-brightSun-600);
  -webkit-mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-alert-circle.svg) center / contain no-repeat;
}

/* ── Edit-history overlay (project feature, not a Figma state; co-occurs with
   any state): this field was flagged in a PRIOR round and the doctor has since
   resubmitted. A gold-600 accent on the left (inline-end, RTL) edge of the row +
   the request-time value below the current one. Neutral styling (no
   strike-through / "changed" verdict) — flagged_value is a locale-dependent
   label, so a verdict could be a false positive; the reviewer compares the two
   lines themselves. (Colour was cerulean-400 until 2026-06-13 — user switched it
   to the design-system gold-600.) */
.ds-field-cell--changed .ds-field-cell__row {
  border-inline-end: var(--stroke-2) solid var(--color-gold-600);
}
/* (The old inline «عند الطلب» __was line was REMOVED 2026-06-13 — edit
   history now lives in the #cert-history-pop dialog, opened by «السجل».) */

/* ── Resubmitted field-cell reskin (Figma 2851:1037) ──
   A field flagged in a PRIOR round that the doctor has since resubmitted and is
   now pending RE-review. A blue (cerulean) reskin of the DEFAULT cell — it keeps
   the ✓/⚠ verdict buttons — plus the shared `.ds-resub-foot` strip (below).
   Outranked by flagged/approved: once the reviewer re-decides, the cell takes
   the gold/green frame instead. The «عند الطلب» before/after line still renders
   but is HIDDEN until «السجل» reveals it (the design tucks history behind it). */
.ds-field-cell--resubmitted {
  border-color: var(--color-gold-600);
  padding: var(--space-0_5);   /* 2px decided-frame inset, like approved/flagged */
}
.ds-field-cell--resubmitted .ds-field-cell__row {
  background: var(--color-gold-50);       /* #ffffe7 gold tint */
  border-inline-end: 0;                   /* the whole tile is gold — no accent */
  /* Height: inherits the canonical 67px row (Figma 2851:1050) — the 35px verdict
     buttons + 16px padding land at exactly 67px, identical to the approved row,
     so NO height/padding override here (the old fixed-59px + 12px-padding crammed
     the buttons; removed 2026-06-13). */
}
.ds-field-cell--resubmitted .ds-field-cell__value { color: var(--color-gold-950); }

/* ── Verdict-change settle animation ──
   A soft one-shot played on the cell whose verdict JUST changed (the grid is
   rebuilt by renderGrid, so the new cell "settles" instead of snapping):
   a gentle scale-in + an expanding tinted ring that fades out — GREEN when the
   field was approved, BLUE («في انتظار المراجعة») when an approval was
   withdrawn back to review. The piece the change introduced (the approved
   footer / the returning ✓⚠ buttons) fade-rises in. JS adds the class after
   re-render and removes it on animationend (timeout fallback) — transient
   glow, not a designed static shadow. */
@keyframes ds-cell-verdict-approved {
  0%   { box-shadow: 0 0 0 0 rgba(0, 180, 141, 0.35); transform: scale(0.985); }
  45%  { transform: scale(1.004); }
  70%  { transform: scale(1); }
  100% { box-shadow: 0 0 0 0.875rem rgba(0, 180, 141, 0); transform: scale(1); }
}
@keyframes ds-cell-verdict-cleared {
  0%   { box-shadow: 0 0 0 0 rgba(54, 107, 251, 0.30); transform: scale(0.985); }
  45%  { transform: scale(1.004); }
  70%  { transform: scale(1); }
  100% { box-shadow: 0 0 0 0.875rem rgba(54, 107, 251, 0); transform: scale(1); }
}
@keyframes ds-cell-verdict-flagged {
  0%   { box-shadow: 0 0 0 0 rgba(209, 157, 0, 0.32); transform: scale(0.985); }
  45%  { transform: scale(1.004); }
  70%  { transform: scale(1); }
  100% { box-shadow: 0 0 0 0.875rem rgba(209, 157, 0, 0); transform: scale(1); }
}
@keyframes ds-cell-piece-in {
  from { opacity: 0; transform: translateY(0.25rem); }
  to   { opacity: 1; transform: translateY(0); }
}
.ds-field-cell.is-verdict-anim-approved {
  animation: ds-cell-verdict-approved 0.6s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
.ds-field-cell.is-verdict-anim-cleared {
  animation: ds-cell-verdict-cleared 0.6s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
.ds-field-cell.is-verdict-anim-flagged {
  animation: ds-cell-verdict-flagged 0.6s cubic-bezier(0.22, 0.8, 0.36, 1) both;
}
.ds-field-cell.is-verdict-anim-approved .ds-field-cell__status,
.ds-field-cell.is-verdict-anim-flagged .ds-field-cell__status,
.ds-field-cell.is-verdict-anim-cleared .ds-verdict-btns {
  animation: ds-cell-piece-in 0.32s ease-out both;
}
@media (prefers-reduced-motion: reduce) {
  .ds-field-cell.is-verdict-anim-approved,
  .ds-field-cell.is-verdict-anim-cleared,
  .ds-field-cell.is-verdict-anim-flagged,
  .ds-field-cell.is-verdict-anim-approved .ds-field-cell__status,
  .ds-field-cell.is-verdict-anim-flagged .ds-field-cell__status,
  .ds-field-cell.is-verdict-anim-cleared .ds-verdict-btns { animation: none; }
}

/* ── Resubmitted footer (CANONICAL, reusable component: ds_resub_footer) ──
   The gold status strip under any resubmitted entity (Figma 2851:1042 field
   cell / 2852:1110 certificate row): «السجل» history toggle on the far LEFT +
   «أعاد تقديمه» status on the far RIGHT (RTL `space-between`). Standalone — it
   drops under a ds_field_cell row, a qualification row, or a document tile; the
   consumer wires what «السجل» reveals via the button's data-* hook. Emitted in
   RTL visual order (status first → right; inside each group the icon is first so
   rtl parks it on the right with the text to its left).
   Theme: GOLD (recoloured 2026-06-12 from cerulean) — gold-100 strip, gold-600
   status. The «السجل» pill keeps its BLUE (button-primary #234cf0) text+glyph on
   a gold-50 fill (Figma 2856:1145/1146). */
.ds-resub-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-block-size: 2.25rem;                  /* 36px (Figma footer 2856:1145 h-36) */
  /* 6px block padding (NOT 8px): the «السجل» pill is 24px tall, so 6 + 24 + 6 =
     exactly 36px — matches Figma 2856:1145 (pill 2856:1146 sits at y=6). 8px
     padding would push the strip to 40px and break parity with the 36px
     approved footer. */
  padding: 0.375rem var(--space-4);         /* 6px / 16px */
  background: var(--color-gold-100);
}
/* «السجل» history toggle: gold-50 pill, blue-600 label + 16px clock-03 glyph. */
.ds-resub-foot__history {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                      /* 8px (Figma gap-8) */
  padding: var(--space-1) var(--space-2);   /* 4px / 8px (Figma py-4 px-8) */
  border: 0;
  border-radius: var(--radius-4);
  background: var(--color-gold-50);
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-12);
  color: var(--color-blue-600);
  cursor: pointer;
}
.ds-resub-foot__history-icon {
  inline-size: 1rem;      /* 16px (Figma 2856:1148) */
  block-size: 1rem;
  flex-shrink: 0;
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-clock-03.svg) center / contain no-repeat;
}
/* «أعاد تقديمه» status: gold-600 bold label + 20px information-square glyph;
   icon emitted FIRST so rtl flex parks it on the far-right edge. */
.ds-resub-foot__status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                      /* 8px */
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-14);
  color: var(--color-gold-600);
}
.ds-resub-foot__status-icon {
  inline-size: 1.25rem;   /* 20px */
  block-size: 1.25rem;
  flex-shrink: 0;
  background-color: var(--color-gold-600);
  -webkit-mask: url(/static/icons/fig-information-square.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-information-square.svg) center / contain no-repeat;
}

/* ── Comparison hint banner ── (Figma 2706:5062) */
.ds-cert-hint[hidden] { display: none; }   /* hidden-attr guard (see canvas note) */
.ds-cert-hint {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-cerulean-50);
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
.ds-cert-hint__main {
  display: flex;
  align-items: center;
  /* RTL start = right: pack the info icon + text flush to the right edge so any
     slack lands on the left (next to the dismiss X), not between them. */
  justify-content: flex-start;
  gap: var(--space-2);
  flex: 1 1 auto;
  min-width: 0;
}
.ds-cert-hint__info {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: block;
}
.ds-cert-hint__text {
  margin: 0;
  text-align: right;
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-14);
  color: var(--color-cerulean-950);
}
.ds-cert-hint__dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}
.ds-cert-hint__dismiss-ico {
  width: 20px;
  height: 20px;
  background-color: var(--color-blue-300);
  -webkit-mask: url(/static/icons/fig-close.svg) center / contain no-repeat;
  mask: url(/static/icons/fig-close.svg) center / contain no-repeat;
}

/* ── Divider ── (Figma 2705:5008: blue-800 hairline @ 8% opacity)
   The inner stack uses a uniform 20px gap; pull the divider in by 4px on each
   side so the space between it and the fields/footer reads as 16px (not 20). */
.ds-cert-divider {
  width: 100%;
  height: 1px;
  flex-shrink: 0;
  margin-block: -4px;
  background: var(--color-blue-800);
  border-radius: var(--radius-4);
  opacity: 0.08;
}

/* ── 4. Footer ── */
.ds-cert-modal__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-shrink: 0;
  flex-wrap: wrap;
}
.ds-cert-modal__nav {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}
/* Indicator (Figma 2705:5059): 18px blue-900, current position BOLD, the total
   and slash faded to 40%. `direction:ltr` so it always reads «total / current»
   exactly as designed regardless of the surrounding RTL flow. */
.ds-cert-modal__nav-ind {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  direction: ltr;
  font-size: var(--type-ar-18);
  color: var(--color-blue-900);
  min-width: 44px;
}
.ds-cert-modal__nav-ind .ds-cert-nav-ind__total,
.ds-cert-modal__nav-ind .ds-cert-nav-ind__sep {
  font-family: var(--font-activist-regular);
  opacity: 0.4;
}
.ds-cert-modal__nav-ind .ds-cert-nav-ind__current {
  font-family: var(--font-activist-bold);
}

/* Certificate nav buttons (Figma 2705:5049/5050/5051): a blue-50 chevron chip
   inset 2px inside a 1px blue-600 ring. Disabled = the whole button at 40%. */
.ds-cert-navbtn {
  display: inline-flex;
  padding: 2px;
  border: var(--stroke-1) solid var(--color-blue-600);
  border-radius: var(--radius-4);
  background: transparent;
  color: var(--color-blue-600);
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.ds-cert-navbtn__fill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  background: var(--color-blue-50);
  border-radius: var(--radius-2);
  transition: background-color 0.15s ease;
}
.ds-cert-navbtn .ds-cert-ico { width: 20px; height: 20px; }
.ds-cert-navbtn:hover:not([disabled]) .ds-cert-navbtn__fill {
  background: var(--color-blue-100);
}
.ds-cert-navbtn[disabled] { opacity: 0.4; pointer-events: none; }

/* ── Decision buttons (reusable component: ds_decision_buttons macro) ──
   The green «approve» + amber «request changes» pair, shared by the
   step-review bar and the certificate modal. The flat fill + two-layer colour-
   tinted drop shadow comes from the canonical `.ds-btn--success` /
   `.ds-btn--warning` variants in this file (design-system.css) — this block
   only owns the cluster LAYOUT (12px gap; edge-to-edge on phones). */
.ds-decision-btns {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  /* THE ACTIONS BOX SITS AT THE FAR LEFT — always, every container, every width
     (Mohammed, standing). `flex-end` is the trailing edge, so it is the visual
     LEFT in RTL and the right in LTR; the rule needs no direction branch.
     It is a no-op wherever the cluster is shrink-to-fit (the desktop modal
     footer, the step bar) and wherever its children already fill the row (the
     ≤640 phone rule) — it earns its keep the moment a parent STRETCHES it. That
     is the tablet certificate footer, which stacks at ≤1024px: the box became
     884px wide and packed its buttons at the inline-START, i.e. the visual
     RIGHT. It was fixed once for `.ds-cert-rc-foot__actions` alone, as a
     media-query special case, so the very next stretched cluster reproduced it.
     Stated on the cluster itself, it cannot. */
  justify-content: flex-end;
}
/* Same height as the certificate-nav buttons (.ds-cert-navbtn = 3.125rem/50px)
   so both buttons match each other and the nav, reading as one aligned band.
   The inner fills the set height and keeps its centred icon + label. */
.ds-decision-btn {
  height: 3.125rem;
}
.ds-decision-btn .ds-btn__inner {
  height: 100%;
}
/* Phone: the pair fills the row edge-to-edge with a 12px gap between. */
@media (max-width: 640px) {
  .ds-decision-btns {
    display: flex;
    width: 100%;
    flex-wrap: nowrap;
  }
  .ds-decision-btns .ds-decision-btn {
    flex: 1 1 0;
    min-width: 0;
  }
  .ds-decision-btns .ds-decision-btn .ds-btn__inner {
    width: 100%;
  }
}

/* ── Responsive ── */
@media (min-width: 880px) {
  .ds-cert-modal { width: min(1180px, 94vw); }
}
/* Phone + tablet: shrink the top/bottom gap from 32px to 16px each, and split
   the footer into stacked rows — the decision (action) buttons sit on TOP, and
   the certificate nav drops to its OWN full-width row BELOW with the arrows
   pinned to the two edges («السابق» → far right, «التالي» → far left) and the
   count centred between them. (column-reverse: DOM is nav→decision, so reversed
   puts the action buttons above the nav row.) */
@media (max-width: 1024px) {
  .ds-cert-modal {
    height: calc(100dvh - 32px);
    max-height: calc(100dvh - 32px);
  }
  /* Phone + tablet: box padding 16px on all four sides (desktop is 20px). */
  .ds-cert-modal__inner {
    padding: var(--space-4);
  }
  .ds-cert-modal__foot {
    flex-direction: column-reverse;
    align-items: stretch;
  }
  .ds-cert-modal__nav {
    width: 100%;
    justify-content: space-between;
  }
  /* Tablet: the request-changes footer stacks, so the (now full-width) actions
     box would otherwise pack its buttons at the inline-start (right, RTL). Pin
     the button cluster to the far LEFT (end) edge instead — the same standing
     rule `.ds-decision-btns` now carries on itself, kept here because this
     cluster is a different element and has no such base rule. */
  .ds-cert-rc-foot__actions {
    justify-content: flex-end;
  }
}
@media (max-width: 640px) {
  .ds-cert-grid { grid-template-columns: 1fr; }
  /* Phones get a SHORTER viewer floor than the desktop 20rem so the
     single-column data block keeps more room; still vh-capped so a short
     viewport shrinks the frame rather than overflowing the modal. The viewer
     fills the frame (base rule), so the floating toolbar always stays inside
     the visible black box. */
  .ds-cert-viewer-frame { min-height: min(15rem, 42vh); }
}

/* ============================================================================
   OVERLAY SCROLLBAR THEME — os-theme-alhakeem (OverlayScrollbars v2)
   A slim, rounded, translucent overlay bar that floats over the content (zero
   layout width), tinted with the Carrara (warm-neutral) design-system shades so
   it sits quietly against the page instead of shouting in brand blue. Wired in
   static/js/scroll-init.js on the document body. The handle is slimmer than its
   track at rest and swells to full width on hover for a polished feel.
   ============================================================================ */
.os-theme-alhakeem {
  --os-size: 9px;                          /* slim bar */
  --os-padding-axis: 2px;
  --os-padding-perpendicular: 2px;
  --os-track-border-radius: 10px;
  --os-track-bg: transparent;
  --os-track-bg-hover: color-mix(in srgb, var(--color-cararra-500) 7%, transparent);
  --os-track-bg-active: color-mix(in srgb, var(--color-cararra-500) 10%, transparent);
  --os-handle-border-radius: 10px;
  /* Carrara (warm neutral) shades — ascending degrees for rest → hover → active. */
  --os-handle-bg: color-mix(in srgb, var(--color-cararra-400) 60%, transparent);
  --os-handle-bg-hover: color-mix(in srgb, var(--color-cararra-500) 78%, transparent);
  --os-handle-bg-active: color-mix(in srgb, var(--color-cararra-600) 90%, transparent);
  --os-handle-min-size: 40px;
  --os-handle-perpendicular-size: 58%;
  --os-handle-perpendicular-size-hover: 100%;
  --os-handle-perpendicular-size-active: 100%;
}

/* Native scrollbars EVERYWHERE the body overlay doesn't cover — inner scroll
   areas like the cert-modal data block + document viewer, long menus, any
   overflow container. Slim + Carrara so every scrollbar in the panel matches
   the page bar. The transparent inset border (background-clip: padding-box)
   makes the thumb read even thinner than the 8px track. Chromium/WebKit via
   ::-webkit-scrollbar; Firefox via scrollbar-width/color. OverlayScrollbars'
   own host hides its native bar with higher specificity, so the page never
   shows a double scrollbar. */
/* Firefox: `scrollbar-color` is inherited (set once on html → reaches every
   inner scroller); `scrollbar-width` is NOT inherited, so it's applied to the
   scrollers themselves — everything EXCEPT the html/body document scroller,
   which OverlayScrollbars owns (keeping the page's only bar the overlay one).
   OverlayScrollbars' own viewport keeps `none` via its higher-specificity rule.
   The solid colour before each `color-mix` is the fallback for engines without
   color-mix() support. */
html {
  scrollbar-color: #778a79 transparent;
  scrollbar-color: color-mix(in srgb, var(--color-cararra-500) 55%, transparent) transparent;
}
:where(:not(html, body)) {
  scrollbar-width: thin;
}
/* Chromium / WebKit (Chrome, Edge, Safari, Opera, older Blink). */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: #778a79;
  background: color-mix(in srgb, var(--color-cararra-400) 70%, transparent);
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: #5c705f;
  background: color-mix(in srgb, var(--color-cararra-500) 88%, transparent);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner {
  background: transparent;
}

/* ── Unified request-changes pop (scalar steps, Figma 2817:1762) ──────────────
   The pop reuses the certificate modal's rc CARD / TOGGLE / REASON components
   (`.ds-cert-rc*`) verbatim — only the standalone dialog shell, footer and the
   freshly-built reasons list are scoped here (the cert modal embeds its rc panel
   in the modal body; this one lives in its own .ds-decision-dialog). */
.ds-decision-dialog.ds-step-rc-dialog {
  width: min(940px, 96vw);
  max-width: 96vw;
}
/* NO top accent stripe (the base .ds-decision-dialog__inner paints a 3px blue
   border-block-start; Figma 2817:1762 has none). */
.ds-step-rc-dialog .ds-decision-dialog__inner { border-block-start: 0; }
/* 20px content inset all round (Figma frame margin: 885−845 = 20px each side). */
.ds-step-rc-dialog .ds-decision-dialog__header {
  padding: var(--space-5) var(--space-5) var(--space-4);
}
.ds-step-rc-dialog .ds-decision-dialog__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);                 /* 20px hint ↔ cards (Figma) */
  padding: var(--space-4) var(--space-5) var(--space-5);
}
.ds-step-rc-hint { margin: 0; }
/* Header (Figma 2817:1763): close (trailing/left) + a brightSun-50 title pill
   (reading/right) — NO header hairline (the only divider sits above the foot). */
.ds-step-rc-head::after { display: none; }
/* Spacing per Figma 2817:1767 — REMEMBER the scale offset: Figma spacing/N maps
   to the project's --space-(N-1) (--space-1=4px … --space-5=20px). */
.ds-step-rc-titlepill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                 /* 8px chip ↔ text (Figma spacing/3) */
  padding: var(--space-3);             /* 12px (Figma spacing/4) */
  background: var(--color-brightSun-50);
  border-radius: var(--radius-4);
}
.ds-step-rc-titlepill__text {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);                 /* 4px «طلب تعديل» ↔ step name (Figma spacing/2) */
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  color: var(--color-brightSun-1000);
  white-space: nowrap;
}
.ds-step-rc-titlepill__pre { opacity: 0.7; }
.ds-step-rc-titlepill__chip {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  border-radius: var(--radius-2);
  background: var(--color-brightSun-500);
}
/* OverlayScrollbars host: caps the height + scrolls (the JS inits OS on it).
   The grid stays the inner child so OS's viewport can't break the columns.
   `overflow-y: auto` gives it a NATIVE scroll base too, so the mouse wheel
   works even before/without OS (OS keeps native scrolling underneath). */
.ds-step-rc-scroll {
  /* min-height: 0 is LOAD-BEARING: this is a flex item (the body is a flex
     column), and a flex item's default `min-height: auto` resolves to the
     content size and OVERRIDES max-height — so without this the host grows to
     fit every card, never overflows, and nothing (native OR OverlayScrollbars)
     scrolls. With min-height:0 it caps at max-height, overflows, and scrolls. */
  min-height: 0;
  max-height: min(54vh, 420px);
  overflow-y: auto;
}
/* `.ds-cert-rc` lays the two-column grid; override its 20px gap to the Figma
   2817 card gap (16px = spacing/5). The host above owns the scroll — so the
   grid must NOT be a scroll container here: the shared `.ds-cert-rc` sets
   `overflow-y: auto` + `overscroll-behavior: contain`, and with no height limit
   on the grid that combination TRAPS the mouse wheel on a non-scrollable
   element so it never reaches the host (the wheel-dead bug). Reset both. */
.ds-step-rc-cards {
  gap: var(--space-4);
  overflow: visible;
  overscroll-behavior: auto;
}
/* Footer (Figma 2817:1830): counter on the reading edge, the confirm + cancel
   buttons on the trailing edge — INSIDE the 20px content inset, with a hairline
   above (the cards↔footer divider, Figma 2817:1827). */
.ds-step-rc-foot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);                 /* 12px */
  flex-wrap: wrap;
  padding: var(--space-4) var(--space-5);
}
/* Inset hairline (Figma 2817:1829 — the cards↔footer divider): a faint line
   that stops short of the edges, aligned with the 20px content inset, NOT a
   full-bleed border (same inset-::after pattern as the dialog header). */
.ds-step-rc-foot::before {
  content: "";
  position: absolute;
  inset-inline: var(--space-5);
  inset-block-start: 0;
  block-size: var(--stroke-1);
  background: var(--border-divider);
}
.ds-step-rc-foot .ds-cert-rc-foot__count {
  margin: 0;
  font-size: var(--type-ar-14);
  color: var(--color-blue-900);
  opacity: 0.4;                        /* Figma 2817:1845 */
}
.ds-step-rc-foot .ds-cert-rc-foot__actions {
  display: flex;
  gap: var(--space-3);                 /* 12px between confirm + cancel (Figma) */
  flex-wrap: wrap;
}
/* Reasons list built fresh in the picker (not reparented from a card). */
.ds-step-rp-reasons {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* Step reason picker: no top accent stripe either (Figma). */
[data-step-reason-picker] .ds-decision-dialog__inner { border-block-start: 0; }

/* ============================================================================
   Shared tooltip — .ds-tooltip / [data-ds-tip]
   ----------------------------------------------------------------------------
   A single body-appended, position:fixed bubble driven by the panel.js
   tooltip controller. Fixed positioning lets it escape overflow/scroll
   clipping (scrollable dropdown panels) and viewport edges. Any element with
   `data-ds-tip="…"` triggers it on hover/focus. Dark bubble, hugs its text,
   wraps long copy. Promoted from the form-detail .rf-tip-bubble one-off.
   ========================================================================== */
.ds-tooltip {
  position: fixed;
  top: 0;
  left: 0;
  /* Orders the bubble against ORDINARY content only. It says nothing about
     modal dialogs: a <dialog> opened with showModal() sits in the browser's TOP
     LAYER, which paints above the entire normal document whatever z-index it
     meets. (The comment here used to claim "above dialogs/popovers" beside a
     fallback of 2147483000 — wrong twice: the token IS defined, so the value
     was 70, and no value would have won.) The bubble is instead RE-HOMED into
     the open dialog by panel.js; see `hostFor`. */
  z-index: var(--z-tooltip, 70);
  max-width: min(88vw, 20rem);
  box-sizing: border-box;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-4);
  background: var(--color-blackHaze-1000);
  color: var(--color-blackHaze-50);
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  font-weight: 400;
  line-height: 1.6;
  text-align: start;
  white-space: normal;
  word-break: break-word;
  box-shadow:
    0 6px 20px color-mix(in srgb, var(--color-blackHaze-1000) 16%, transparent),
    0 1px 3px color-mix(in srgb, var(--color-blackHaze-1000) 10%, transparent);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.125rem);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s ease;
  pointer-events: none; /* never steals the hover from its trigger */
}
.ds-tooltip.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* ── The arrow ──
   A rotated SQUARE, not a `border`-triangle: a border triangle cannot take a
   box-shadow, so it would read as a flat tab stuck on a bubble that has one.
   Half of the square overlaps the bubble and half pokes out; both wear the same
   background, so the joint is invisible and the shadow continues around it.
   `panel.js` sets `left` (aimed at the TRIGGER's centre, clamped clear of the
   corner radius) and `top` (the edge facing the trigger). Keep the 8px in step
   with `ARROW` there. */
.ds-tooltip__arrow {
  position: absolute;
  inline-size: 8px;
  block-size: 8px;
  background: inherit;
  border-radius: 1px;
  transform: rotate(45deg);
  /* Only the OUTER half is visible, so the shadow must not fall on the bubble's
     own face; this pair matches the bubble's two-layer shadow at the angle the
     arrow shows. */
  box-shadow:
    0 6px 20px color-mix(in srgb, var(--color-blackHaze-1000) 16%, transparent),
    0 1px 3px color-mix(in srgb, var(--color-blackHaze-1000) 10%, transparent);
  pointer-events: none;
}
/* The half that overlaps the bubble must not cast onto it — the bubble's own
   background paints over the arrow's inner half, and this keeps the joint from
   showing a seam when the arrow points UP (shadow travels downward). */
.ds-tooltip.is-below .ds-tooltip__arrow { box-shadow: none; }
.ds-tooltip__label {
  position: relative;   /* above the arrow's overlapping half */
  display: block;
}

/* ============================================================================
   Tip host — `.ds-tip-host` : explaining a DISABLED control
   ----------------------------------------------------------------------------
   A `<button disabled>` is inert: engines retarget its mouse events to the
   nearest ENABLED ancestor, and it is out of the tab order — so the delegated
   `[data-ds-tip]` controller (panel.js) can never resolve a tip sitting on the
   button itself, and the native `title` is not painted either. Every disabled
   control on the review surface was therefore silent on hover.

   The fix keeps the button REALLY disabled — flipping it to `aria-disabled`
   would make it clickable again, and at least two server write paths still
   rely on the client-side block, so that would open a hole rather than close
   one. Instead the reason hangs on an enabled HOST:

     · a container that already wraps the pair — `.ds-verdict-btns[data-ds-tip]`
       when BOTH verdicts are blocked by the SAME reason (no new markup at all);
     · this `.ds-tip-host` span around a lone disabled button standing next to
       enabled siblings, where a container-level tip would also cover them.

   `tabindex="0"` keeps the explanation reachable by keyboard, which the
   disabled button itself cannot be. The host must NOT set `pointer-events`
   and must lay out transparently, so it mirrors the flex/inline nature of the
   button it wraps.
   ========================================================================== */
.ds-tip-host {
  display: inline-flex;
  align-items: center;
  /* The host is a pure wrapper: it must never add its own box metrics, and it
     must never show a focus ring of its own (the tooltip IS the affordance). */
  margin: 0;
  padding: 0;
  outline: none;
}
.ds-tip-host:focus-visible {
  border-radius: var(--radius-4);
  outline: 2px solid var(--color-cerulean-500, currentColor);
  outline-offset: 2px;
}
/* The host must not grow, or a lone button would stretch across the row.
   NOTE, measured 2026-07-28 at 390px: the ≤640 «edge-to-edge» rule below targets
   `.ds-decision-btn`, which is now a GRANDCHILD — the macro wraps every button
   in a `.ds-tip-host` so a disabled one can carry its reason — so `flex: 1 1 0`
   never reaches it and the pair does NOT fill the row on phones. The cluster
   still sits at the far LEFT there (the standing rule on `.ds-decision-btns`),
   so nothing is misplaced; the fill is simply not happening. Left as measured
   rather than "fixed" in passing: making it fill is a visible phone-layout
   change, not a comment correction. */
.ds-decision-btns > .ds-tip-host {
  flex: 0 1 auto;
}
.ds-decision-btns > .ds-tip-host > .ds-btn {
  width: 100%;
}
/* The status-footer pencil parks itself on the far LEFT via
   `margin-inline-start: auto`. Wrapping it hands that job to the host, or the
   button would snap back beside the status text. An explicit modifier rather
   than `:has()` — this panel must not depend on selector-level browser drift. */
.ds-tip-host--pencil {
  margin-inline-start: auto;
}
.ds-tip-host--pencil > .ds-field-cell__edit {
  margin-inline-start: 0;
}
/* Same for the certificate-viewer icon buttons, which are sized by their
   parent's flex rules — the host must not introduce a second box. */
.ds-cert-modal__nav > .ds-tip-host,
.ds-cert-viewer__pages > .ds-tip-host {
  flex: 0 0 auto;
}

/* ============================================================================
   Reopen slot — `.ds-reopen-slot`
   ----------------------------------------------------------------------------
   A SETTLED step renders BOTH its «تعديل القرار» control and the pending-state
   controls, the latter `hidden`; confirming the reopen flips which one shows.
   That is what makes reopening free: no round trip and no write — the verdict
   row is untouched until a NEW decision is taken.

   `display: contents` keeps the wrapper out of the box tree so the buttons stay
   direct flex children of `.ds-step-actions` / `.ds-qual-foot` and their gap and
   alignment are unchanged. It MUST come from this class, never an inline style:
   an inline `display` outranks the UA rule behind the `hidden` attribute, and
   the "hidden" set would render alongside the visible one.
   ========================================================================== */
.ds-reopen-slot {
  display: contents;
}
.ds-reopen-slot[hidden] {
  display: none;
}

/* ============================================================================
   Awaiting-upload box — `.ds-doc-awaiting`
   ----------------------------------------------------------------------------
   Stands exactly where a document row would: the canonical `.ds-empty-state`
   frame saying WHICH document is missing, with the single act that still
   applies under it. No decision card, because with no file AND no value in any
   field the document evidences there is nothing to decide.
   ========================================================================== */
.ds-doc-awaiting {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
}
.ds-doc-awaiting > .ds-btn {
  align-self: flex-end; /* far LEFT in RTL, like every other decision control */
}
@media (max-width: 30rem) {
  .ds-doc-awaiting > .ds-btn {
    align-self: stretch;
  }
}
/* The certificate viewer's icon/nav buttons killed hover outright, which made
   them unreachable even from an ancestor host. Keep them visually inert but
   let the pointer through so the reason can be explained. The buttons remain
   `disabled`, so no click can land. */
.ds-cert-iconbtn[disabled],
.ds-cert-navbtn[disabled] {
  pointer-events: auto;
  cursor: not-allowed;
}


/* ==========================================================================
   MISSING-FIELD TOUR — «أرِني الحقول الناقصة»
   Dims the page and leaves exactly one cell lit, one stop at a time.

   Why a box-shadow spread and not four dim panels: the hole must follow the
   cell's own rounded corners, and one element with a huge spread gives that
   for free while staying a single node to move between hosts.

   THE HOST IS THE HARD PART. Two of doctor 105's three missing cells exist
   only inside the certificate modal, and a <dialog> opened with showModal()
   sits in the browser's TOP LAYER — no z-index reaches over it (measured
   2026-07-26, the same wall the tooltip hit). So when a stop lives inside the
   open dialog, the JS RE-HOMES these nodes into that dialog. `position: fixed`
   keeps resolving against the viewport inside a dialog, so the geometry needs
   no correction when the host changes.
   ========================================================================== */
.ds-spot {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-tooltip, 70);
  border-radius: var(--radius-4);
  /* The dim IS the spread. 100vmax covers any viewport at any scroll. */
  box-shadow:
    0 0 0 100vmax color-mix(in srgb, var(--color-blackHaze-1000) 62%, transparent),
    0 0 0 2px var(--feedback-warning-border, var(--color-blackHaze-50));
  pointer-events: none;
  opacity: 0;
  /* NO geometry transition by default. The hole spends most of its life GLUED
     to a cell that is moving under it (smooth scroll, accordion reflow), and
     the tracker rewrites its rect every frame — a declared transition would be
     restarted by each of those writes and never finish, which is what made the
     movement read as a lag-chase instead of an animation. Easing is opted INTO
     for the one moment it belongs to: travelling from one cell to the next. */
  transition: opacity 0.2s ease;
  will-change: top, left, width, height;
}
.ds-spot.is-on { opacity: 1; }
/* TRAVEL — a single write, then this curve carries the hole across. The curve
   is a decelerate: fast off the mark, settling gently onto the target, which is
   what makes a guided move feel deliberate rather than mechanical. */
.ds-spot.is-travelling {
  transition:
    opacity 0.2s ease,
    top 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    left 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    width 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    height 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    border-radius 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}
/* ARRIVAL — one ring expands out of the cell edge and fades. It is the
   "you are here" beat: the hole alone tells you where the light is, this tells
   you it just LANDED. Transform + opacity only, so it composites. */
.ds-spot::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  border: 2px solid var(--feedback-warning-border, var(--color-blackHaze-50));
  opacity: 0;
  pointer-events: none;
}
.ds-spot.is-arriving::after { animation: ds-spot-land 0.62s ease-out 1; }
@keyframes ds-spot-land {
  0%   { opacity: 0.9; transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.09); }
}

/* The lit cell must sit ABOVE the dim, or the spread paints over it. Only a
   stop in the ORDINARY document needs this — inside the dialog the cell is
   already in the top layer with the spot. */
.ds-spot-target {
  position: relative;
  z-index: calc(var(--z-tooltip, 70) + 1);
  border-radius: var(--radius-4);
}

/* Motion is a guide here, not decoration: with reduced motion the hole still
   MOVES (that is the information), it simply arrives at once and does not
   pulse. */
@media (prefers-reduced-motion: reduce) {
  .ds-spot,
  .ds-spot.is-travelling { transition: opacity 0.2s ease; }
  .ds-spot.is-arriving::after { animation: none; }
  .ds-spot-row { transition: none; }
}

/* The tour's own card. Same re-homing rule as the spot.
   It is a CHECKLIST, not a progress bar: a header, the list of missing fields
   with one row each, then the acts at the trailing edge — the same edge every
   decision row on this surface uses. The bar it replaces showed one name and a
   percentage; the reviewer's question is «which fields still need me», and a
   percentage cannot answer it because it never names its own members. */
.ds-spot-bar {
  position: fixed;
  inset-inline: 0;
  bottom: var(--space-6);
  z-index: calc(var(--z-tooltip, 70) + 2);
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.ds-spot-bar__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-5);
  inline-size: min(94vw, 34rem);
  /* A white CARD, not a dark tooltip: it is a panel surface the reviewer acts
     on, so it takes the page's own card background and the two-layer shadow the
     toasts already use — reused, never re-invented. Its buttons are ordinary
     `.ds-btn`s, which only read correctly on a light surface. */
  background: var(--bg-card, var(--color-white-50));
  border: var(--stroke-1) solid var(--border-divider);
  box-shadow:
    0 6px 12px rgba(23, 28, 24, 0.06),
    0 12px 24px rgba(23, 28, 24, 0.12);
  pointer-events: auto;
  max-width: min(94vw, 52rem);
  overflow: hidden;
}
/* The bar must never cover the cell it is pointing at. When the spotlight
   lands low enough to sit under the card, the card moves to the TOP edge
   instead — cheaper and more reliable than trying to scroll around it, and it
   works inside the certificate modal too, where the scroll range is the
   dialog's, not the page's. */
/* The flip is a TRANSFORM, not a swap of `bottom` for `top`: those two cannot
   transition into one another, so the card teleported. Anchored at the bottom
   always, and lifted to the top edge by exactly the distance between them —
   which is a property the compositor can animate. */
.ds-spot-bar {
  transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .ds-spot-bar { transition: none; }
}
/* Header: what the card is, and how much of it is done — as a COUNT OF SENT
   REQUESTS, not a position in a walk. «٢ من ٣ أُرسلت» is a fact about the work;
   «٢ من ٣» was a fact about the reviewer's cursor. */
.ds-spot-bar__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.ds-spot-bar__title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--text-onPage-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* FOLD — the card covers the page, and inside the certificate modal it has
   nowhere to flee to: the dialog is centred and so is the card, so the list can
   sit straight over the cells being pointed at. Folding drops the list and
   keeps the walk. The chevron turns to say which way it goes. */
.ds-spot-bar__fold {
  flex: 0 0 auto;
  inline-size: 2rem;
  block-size: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-4);
  /* The control reads as a control: blue-50 plate, blue-600 glyph — the pairing
     every actionable icon on this surface uses. */
  background: var(--color-blue-50);
  cursor: pointer;
}
.ds-spot-bar__fold:hover { background: var(--color-blue-100); }
/* An ACTION's glyph is blue-600, always — the same colour the verdict pencil
   and every other actionable icon on this surface carries. It was the muted
   text colour, which reads as a label rather than as something to press. */
.ds-spot-bar__fold-ico {
  inline-size: 1rem;
  block-size: 1rem;
  background-color: var(--color-blue-600);
  -webkit-mask: url(/static/icons/chevron-down-01.svg) center / contain no-repeat;
  mask: url(/static/icons/chevron-down-01.svg) center / contain no-repeat;
  transition: transform 0.2s ease;
}
.ds-spot-bar.is-folded .ds-spot-bar__fold-ico { transform: rotate(180deg); }
.ds-spot-bar.is-folded .ds-spot-list { display: none; }
/* Folded, the header carries the current field instead of the summary — so it
   is the loud element, not a caption over a list that is no longer there. */
.ds-spot-bar.is-folded .ds-spot-bar__title {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-16);
}
/* The close control is the canonical `.ds-close-btn`. */
.ds-spot-bar__close {
  flex: 0 0 auto;
  border-radius: 0;
  width: 2rem;
}

/* ── The list ──
   Scrolls at a cap rather than growing: a doctor can be missing a dozen fields,
   and a card tall enough to hold them would cover the very cell the spotlight
   is pointing at — which is the one thing this card must never do. */
.ds-spot-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-block-size: 15rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.ds-spot-row { border-radius: var(--radius-4); transition: background-color 0.15s ease; }
.ds-spot-row__btn {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  inline-size: 100%;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-4);
  background: transparent;
  text-align: start;
  cursor: pointer;
}
.ds-spot-row__btn:hover { background: var(--bg-page, rgba(23, 28, 24, 0.04)); }
.ds-spot-row.is-current { background: var(--feedback-warning-bg); }
.ds-spot-row.is-current .ds-spot-row__name { font-family: var(--font-activist-bold); }

/* The mark. An empty ring until the change request for that field has been
   CONFIRMED AND SENT, then a green-600 disc carrying a green-50 tick — the tick
   is the light colour ON the fill, never the reverse. `requested` is the
   SERVER's answer, so the ring is the database's state and not a memory of what
   this tab did. */
.ds-spot-row__mark {
  flex: 0 0 auto;
  position: relative;
  inline-size: 1.25rem;
  block-size: 1.25rem;
  border-radius: 50%;
  border: var(--stroke-2) solid var(--border-divider);
}
[data-spot-state="sent"] .ds-spot-row__mark {
  border-color: var(--color-green-600);
  background-color: var(--color-green-600);
}
[data-spot-state="sent"] .ds-spot-row__mark::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--color-green-50);
  -webkit-mask: url(/static/icons/fig-check.svg) center / 0.75rem no-repeat;
  mask: url(/static/icons/fig-check.svg) center / 0.75rem no-repeat;
}

.ds-spot-row__name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-activist-semibold);
  font-size: var(--type-ar-14);
  color: var(--text-onPage-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-spot-bar__foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
/* WHERE YOU ARE, on the reading edge, beside the buttons that change it. The
   current number is the loud element — it is the one fact re-read on every
   move — and the total stays quiet rather than competing at the same weight. */
.ds-spot-bar__pos {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  flex: 0 0 auto;
}
.ds-spot-bar__pos[hidden] { display: none; }
.ds-spot-bar__pos-cur {
  font-family: var(--font-activist-bold);
  font-size: var(--type-ar-18);
  line-height: 1.1;
  color: var(--text-onPage-primary);
}
.ds-spot-bar__pos-of {
  font-family: var(--font-activist-regular);
  font-size: var(--type-ar-12);
  color: var(--text-onPage-secondary, var(--text-onPage-primary));
  opacity: 0.8;
  white-space: nowrap;
}
.ds-spot-bar__acts {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* THE ACTIONS BOX SITS AT THE FAR LEFT — always, every width. The canonical
     `justify-content: flex-end` (`.ds-decision-btns`) only earns its keep when a
     parent STRETCHES the cluster; this one is shrink-to-fit beside the counter,
     so what moves it is an auto margin — on its INLINE-START side, which in RTL
     is its right, pushing the box to the trailing (visual left) edge.
     `margin-inline-end: auto` is the same statement mirrored, and it seated the
     buttons on the RIGHT — measured. And never `space-between` on the row: with
     that the buttons' position would depend on how wide the counter happens to
     render, so «١ من ٣» and «١٠ من ١٢» would seat them differently. */
  justify-content: flex-end;
  margin-inline-start: auto;
  flex-wrap: wrap;
}
@media (max-width: 640px) {
  .ds-spot-bar { inset-inline: var(--space-3); }
  .ds-spot-bar__inner { inline-size: 100%; }
  .ds-spot-list { max-block-size: 9rem; }
}

/* The alert's action slot — trailing edge, vertically centred against the
   two-line body, and never squeezed by a long description. */
.ds-alert__action {
  margin-inline-start: auto;
  align-self: center;
  flex: 0 0 auto;
}
@media (max-width: 640px) {
  .ds-alert__action { margin-inline-start: 0; width: 100%; }
  .ds-alert__action .ds-btn { width: 100%; }
}
