/* ============================================================
   FinOS Design Tokens — single canonical palette
   ------------------------------------------------------------
   ONE source of truth for both the shadcn Ui kit (hsl(var(--background))…)
   and the admin design system (portal.css aliases --bg/--panel/… to these).

   Theme switch: MainLayout sets data-theme="dark|light" on .admin-shell,
   so BOTH the class hook (.dark) AND the attribute hook ([data-theme])
   activate the same token block — the Ui kit and admin flip together.

   Palette: light = navy #1B5286, dark = navy-light #3E84C6 (WCAG AA on dark bg).
   ============================================================ */
/* Font family token — default IBM Plex Sans stack; VI locale overrides below. */
:root {
  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system,
    BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans",
    sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
    "Noto Color Emoji";
}

/* ============================================================
   Z-index scale (#1373 C-11) — canonical layer order, DOCUMENTED HERE ONLY
   ------------------------------------------------------------
   No scale existed before this: components picked ad-hoc numbers (60, 80,
   1000, 1100, 1150, 1200, 2000, 9999…), so a popup opened from inside a
   modal (60) rendered UNDER the modal (1000) and was unreachable — the bug
   this scale fixes. Not theme-dependent, so one definition, no light/dark
   split.
   <p>
   Every NEW stacked-UI component should sit on one of these tokens rather
   than inventing a literal. This run migrates only the two components that
   collided (UiModal, UrlSourceField's popup) — the rest of the ad-hoc
   values found by the #1373 sweep are listed in the PR description as
   out-of-scope follow-up, not silently left inconsistent.
   ============================================================ */
:root {
  --z-sticky: 5;      /* sticky table headers/footers within a page */
  --z-dropdown: 20;    /* inline dropdown/menu, no backdrop */
  --z-modal-backdrop: 999;
  --z-modal: 1000;     /* UiModal and anything else with a full-page backdrop */
  --z-popover: 1100;   /* a popup/search panel that must render ABOVE a modal */
  --z-toast: 2000;     /* toast host — above everything except a true alert */
}

/* [exec/2257] T4 — the white-label brand colour, and its default.
   ------------------------------------------------------------
   `index.html`'s pre-paint block overwrites this with the domain's `brand.json` themeColor as an
   INLINE style on <html>, once it has validated the string as a #RGB/#RRGGBB literal and put it
   through the 4.5:1 contrast guard. An inline style beats this rule, so what is written here is
   only what a visitor with no brand folder, no themeColor, or a malformed one gets: the product's
   own primary — which is exactly T1's "the brand style still renders".

   Declared on :root and NOWHERE ELSE, deliberately. `.admin-shell` matches `[data-theme="light"]`
   and `[data-theme="dark"]` below, so a second `--brand` declaration inside either of those blocks
   would land on the shell and shadow the value inherited from <html> — the brand colour would
   silently become the product accent on every branded host. If you need a per-theme brand value,
   derive it (see portal.css's --sb-* block), never re-declare --brand.

   hsl(var(--primary)) and not the admin `--accent` alias: --accent is declared on `.admin-shell`
   (portal.css), so it is not in scope here on <html>; --primary is, and the two are the same colour
   by construction (`--accent: hsl(var(--primary))`). */
:root {
  --brand: hsl(var(--primary));
}

/* Media scrims — theme-independent: they sit on top of photos/video, whose
   colours do not follow the theme, so light and dark share one value. */
:root {
  --scrim: rgba(0, 0, 0, 0.6);
  --scrim-soft: rgba(0, 0, 0, 0.45);
  --scrim-strong: rgba(0, 0, 0, 0.65);
  --on-scrim: #ffffff;
}

/* Vietnamese locale: prefer Be Vietnam Pro, fall back to the default stack. */
html[lang="vi"] {
  --font-sans: "Be Vietnam Pro", "IBM Plex Sans", ui-sans-serif, system-ui,
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
    "Segoe UI Symbol", "Noto Color Emoji";
}

:root,
[data-theme="light"] {
  color-scheme: light;

  /* shadcn core — light */
  --background: 210 30.8% 94.9%;        /* admin --bg  #eef2f6 */
  --foreground: 215 18.2% 12.9%;        /* admin --text #1b2027 */
  --card: 210 40% 98%;                  /* admin --panel #f8fafc */
  --card-foreground: 216 18.5% 10.6%;
  --popover: 0 0% 100%;
  --popover-foreground: 216 18.5% 10.6%;
  --primary: 210 66% 32%;               /* admin --accent #1B5286 navy */
  /* Fill behind white text (buttons, active tab). Equal to --primary here; dark overrides it. */
  --primary-fill: 210 66% 32%;
  /* #793: always white in BOTH themes — only for content ON TOP OF a --primary/--accent
     background (button text, badge text…). Using it on card/page backgrounds goes
     invisible in light theme. Use --foreground/--muted-foreground there instead. */
  --primary-foreground: 0 0% 100%;      /* admin --accent-fg */
  --secondary: 212.7 31.4% 93.1%;       /* admin --panel-2 #e8edf3 */
  --secondary-foreground: 216 18.5% 10.6%;
  --muted: 210 16.7% 95.3%;
  --muted-foreground: 216 9.1% 43.1%;   /* admin --text-muted #646c78 */
  --accent: 210 16.7% 95.3%;
  --accent-foreground: 216 18.5% 10.6%;
  --destructive: 0 72.2% 50.6%;         /* admin --danger #dc2626 */
  --destructive-foreground: 0 0% 100%;
  --border: 214.3 21.9% 87.5%;          /* admin --border #d8dee6 */
  --input: 213.3 13.7% 61.4%;           /* admin --control-border #8f9baa */
  --ring: 210 66% 32%;                  /* accent */
  --radius: 0.5rem;

  /* Ui-kit helper channels */
  --ui-border: 214.3 14.3% 90.4%;
  --ui-ring: 210 66% 32%;

  /* ── Semantic primitives (resolved colors) — light ──
     Layered surfaces by lightness: app bg darkest of the lights, surfaces
     lighter as they elevate (modal/popover lightest). Consumed by both the
     Ui kit and portal.css; one source of truth, no second token system. */
  --surface-1: #f8fafc;   /* cards / sidebar */
  --surface-2: #e8edf3;   /* raised / hover */
  --surface-3: #ffffff;   /* modal / popover (lightest; sits on a dimmed bg) */
  --border-subtle: #d8dee6;
  --border-strong: #c5ced9;
  --control-bg: #f8fafc;
  --control-bg-hover: #ffffff;
  --control-border: #8f9baa;
  --control-border-hover: #667386;
  --control-ring-bg: #f8fafc;
  --state-hover: #e8edf3;
  --state-selected: #E8EFF6;
  /* [exec/1241] — these three were dark-only, so in light theme the nav rail fell through to
     `var(…, --accent)` fallbacks and rendered off-palette. Derived from the light scale, not
     invented: --nav-active-bg is the accent-soft tint already used by --state-selected, the
     hover one step stronger toward --accent (#1B5286), and --icon-tile-bg the same tint as
     --accent-soft — exactly the relationship the dark block has
     (#1b3d5d / #214a70 lighter / #0d2235 == its --accent-soft). */
  --nav-active-bg: #E8EFF6;
  --nav-active-hover: #D9E4F0;
  --icon-tile-bg: #E8EFF6;
  /* [exec/1299] --nav-active-bg is a tint, not the solid --accent, so --accent-fg (white,
     built for the solid accent) reads as ~1.16:1 on it — invisible. --nav-active-fg is the
     tint's own foreground: the solid accent colour itself, read as text on its own tint
     (6.98:1 here). Dark keeps white because --nav-active-bg dark (#1b3d5d) is dark enough
     for white to already work (11.22:1) — that is a property of the dark value, not of
     --accent-fg, so it is spelled out here rather than reused from --accent-fg. */
  --nav-active-fg: #1B5286;
  --text-primary:   #1b2027;
  /* W14-A: light text tokens clear WCAG AA (4.5:1) on every light surface, including the surface-2 hover fill. */
  --text-secondary: #4b5563;
  --text-muted:     #5f6976;
  --accent-hover:   #163F66;
  --accent-soft:    #E8EFF6;
  --c-teal: #17B2AB; --c-amber: #F4B01E; --c-coral: #EF4136;
  --success: #16a34a; --success-soft: #e8f6ec;
  --warning: #d97706; --warning-soft: #fdf1e1;
  --danger:  #dc2626; --danger-soft:  #fdecec;
  /* [exec/2289] T1 — --info gets its OWN blue. It used to be the primary navy (#1B5286), so an
     "informational" banner and a primary action were the same colour. #1D4ED8 is a saturated royal
     blue, clearly apart from the navy, 5.8:1 on --info-soft and 5.96:1 for --bg text on it. */
  --info:    #1D4ED8; --info-soft:    #E6EEFD;

  /* [exec/2396] Chart series palette (ADR-111 D8; docs/dashboard-org-research.md section 6.5).
     The slot ORDER is the colour-vision-deficiency safety mechanism: the seven hues were validated
     as a sequence, so a chart must take them in order and never skip or reshuffle. Colour follows
     the ENTITY, never its rank: a class keeps its slot when the sort order changes. Status tones
     (--success/--warning/--danger) are never series colours, and a series colour never carries
     meaning alone (always a visible label and a table view). Light slots 3, 4 and 5 are below 3:1
     on --surface-1 (2.69 / 2.07 / 2.57 respectively), which is exactly why the labels and table are mandatory. */
  --chart-1: #2a78d6;
  --chart-2: #eb6834;
  --chart-3: #1baf7a;
  --chart-4: #eda100;
  --chart-5: #e87ba4;
  --chart-6: #008300;
  --chart-7: #4a3aa7;

  /* [exec/2589] Soft tint per slot: literal colours (no color-mix on Chromium 85). Used for projected
     segments, KPI hover and the donut hover ring; never as the only carrier of meaning. */
  --chart-1-soft: #d7e5f6;
  --chart-2-soft: #f6e3dc;
  --chart-3-soft: #d5eee7;
  --chart-4-soft: #f6ecd4;
  --chart-5-soft: #f5e6ee;
  --chart-6-soft: #d0e7d4;
  --chart-7-soft: #dcdbee;
  /* [exec/2418] Empty-track fill for rings, stacked bars and bar lists. --surface-2 was
     indistinguishable from the card in dark. Light uses a literal one step darker than
     --border-subtle (#d8dee6 is only 1.29:1 on --surface-1; this is 1.35:1, floor 1.3:1). */
  --chart-track: #d3dae3;
  --chart-muted: var(--text-muted);
  /* [exec/2418] Fill of the NON-highlighted bars: --primary at reduced alpha (no color-mix on
     Chromium 85, ADR-138). Reuse for any 'de-emphasised series' fill, e.g. UiLineChart (exec/2422). */
  --chart-bar-muted: hsl(var(--primary) / .4);

  /* [exec/2289] T1 — category colours (notification / feed icon circles). One hue per meaning:
     announce violet, finance green, health red, attendance teal, meal amber, survey blue, other slate.
     --on-cat is the glyph drawn ON the solid fill. Light fills are the 700-weight of each hue rather
     than the brighter --c-teal/--c-amber/--success, because a white glyph on #17B2AB is 2.5:1 and on
     #F4B01E 1.8:1; every pair below is >= 4.5:1 (CategoryTokensContrastTests computes it from this
     file). -soft is the quiet tint of the hue on the card surface (--surface-3). */
  --cat-announce:   #7C3AED;
  --cat-finance:    #15803D;
  --cat-health:     #DC2626;
  --cat-attendance: #0F766E;
  --cat-meal:       #B45309;
  --cat-survey:     #2563EB;
  --cat-other:      #475569;
  --on-cat:         #ffffff;
  --cat-announce-soft:   color-mix(in srgb, var(--cat-announce) 14%, var(--surface-3));
  --cat-finance-soft:    color-mix(in srgb, var(--cat-finance) 14%, var(--surface-3));
  --cat-health-soft:     color-mix(in srgb, var(--cat-health) 14%, var(--surface-3));
  --cat-attendance-soft: color-mix(in srgb, var(--cat-attendance) 14%, var(--surface-3));
  --cat-meal-soft:       color-mix(in srgb, var(--cat-meal) 14%, var(--surface-3));
  --cat-survey-soft:     color-mix(in srgb, var(--cat-survey) 14%, var(--surface-3));
  --cat-other-soft:      color-mix(in srgb, var(--cat-other) 14%, var(--surface-3));

  /* [exec/2666] Workflow group identity: ONE colour per node group (Khi / Lấy / Rẽ nhánh & chờ / Duyệt / Thì), derived from the status hues so it follows
     both themes, and used for the same group on the simple-view card bar, the marketplace card placeholder and the runs step list. Never the only
     signal: the group name and its SVG icon always sit beside it. -soft is the quiet tint on the card surface. Used as a fill, bar or icon
     colour; label text stays --text-primary, because the warning hue alone is under 4.5:1 as text on white (AA). */
  --wf-when:     var(--info);
  --wf-get:      hsl(var(--primary));
  --wf-control:  var(--warning);
  --wf-approval: var(--success);
  --wf-action:   var(--danger);
  --wf-when-soft:     color-mix(in srgb, var(--wf-when) 14%, var(--surface-3));
  --wf-get-soft:      color-mix(in srgb, var(--wf-get) 14%, var(--surface-3));
  --wf-control-soft:  color-mix(in srgb, var(--wf-control) 16%, var(--surface-3));
  --wf-approval-soft: color-mix(in srgb, var(--wf-approval) 14%, var(--surface-3));
  --wf-action-soft:   color-mix(in srgb, var(--wf-action) 14%, var(--surface-3));

  /* [exec/2289] T3 — the Family page ground: a neutral light grey so white cards read as raised. */
  --surface-page: #f0f2f5;

  /* [exec/2290] T6 — the four assessment rating levels (Not yet / Emerging / Developing / Secure).
     Each level has three tokens: --rating-X (the solid fill of the ACTIVE control and the outline of an
     inactive one), --rating-X-fg (the label ON that solid fill) and --rating-X-soft (a quiet tint for
     hover / mini bars). Kept apart from --success/--warning/--info on purpose: those mean "status of a
     record", these mean "how a child is doing", and the two must be free to move independently.
     WCAG AA (>= 4.5:1), checked by RatingTokensContrastTests against this very file:
       light  fg on fill   7.6 / 5.8 / 5.9 / 7.1   fill on --surface-1 (outline / text)  7.2 / 5.6 / 5.7 / 6.8
       dark   fg on fill   7.3 / 10.9 / 7.1 / 9.8  fill on --surface-1 (outline / text)  7.4 / 11.4 / 7.5 / 10.9 */
  --rating-notyet:      #475569; --rating-notyet-fg:      #ffffff; --rating-notyet-soft:      #eceff3;
  --rating-emerging:    #a8480a; --rating-emerging-fg:    #ffffff; --rating-emerging-soft:    #fef6ea;
  --rating-developing:  #0369a1; --rating-developing-fg:  #ffffff; --rating-developing-soft:  #e3f1fa;
  --rating-secure:      #166534; --rating-secure-fg:      #ffffff; --rating-secure-soft:      #e6f5ec;

  /* [exec/2233] T5/T8 — two closed, INDEX-ADDRESSED palettes.
     ------------------------------------------------------------
     Both are identity colours, not surfaces, which is why they are the only tokens in this file
     that do NOT change between light and dark: a label the school painted red is the same label in
     either theme, and an avatar whose colour flipped when somebody toggled the theme would stop
     being a recognition aid at all. They are discs and pills with their own foreground
     (--tag-fg / --avatar-fg), so they never have to contrast against the page.

     --tag-0..7 is the inbox label palette. The byte that travels on the wire (InboxTagRow.Color,
     0-7) is an INDEX into this list, so the order is a contract, not a preference —
     InboxTagPaletteSingleSourceTests fails if these drift from InboxPanel.razor's TagPaletteHex,
     because picking the green swatch must not save a tag that renders teal.

     [exec/2239] T4b — the eight values are DARKER than the 500-weight set they replace, for one
     reason: the chip is a pill with the tag's NAME written on it in --tag-fg (#ffffff), and white
     on the old palette measured 1.92:1 on --tag-2, 2.28 on --tag-3, 2.49 on --tag-4, 2.80 on
     --tag-1, 3.68/3.76/3.96 on --tag-5/0/6 and 4.83 on --tag-7 — seven of eight below AA's 4.5:1
     for body text, on a chip whose type is 11px. Nobody saw it while the colour rules were dead
     (see InboxPanel.razor.css), which is exactly why it is being stated here now that they are
     live. Every entry clears 4.5:1 against --tag-fg — measured 4.62 at the weakest (--tag-2),
     4.76, 4.83, 5.02, 5.02, 5.17, 5.38, 5.47 — the same claim --avatar-* below already makes, and
     the values were chosen to maximise the SMALLEST CIE76 distance between any two of them
     (30.8, against 34.6 for the old set), because telling one label from another is the whole job
     of an index-addressed palette.

     --avatar-1..8 is UiAvatar's initials-disc palette, chosen by a stable FNV-1a hash of the
     subject's id so one person keeps one colour across every screen and every session. Every entry
     clears 4.5:1 against --avatar-fg (measured: 5.02 at the weakest, #B45309).
     ------------------------------------------------------------ */
  --tag-0: #dc2626; --tag-1: #b45309; --tag-2: #92700a; --tag-3: #15803d;
  --tag-4: #0f766e; --tag-5: #2563eb; --tag-6: #9333ea; --tag-7: #64748b;
  /* [exec/2259] T4 — --tag-8 is PINK, appended because InboxTagColor.Pink is enum value 8: the
     offered palette became Zalo's eight (red, pink, orange, yellow, green, teal, blue, purple) and
     pink was the one this house palette did not have. It is appended rather than slotted next to
     --tag-0 where it belongs visually, because the index IS the wire byte and is persisted.
     #db2777 measures 4.60:1 against --tag-fg (#ffffff) — above AA's 4.5:1, in line with the rest of
     the palette (4.62 at the weakest). --tag-7 (gray) stays defined and is no longer offered in the
     picker; labels already carrying it still paint. */
  --tag-8: #db2777;
  --tag-fg: #ffffff;
  --avatar-1: #1B5286; --avatar-2: #0F766E; --avatar-3: #B45309; --avatar-4: #9F1239;
  --avatar-5: #5B21B6; --avatar-6: #166534; --avatar-7: #7C2D12; --avatar-8: #334155;
  --avatar-fg: #ffffff;
  --radius:    0.5rem;
  --radius-sm: calc(var(--radius) - 2px);
  --radius-lg: 14px;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px;
  --shadow:    0 1px 2px rgba(16, 24, 40, 0.05), 0 1px 3px rgba(16, 24, 40, 0.08);
  --shadow-lg: 0 12px 30px rgba(16, 24, 40, 0.12);
  --focus-ring: 0 0 0 2px var(--control-ring-bg), 0 0 0 4px hsl(var(--ui-ring) / 0.55);

  /* Sidebar (FinOS specific) */
  --sidebar-background: 210 66% 14%;
  --sidebar-foreground: 210 40% 98%;
  --sidebar-primary: 210 55% 51%;
  --sidebar-primary-foreground: 0 0% 100%;
  --sidebar-accent: 210 50% 20%;
  --sidebar-border: 210 50% 20%;
  --sidebar-ring: 210 55% 51%;
}

.dark,
[data-theme="dark"] {
  color-scheme: dark;

  /* shadcn core — dark (near-black + navy-light accent) */
  --background: 210 16.7% 2.4%;         /* admin --bg  #050607 */
  --foreground: 212.7 20.8% 90%;        /* admin --text #e0e5eb (off-white) */
  --card: 216 21.7% 6.1%;               /* admin --panel #0d1014 */
  --card-foreground: 216 11.6% 91.6%;
  --popover: 210 8.7% 9%;
  --popover-foreground: 216 11.6% 91.6%;
  --primary: 207.8 48.5% 48%;           /* admin --accent #3f83b6 */
  /* Filled controls only: #2e6a9e gives white text 5.7:1 (WCAG AA). --primary stays light because it is also text on dark. */
  --primary-fill: 208 55% 40%;
  /* #793: always white in BOTH themes — only for content ON TOP OF a --primary/--accent
     background. Using it on card/page backgrounds goes invisible in light theme. */
  --primary-foreground: 0 0% 100%;      /* admin --accent-fg white */
  --secondary: 216 21.4% 8.2%;          /* admin --panel-2 #101419 */
  --secondary-foreground: 210 17% 91.6%;
  --muted: 216 15% 17.1%;
  --muted-foreground: 216.7 9.8% 63.9%; /* admin --text-muted #9aa1ac */
  --accent: 210 8.3% 14.1%;             /* admin --panel-hover #212427 */
  --accent-foreground: 216 11.6% 91.6%;
  --destructive: 0 90.6% 70.8%;         /* admin --danger #f87171 */
  --destructive-foreground: 36 62.5% 6.3%;
  --border: 213.3 17.6% 20%;            /* admin --border #2a333c */
  --input: 213.3 12.8% 41.4%;           /* admin --control-border #5c6877 */
  --ring: 210 55% 51%;                  /* accent */

  --ui-border: 213.3 17.6% 20%;
  --ui-ring: 210 55% 51%;

  /* ── Semantic primitives — dark ──
     Elevation by lightness and state tokens:
     bg #050607 < surface-1 #0d1014 < surface-2 #101419 < surface-3 #181e25. */
  --surface-1: #0d1014;   /* cards / sidebar */
  --surface-2: #101419;   /* raised / hover */
  --surface-3: #181e25;   /* modal / popover / active controls */
  --border-subtle: #2a333c;
  --border-strong: #343f4a;
  --control-bg: #11161c;
  --control-bg-hover: #181e25;
  --control-border: #5c6877;
  --control-border-hover: #657385;
  --control-ring-bg: #0d1014;
  --state-hover: #101419;
  --state-selected: #10263a;
  --nav-active-bg: #1b3d5d;
  --nav-active-hover: #214a70;
  --icon-tile-bg: #0d2235;
  --nav-active-fg: #ffffff; /* [exec/1299] paired with --nav-active-bg; see light block for why */
  --text-primary:   #e0e5eb;
  --text-secondary: #9aa4b0;
  --text-muted:     #8a96a6;   /* W14-A: was #6f7a88, which fell to 3.9:1 on the raised surface-3 */
  --accent-hover:   #5b9bcf;
  --accent-soft:    #0d2235;
  --c-teal: #2DD4BF; --c-amber: #FBBF24; --c-coral: #F87171;
  --success: #4ade80; --success-soft: #14271a;
  --warning: #fbbf24; --warning-soft: #2a200d;
  --danger:  #f87171; --danger-soft:  #3a1d1d;
  --info:    #60A5FA; --info-soft:    #0e1e30;

  /* [exec/2396] Chart series palette (ADR-111 D8; docs/dashboard-org-research.md section 6.5).
     The slot ORDER is the colour-vision-deficiency safety mechanism: the seven hues were validated
     as a sequence, so a chart must take them in order and never skip or reshuffle. Colour follows
     the ENTITY, never its rank: a class keeps its slot when the sort order changes. Status tones
     (--success/--warning/--danger) are never series colours, and a series colour never carries
     meaning alone (always a visible label and a table view). Light slots 3, 4 and 5 are below 3:1
     on --surface-1 (2.69 / 2.07 / 2.57 respectively), which is exactly why the labels and table are mandatory. */
  --chart-1: #3987e5;
  --chart-2: #d95926;
  --chart-3: #199e70;
  --chart-4: #c98500;
  --chart-5: #d55181;
  --chart-6: #008300;
  --chart-7: #9085e9;

  /* [exec/2589] Soft tint per slot: literal colours (no color-mix on Chromium 85). Used for projected
     segments, KPI hover and the donut hover ring; never as the only carrier of meaning. */
  --chart-1-soft: #182d46;
  --chart-2-soft: #3e2218;
  --chart-3-soft: #10322a;
  --chart-4-soft: #3a2c0f;
  --chart-5-soft: #3d202e;
  --chart-6-soft: #0a2c0f;
  --chart-7-soft: #2c2c47;
  /* [exec/2418] Empty-track fill, see the light block. --border-subtle #2a333c is 1.49:1 on --surface-1. */
  --chart-track: var(--border-subtle);
  --chart-muted: var(--text-muted);
  /* [exec/2418] De-emphasised bar fill, see the light block. */
  --chart-bar-muted: hsl(var(--primary) / .5);

  /* [exec/2289] T1 — dark category fills: the light 300/400-weights, with a DARK glyph (--on-cat)
     because white on these is 2.7:1 or worse. Finance is --success (#4ade80). */
  --cat-announce:   #A78BFA;
  --cat-finance:    #4ADE80;
  --cat-health:     #F87171;
  --cat-attendance: #2DD4BF;
  --cat-meal:       #FBBF24;
  --cat-survey:     #60A5FA;
  --cat-other:      #94A3B8;
  --on-cat:         #0b1220;
  --cat-announce-soft:   color-mix(in srgb, var(--cat-announce) 14%, var(--surface-3));
  --cat-finance-soft:    color-mix(in srgb, var(--cat-finance) 14%, var(--surface-3));
  --cat-health-soft:     color-mix(in srgb, var(--cat-health) 14%, var(--surface-3));
  --cat-attendance-soft: color-mix(in srgb, var(--cat-attendance) 14%, var(--surface-3));
  --cat-meal-soft:       color-mix(in srgb, var(--cat-meal) 14%, var(--surface-3));
  --cat-survey-soft:     color-mix(in srgb, var(--cat-survey) 14%, var(--surface-3));
  --cat-other-soft:      color-mix(in srgb, var(--cat-other) 14%, var(--surface-3));

  /* [exec/2666] Workflow group identity: ONE colour per node group (Khi / Lấy / Rẽ nhánh & chờ / Duyệt / Thì), derived from the status hues so it follows
     both themes, and used for the same group on the simple-view card bar, the marketplace card placeholder and the runs step list. Never the only
     signal: the group name and its SVG icon always sit beside it. -soft is the quiet tint on the card surface. Used as a fill, bar or icon
     colour; label text stays --text-primary, because the warning hue alone is under 4.5:1 as text on white (AA). */
  --wf-when:     var(--info);
  --wf-get:      hsl(var(--primary));
  --wf-control:  var(--warning);
  --wf-approval: var(--success);
  --wf-action:   var(--danger);
  --wf-when-soft:     color-mix(in srgb, var(--wf-when) 14%, var(--surface-3));
  --wf-get-soft:      color-mix(in srgb, var(--wf-get) 14%, var(--surface-3));
  --wf-control-soft:  color-mix(in srgb, var(--wf-control) 16%, var(--surface-3));
  --wf-approval-soft: color-mix(in srgb, var(--wf-approval) 14%, var(--surface-3));
  --wf-action-soft:   color-mix(in srgb, var(--wf-action) 14%, var(--surface-3));

  /* [exec/2289] T3 — dark page ground = the app background; cards rise by --surface-3, not shadow. */
  --surface-page: #050607;

  /* [exec/2290] T6 — dark counterparts of the rating levels: lighter fills with dark labels, so the
     active state is still a solid block with AA text. See the light block for the contrast figures. */
  --rating-notyet:      #94a3b8; --rating-notyet-fg:      #0b1220; --rating-notyet-soft:      #1c2430;
  --rating-emerging:    #fbbf24; --rating-emerging-fg:    #1f1300; --rating-emerging-soft:    #2a200d;
  --rating-developing:  #60a5fa; --rating-developing-fg:  #06162b; --rating-developing-soft:  #0e1e30;
  --rating-secure:      #4ade80; --rating-secure-fg:      #04210f; --rating-secure-soft:      #14271a;

  /* [exec/2233] T5/T8 — restated verbatim so a reader of the dark block is not left wondering
     whether the palettes were forgotten here. They are identity colours and do not flip; see the
     light block for the full reasoning.
     [exec/2239] T4b — because they do not flip, the contrast measurement in the light block is the
     measurement for BOTH themes: --tag-fg on --tag-N is the same pair of colours either way. */
  --tag-0: #dc2626; --tag-1: #b45309; --tag-2: #92700a; --tag-3: #15803d;
  --tag-4: #0f766e; --tag-5: #2563eb; --tag-6: #9333ea; --tag-7: #64748b;
  /* [exec/2259] T4 — same pink as the light block; a label colour is an identity, not a surface. */
  --tag-8: #db2777;
  --tag-fg: #ffffff;
  --avatar-1: #1B5286; --avatar-2: #0F766E; --avatar-3: #B45309; --avatar-4: #9F1239;
  --avatar-5: #5B21B6; --avatar-6: #166534; --avatar-7: #7C2D12; --avatar-8: #334155;
  --avatar-fg: #ffffff;
  --shadow:    0 1px 2px rgba(0, 0, 0, 0.50), 0 8px 18px rgba(0, 0, 0, 0.28);
  --shadow-lg: 0 18px 52px rgba(0, 0, 0, 0.66);
}

/* ============================================================
   Breakpoint convention (#961) — canonical widths, DOCUMENTED LITERALS ONLY
   ------------------------------------------------------------
   CSS custom properties are NOT usable inside @media conditions — a rule
   like `@media (max-width: var(--bp-mobile))` is invalid, the whole block
   is silently dropped, and the page renders exactly as if the rule were
   never written (no console error, no visual difference from "not done").
   Do not attempt it. Every @media rule in the codebase must repeat one of
   the literal pixel values below verbatim; this comment block is the
   single source of truth for what each number means and is not itself
   consumed by any stylesheet.

   Evidence (grep across wasm/FinOS.Wasm.Portal/Pages, 2026-08-05):
   4 × max-width:560px, 3 × max-width:640px, 1 × max-width:768px,
   1 × max-width:700px, 1 × max-width:600px, 1 × prefers-reduced-motion.
   Two widths (560, 640) already carry almost all existing usage, and 768
   appears once as a tablet step — it is also the fixed threshold
   Ui/UiTable.razor.css already uses for its table→card fold (#559), so it
   is the natural third value rather than a new invention. Canonicalizing
   on exactly these three changes no page that already used one of them
   and gives every future file three names instead of a sixth ad-hoc
   number.

     560px — phone   : tightest mobile layout — stacked controls, single
                        column, wrapped/scrolled filter and action rows.
     640px — mobile  : general "small screen" cutoff — most existing
                        max-width:640px rules already land here; use for
                        any mobile change that isn't 560's tightest case.
     768px — tablet  : two-column→one-column / table→card-list step;
                        matches UiTable's card-mode threshold so a page's
                        own layout and its embedded tables fold together.

   These names exist for readability only, in a same-line comment next to
   the literal — write the label inside a comment alongside the media query.
   Do not introduce a fourth value without updating this block.
   ============================================================ */

/* ── Base control theming ────────────────────────────────────────
   Raw <input>/<select>/<textarea> not yet on the Ui kit still pick up
   the active theme (background/foreground/border) so nothing renders
   white-on-white in dark. The Ui kit components override these with
   their own .ui-input / .ad-input rules. */
input,
select,
textarea {
  background-color: var(--control-bg);
  color: hsl(var(--foreground));
  border-color: var(--control-border);
}
