/* ============================================
   CSS Variables — the SHARED design tokens
   ============================================
   What is shared by every design lives here; what a DESIGN changes lives in
   static/css/designs/<id>.css (two rules, colours only — see that file's
   header and plans/switchable-designs-plan.md §4.2).

   The split, and the rule for deciding where a token goes: a design changes
   COLOURS and nothing else (D5, D9). So the surfaces, the text, the borders,
   the brand and the chrome colours are design tokens and are NOT here; the
   spacing, the radii, the type, the z-index ladder, the transitions and the
   STATUS colours are the app's, in every design, and are.

   Two consequences worth knowing:

   * SHARED IS NOT THE SAME AS THEME-INVARIANT. The status colours differ
     between light and dark and are still shared by every design, which is why
     this file keeps both a :root and a [data-theme="dark"] block.
   * THERE IS NO :root FALLBACK DESIGN, deliberately. A page rendered without
     data-design paints with no design tokens — visibly broken on first load
     rather than quietly wrong — and the fouc-script stamps a valid id before
     first paint on every page, validated against the registry the server
     rendered into it.

   Standard Breakpoint Tiers (for media queries):
     640px  — small mobile landscape
     768px  — tablet portrait
     1024px — tablet landscape / sidebar collapse
     1280px — small desktop (HD)
     1536px — medium desktop
     1920px — full HD (1080p)
     2560px — 2K / QHD (or scaled 2K at 150% DPI)
   ============================================ */

:root {
    color-scheme: light;

    /* ===========================================
       Type — ONE stack for every design (D5)
       ===========================================
       The owner compared nine families at the app's real sizes and chose to
       keep today's: «نبقي على استخدام Segoe UI خط التطبيق اليوم». So NO web
       font ships, font-src stays 'self', and there is nothing to download on
       any page in any design.

       These are SHARED, never design tokens: the four designs render in the
       same type, and a design file may not name a family (DesignTokensTest
       refuses one). Changing the app's font later is this one line — plus a
       self-hosted @font-face library and a CACHE_VERSION bump only if the new
       family is not a system font.

       --font-sans is the DEVICE's font: Segoe UI on Windows, San Francisco on
       a Mac or an iPhone, Roboto on Android. It is also the font CLAUDE.md's
       measured numbers were taken with — the 270px stat-tile floor, the
       clamped column widths, the 260px rail — so those stay valid.

       --font-mono unifies what were three different stacks across six
       declarations ('Courier New'; 'Consolas','Monaco','Courier New'; bare
       monospace), so ids, OTP codes and technical values share one family. */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
    --font-mono: ui-monospace, Consolas, "Courier New", monospace;
    /* Bootstrap reads its own variables, which never follow data-theme — but
       these two are not theme values, so bridging them here makes Bootstrap's
       code, kbd and .font-monospace follow the app's type with no per-rule
       override. */
    --bs-body-font-family: var(--font-sans);
    --bs-font-monospace: var(--font-mono);

    /* ===========================================
       Status Colors — SHARED by every design (D9)
       ===========================================
       A status is a SIGNAL, not a brand colour: «انعقدت» must read the same
       green whichever design a colleague picked, so these are defined once
       here and a design file may not redefine them. Confirmed 2026-09-16:
       «اترك ألوان الحالات كما هي». */
    --success: #10b981;
    --success-rgb: 16, 185, 129;
    --danger: #ef4444;
    --danger-rgb: 239, 68, 68;
    --warning: #f59e0b;
    --warning-rgb: 245, 158, 11;
    --info: #3b82f6;
    --info-rgb: 59, 130, 246;

    /* Button fills and readable-as-text shades (2026-09-12, plans/button-scale-plan.md).
       The four status tokens above are tuned for badges, rings and icons. As a FILL
       under white text, or as TEXT on the card, three of them fail WCAG AA —
       measured: --danger 3.76:1, --success 2.04:1, --info 3.68:1 with white, and
       --warning 2.15:1 as text on white. So a filled button takes --*-fill (white
       text; amber is the one that only passes under DARK text, hence --on-warning)
       and an outline button takes --*-text at rest, tuned against the page
       background, the darker of the two surfaces a toolbar sits on: a shade that
       passes on the card alone measured 4.08:1 on the page. The fills are the same
       in both themes, because a button's contrast is its text against its own fill
       and the theme around it changes nothing; the -text shades flip with the card. */
    --danger-fill: #dc2626;          /* 4.83:1 under white */
    --danger-fill-hover: #b91c1c;    /* 6.47:1 */
    --danger-text: #b91c1c;          /* 5.25:1 on --bg, 6.14:1 on --card */
    --success-fill: #047857;         /* 5.48:1 under white */
    --success-fill-hover: #065f46;
    --success-text: #065f46;         /* 6.23:1 on --bg */
    --info-fill: #2563eb;            /* 5.17:1 under white */
    --info-fill-hover: #1d4ed8;
    --info-text: #1d4ed8;            /* 5.44:1 on --bg */
    --warning-fill: var(--warning);  /* 6.83:1 under --on-warning */
    --warning-fill-hover: #d97706;   /* 4.6:1 under --on-warning */
    --warning-text: #92400e;         /* 5.76:1 on --bg */
    --on-warning: #1f2937;
    /* The NEUTRAL fill, for the one status that is not a status: a .bg-secondary
       badge means "none / not set". It joined the set in phase 4, when the six
       .bg-* badges stopped carrying literals — every other badge had a token to
       point at and this one had #6b7280 written into the rule. Grey on purpose
       rather than a design token: "not set" must not read as a brand colour. */
    --neutral-fill: #6b7280;         /* 4.83:1 under white, on either card */

    /* ── Chart series — SHARED by every design, like the statuses ───────────
       Three slots, for the three money series of لوحة الإدارة (المفوتر ·
       المحصّل · المصروفات). A design MUST NOT redefine them: a chart legend
       that changed colour with the reader's design would make two colleagues
       describe the same bar differently.

       NEVER THE BRAND, and that is measured rather than stylistic: the brand
       sits on --success in أخضر, on --info in نيلي and on --danger in عنّابي,
       and in ليموني dark the brand fill against --success is ΔE 13.6 — under
       the 15 floor at which full-colour vision can still tell two series
       apart (plan D12).

       CHOSEN BY SWEEPING OKLCH, not by eye, and validated against the
       tightest surface of ANY design in each theme — white in light, ليموني's
       #143a44 in dark, which is several steps lighter than the other four
       dark cards. Every check of the data-viz validator passes on all five
       cards in both themes:
         light  protan/deutan ΔE 15.1 · normal 19.1 · contrast all >= 3:1
         dark   protan/deutan ΔE 16.0 · normal 16.6 · contrast all >= 3:1
       Tritan lands at 5.3 / 3.9, below the 6–8 band, which is legal here and
       only here because the chart carries THREE secondary encodings: a
       legend, a fixed bar ORDER inside every month's group, and the
       visually-hidden table twin that names every figure. Re-run the
       validator before changing a value — and re-run it on all five cards,
       because a shared value lands on every one of them. */
    --chart-1: #2f6fe4;              /* المفوتر */
    --chart-2: #00968f;              /* المحصّل */
    --chart-3: #c85a00;              /* المصروفات */

    /* ===========================================
       Top bar
       ===========================================
       Renamed from --navbar-height with the gradient navbar it was measured
       for: the bar is .app-topbar now, painted --topbar-bg by the design, and
       a token named after a class that no longer exists would lie. Its two
       companions, --navbar-bg and --navbar-text, are GONE — the bar takes its
       colours from the design's chrome tokens, and the white-on-gradient
       rules they fed were deleted rather than recoloured.

       Read by the rail's brand block (which is exactly this tall, so its
       bottom edge lines up with the bar's) and by #cpCaseHub's anchor offset
       in _admin.css. */
    --topbar-height: 64px;

    /* ===========================================
       Spacing & Sizing
       =========================================== */
    /* Fixed base font/spacing. The 2K rollout uses container width for more
       horizontal space; fonts/paddings stay at standard sizes so admin pages
       do NOT look zoomed-in on 2K/4K monitors. */
    --base-font-size: 15px;
    --base-spacing: 1rem;

    /* Border Radius — the SCALE a design picks from, never per design (D9) */
    --radius-xs: 4px;
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 10px;
    --radius-xl: 12px;
    --radius-2xl: 16px;
    --radius-pill: 24px;
    --radius-circle: 50%;
    /* One radius per KIND of element, picked from the scale above and shared
       by every design: the four designs are one design in shapes and sizes,
       and only their colours differ. A design file that set one of these
       fails DesignTokensTest.

       This line is where all four are decided.

       --radius-btn is 8px and NOT the sheets' pill. The sheets draw a pill,
       the plan asked for one (§4.4, §4.5, §4.8), it was applied on 2026-09-16
       and the owner rejected it on sight the same day — «I did not like the
       shape», with the pill and the 8px button side by side. The 8px button is
       the shape the app ships. **Do not restore the pill from the plan**: §5
       of plans/switchable-designs-plan.md records this as a deliberate
       departure from the sheets, and the plan's own radius lines now say 8px.

       --radius-card and --radius-modal ARE the sheets' values (10px each);
       they moved in the same change and were not part of what was rejected.

       --radius-icon-btn is its own decision, not a smaller --radius-btn: an
       icon button in an actions cell stays square-cornered, and it resolves to
       the same step as .btn-sm today by coincidence rather than by design —
       see _admin.css, where the actions cell reads this token by name. */
    --radius-btn: var(--radius-md);
    --radius-icon-btn: var(--radius-sm);
    --radius-field: var(--radius-md);
    --radius-card: var(--radius-lg);
    --radius-modal: var(--radius-lg);
    /* The active nav item is a FILLED block inset from the rail's edges now,
       so it is rounded. It held 0 through phase 1, when the marker was a 3px
       border on a square row and "today's value" was no radius at all. */
    --radius-nav: var(--radius-md);
    --radius-chip: var(--radius-md);

    /* The active nav item's gradient runs toward the reading direction's END,
       which CSS gradients cannot express logically. One variable per
       direction, the same shape the mobile drawer's transform uses — a
       direction rule that set the gradient itself would outrank the state
       rules that need to. */
    --nav-active-dir: to left;

    /* ===========================================
       Shadows
       =========================================== */
    /* Cooled and slightly deepened: pure black at 0.10 alpha is invisible on a
       light field, which is why stat cards had a computed box-shadow of none
       in all but name and relied on the hairline for their whole edge. */
    --shadow: rgba(16, 24, 40, 0.10);
    --shadow-hover: rgba(16, 24, 40, 0.16);
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-sm: 0 1px 3px var(--shadow);
    --shadow-md: 0 4px 12px var(--shadow);
    --shadow-lg: 0 8px 24px var(--shadow);
    --shadow-xl: 0 20px 60px rgba(0, 0, 0, 0.3);
    /* DERIVED from the design's own --primary-rgb rather than a literal copy
       of it. It used to be a hardcoded rgba(90, 95, 208, …) kept "in step" by
       hand; with the brand colour now living in a design file, a literal here
       would ring every design's buttons in violet. */
    --shadow-primary: 0 2px 8px rgba(var(--primary-rgb), 0.25);
    --shadow-primary-hover: 0 4px 12px rgba(var(--primary-rgb), 0.35);

    /* ===========================================
       Transitions
       =========================================== */
    --transition-fast: 0.15s ease;
    --transition-normal: 0.2s ease;
    --transition-slow: 0.3s ease;
    --transition-bounce: 0.25s cubic-bezier(0.4, 0, 0.2, 1);

    /* ===========================================
       Z-Index Scale
       =========================================== */
    --z-dropdown: 1000;
    --z-sticky: 1020;
    --z-fixed: 1030;
    --z-modal-backdrop: 1040;
    --z-modal: 1050;
    --z-popover: 1060;
    --z-tooltip: 1070;
    --z-toast: 9999;
}

/* The gradient runs toward the reading direction's end in both directions —
   see --nav-active-dir above. */
html[dir="ltr"] {
    --nav-active-dir: to right;
}

/* ===========================================
   Dark theme — the SHARED half
   ===========================================
   Only tokens every design shares. The surfaces, text, borders, brand and
   chrome for the dark theme live in each design's own dark rule. */
[data-theme="dark"] {
    color-scheme: dark;

    /* Status Colors */
    --success: #34d399;
    /* Pairs with --success above, for the same reason --danger-rgb does below:
       without it the dark theme kept the LIGHT --success-rgb (16,185,129) while
       --success itself changed, so the valid-input focus ring was drawn in a
       green that did not match the border it surrounded. */
    --success-rgb: 52, 211, 153;
    --danger: #f87171;
    /* Pairs with --danger above. Without this the dark theme inherited the
       LIGHT --danger-rgb (239,68,68) while --danger itself changed, so every
       rgba(var(--danger-rgb), …) glow — the invalid-input focus ring, the
       multi-select invalid state, the form stepper's invalid step — was drawn
       in a red that did not match the border it surrounded. */
    --danger-rgb: 248, 113, 113;
    --warning: #fbbf24;
    /* Pairs with --warning above, same reason as the two pairs before it:
       without it rgba(var(--warning-rgb), …) — the multi-select advisory
       (.has-warning) focus ring — kept the LIGHT amber under a dark border. */
    --warning-rgb: 251, 191, 36;
    --info: #60a5fa;

    /* The button shades, dark side — see the light block. Fills stay the same
       hex (text-on-fill does not change with the theme).
       ─────────────────────────────────────────────────────────────────────
       THE -text SHADES WERE MEASURED ON ONE DARK CARD AND THERE ARE NOW FOUR.
       They used to be `var(--danger)` and friends, and the figures beside them
       (#f87171 6.16:1, #60a5fa 6.7:1) were taken on #1a1a2e — the only dark
       card the app had in 2026-09-12. A design owns its surfaces, so the SAME
       shared shade now lands on four of them, and lime's dark card (#143a44,
       a teal several steps lighter than the other three) is the binding one:
       there --danger read 4.42:1 as an outline button's rest colour and
       3.73:1 in an alert, and --info 4.81:1 and 4.01:1. Both under the floor,
       in one design only, which is exactly the failure a shared value can hide.

       So the two that failed are now shades of their own, lightened along
       their own hue until the WORST of the four cards clears 4.8:1 — and the
       base --danger / --info above did NOT move, because the owner asked for
       the status colours themselves to stay as drawn («اترك ألوان الحالات كما
       هي»). That is the whole point of a -text token sitting beside a status
       token: the signal keeps its colour, the text gets a readable one.
       --success and --warning still clear every card as they are (6.36:1 and
       7.32:1 on the tightest), so they keep pointing at the base token — a
       shade of their own would claim a problem they do not have.
       Re-measure all four if any design's dark --card is ever lightened:
       scratchpad phase4/status-on-surfaces.js is the sweep. */
    --danger-fill: #dc2626;
    --danger-fill-hover: #b91c1c;
    --danger-text: #fa9595;          /* 5.70:1 on lime's card, 7.46:1 on violet's */
    --success-fill: #047857;
    --success-fill-hover: #065f46;
    --success-text: var(--success);  /* 6.36:1 on the tightest dark card */
    --info-fill: #2563eb;
    --info-fill-hover: #1d4ed8;
    --info-text: #88bcfb;            /* 6.19:1 on lime's card, 8.10:1 on violet's */
    --warning-fill: var(--warning);
    --warning-fill-hover: #d97706;
    --warning-text: var(--warning);  /* 7.32:1 on the tightest dark card */
    --on-warning: #1f2937;

    /* The chart series' dark steps. NOT the light ones lightened by eye: the
       validator's dark band is OKLCH L 0.48–0.67, a full step narrower than
       light's 0.43–0.77, and a colour light enough to clear 3:1 on ليموني's
       #143a44 usually lands outside it — the two constraints pull against
       each other, which is why these were swept rather than picked. */
    --chart-1: #0088ef;
    --chart-2: #00a69f;
    --chart-3: #d65e00;

    /* Dark shadows */
    --shadow: rgba(0, 0, 0, 0.5);
    --shadow-hover: rgba(0, 0, 0, 0.7);
    --shadow-primary: 0 2px 8px rgba(var(--primary-rgb), 0.3);
    --shadow-primary-hover: 0 4px 12px rgba(var(--primary-rgb), 0.4);
}
