/* =====================================================================
 * FlexOps Unified Design Tokens
 * =====================================================================
 * Single source of truth for all FlexOps Blazor portals.
 * Import this file in every portal's index.html or App.razor BEFORE
 * the portal's own theme.css.
 *
 * Naming convention: --flexops-{category}-{variant}
 * Categories: brand, gray, error, warning, success, blue, text, bg,
 *             border, fg, font, radius, shadow, space, transition,
 *             track, line-height
 *
 * Aligned with the Next.js marketing site (src/styles/theme.css).
 * =====================================================================
 * Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
 * =================================================================== */

:root {
  /* =====================================================================
   * PALETTE PROVENANCE — read before "migrating" any colour.
   *
   * Brand, gray and the semantic families (error / warning / success) are a
   * FlexOps-DESIGNED colour scale, authored by the co-founder and agreed. They
   * are NOT an un-migrated copy of some vendor palette, so there is nothing to
   * "catch up" to. Proposals to conform them to Tailwind or to Untitled UI v8
   * have been considered and DECLINED — see Shared#100.
   *
   * Untitled UI v8 supplies the ACCENT hues ONLY (blue, sky, indigo, purple,
   * pink, orange), adopted because no designed equivalent existed for them.
   * Those six follow Tailwind's ramp and are consistent with each other; that
   * is the deliberate boundary, not an inconsistency to resolve.
   *
   * Two facts that make a "tidy-up" expensive:
   *   - The gray is purple-tinted on purpose (#787480, B>R>G) so it sits with
   *     the brand. Tailwind's neutral #737373 is a different design.
   *   - The Next.js marketing site carries the IDENTICAL values
   *     (src/styles/theme.css: --color-brand-600: rgb(108 59 170),
   *     --color-gray-500: rgb(120 116 128)). Changing them here silently
   *     desynchronises product and website.
   *
   * --color-brand-* resolves through --flexops-brand-*, and that indirection is
   * what makes the per-portal accent possible. Never flatten it to a hex.
   * ===================================================================== */

  /* -----------------------------------------------------------------
   * Brand Purple (primary)
   * ----------------------------------------------------------------- */
  --flexops-brand-25: #FAFAFF;
  --flexops-brand-50: #F7F1FD;
  --flexops-brand-100: #F0E6FC;
  --flexops-brand-200: #E3D2F9;
  --flexops-brand-300: #CDB1F1;
  --flexops-brand-400: #B28BE4;
  --flexops-brand-500: #8D59CF;
  --flexops-brand-600: #6C3BAA;
  --flexops-brand-700: #5C368C;
  --flexops-brand-800: #4A2D6E;
  --flexops-brand-900: #382451;
  --flexops-brand-950: #27163B;

  /* -----------------------------------------------------------------
   * Gray (neutral)
   * ----------------------------------------------------------------- */
  --flexops-gray-25: #FCFCFD;
  --flexops-gray-50: #FAF9FB;
  --flexops-gray-100: #F3F1F5;
  --flexops-gray-200: #E9E6EC;
  --flexops-gray-300: #D8D4DD;
  --flexops-gray-400: #B0ABB8;
  /* Half-stop, added for --flexops-border-control. The ramp jumps 2.24:1 -> 4.56:1
     against white between 400 and 500, with nothing near the 3:1 that WCAG 1.4.11
     asks of a control boundary; -400 misses it and -500 clears it so far that every
     input reads as an outlined box. This is the midpoint of those two, keeping the
     ramp's deliberate purple bias (B > R > G), and it is the only value that clears
     3:1 on BOTH grounds: 3.12:1 on #FFFFFF and 5.52:1 on #1C1A21. An addition to
     the scale, not a re-tint of it — every existing stop is untouched. */
  --flexops-gray-450: #94909C;
  --flexops-gray-500: #787480;
  --flexops-gray-600: #635F6B;
  --flexops-gray-700: #4A4651;
  --flexops-gray-800: #2C2A33;
  --flexops-gray-900: #1C1A21;
  --flexops-gray-950: #110F15;

  /* -----------------------------------------------------------------
   * Error (red)
   * ----------------------------------------------------------------- */
  --flexops-error-25: #FFFBFA;
  --flexops-error-50: #FEF3F2;
  --flexops-error-100: #FEE4E2;
  --flexops-error-200: #FECDCA;
  --flexops-error-300: #FDA29B;
  --flexops-error-400: #F97066;
  --flexops-error-500: #F04438;
  --flexops-error-600: #D92D20;
  --flexops-error-700: #B42318;
  --flexops-error-800: #912018;
  --flexops-error-900: #7A271A;
  --flexops-error-950: #55160C;

  /* -----------------------------------------------------------------
   * Warning (amber)
   * ----------------------------------------------------------------- */
  --flexops-warning-25: #FFFCF5;
  --flexops-warning-50: #FFFAEB;
  --flexops-warning-100: #FEF0C7;
  --flexops-warning-200: #FEDF89;
  --flexops-warning-300: #FEC84B;
  --flexops-warning-400: #FDB022;
  --flexops-warning-500: #F79009;
  --flexops-warning-600: #DC6803;
  --flexops-warning-700: #B54708;
  --flexops-warning-800: #93370D;
  --flexops-warning-900: #7A2E0E;
  --flexops-warning-950: #4E1D09;

  /* -----------------------------------------------------------------
   * Success (green)
   * ----------------------------------------------------------------- */
  --flexops-success-25: #F6FEF9;
  --flexops-success-50: #ECFDF3;
  --flexops-success-100: #DCFAE6;
  --flexops-success-200: #ABEFC6;
  --flexops-success-300: #75E0A7;
  --flexops-success-400: #47CD89;
  --flexops-success-500: #17B26A;
  --flexops-success-600: #079455;
  --flexops-success-700: #067647;
  --flexops-success-800: #085D3A;
  --flexops-success-900: #074D31;
  --flexops-success-950: #053321;

  /* -----------------------------------------------------------------
   * Blue (info)
   * ----------------------------------------------------------------- */
  /* Tailwind's blue, matching the five accent ramps below. This ramp previously
     held Untitled UI's OLDER blue (#2E90FA at 500) and stopped at five stops —
     the last hue still on the pre-v8 palette while sky/indigo/purple/pink/orange
     had already moved. Completed to eleven stops because the utility-* layer's
     dark theme inverts into 800/900/950, which did not exist. */
  --flexops-blue-50: #EFF6FF;
  --flexops-blue-100: #DBEAFE;
  --flexops-blue-200: #BFDBFE;
  --flexops-blue-300: #93C5FD;
  --flexops-blue-400: #60A5FA;
  --flexops-blue-500: #3B82F6;
  --flexops-blue-600: #2563EB;
  --flexops-blue-700: #1D4ED8;
  --flexops-blue-800: #1E40AF;
  --flexops-blue-900: #1E3A8A;
  --flexops-blue-950: #172554;

  /* -----------------------------------------------------------------
   * Sky / Indigo / Purple / Pink / Orange (Untitled UI v8, Tailwind ramp)
   * Only the three stops badge rules consume: 50 (tint), 200 (border),
   * 700 (text). Fuller ramps intentionally not added — see
   * .superpowers/sdd/2026-08-07-badge-intents-and-palette/palette-values.md.
   * ----------------------------------------------------------------- */
  --flexops-sky-50: #F0F9FF;
  --flexops-sky-200: #BAE6FD;
  --flexops-sky-700: #0369A1;

  --flexops-indigo-50: #EEF2FF;
  --flexops-indigo-200: #C7D2FE;
  --flexops-indigo-700: #4338CA;

  --flexops-purple-50: #FAF5FF;
  --flexops-purple-200: #E9D5FF;
  --flexops-purple-700: #7E22CE;

  --flexops-pink-50: #FDF2F8;
  --flexops-pink-200: #FBCFE8;
  --flexops-pink-700: #BE185D;

  --flexops-orange-50: #FFF7ED;
  --flexops-orange-200: #FED7AA;
  --flexops-orange-700: #C2410C;

  /* -----------------------------------------------------------------
   * UTILITY COLOURS (Untitled UI v8)
   *
   * Chart series, badges and status tints. Untitled UI ships sixteen of
   * these families; we carry the SIX whose base ramp we own at full
   * 50..950 depth. The rest (sky, indigo, purple, pink, orange, fuchsia,
   * emerald, slate, amber) are NOT here on purpose -- each would need
   * ~8 more base values, and the badge ramps above deliberately stop at
   * the three stops those rules consume.
   *
   * To add one: complete its base ramp to 50..950 FIRST, then the family,
   * then drop it from UtilityColorTokenTests' unsupported list. Adding a
   * family whose deep stops are missing does not fail loudly -- the dark
   * theme would reference an undefined property, CSS would drop the
   * declaration, and the tint would simply not render.
   * ----------------------------------------------------------------- */
  /* brand */
  --color-utility-brand-50: var(--flexops-brand-50);
  --color-utility-brand-50_alt: var(--flexops-brand-50);
  --color-utility-brand-100: var(--flexops-brand-100);
  --color-utility-brand-100_alt: var(--flexops-brand-100);
  --color-utility-brand-200: var(--flexops-brand-200);
  --color-utility-brand-200_alt: var(--flexops-brand-200);
  --color-utility-brand-300: var(--flexops-brand-300);
  --color-utility-brand-300_alt: var(--flexops-brand-300);
  --color-utility-brand-400: var(--flexops-brand-400);
  --color-utility-brand-400_alt: var(--flexops-brand-400);
  --color-utility-brand-500: var(--flexops-brand-500);
  --color-utility-brand-500_alt: var(--flexops-brand-500);
  --color-utility-brand-600: var(--flexops-brand-600);
  --color-utility-brand-600_alt: var(--flexops-brand-600);
  --color-utility-brand-700: var(--flexops-brand-700);
  --color-utility-brand-700_alt: var(--flexops-brand-700);

  /* neutral */
  --color-utility-neutral-50: var(--flexops-gray-50);
  --color-utility-neutral-100: var(--flexops-gray-100);
  --color-utility-neutral-200: var(--flexops-gray-200);
  --color-utility-neutral-300: var(--flexops-gray-300);
  --color-utility-neutral-400: var(--flexops-gray-400);
  --color-utility-neutral-500: var(--flexops-gray-500);
  --color-utility-neutral-600: var(--flexops-gray-600);
  --color-utility-neutral-700: var(--flexops-gray-700);

  /* red */
  --color-utility-red-50: var(--flexops-error-50);
  --color-utility-red-100: var(--flexops-error-100);
  --color-utility-red-200: var(--flexops-error-200);
  --color-utility-red-300: var(--flexops-error-300);
  --color-utility-red-400: var(--flexops-error-400);
  --color-utility-red-500: var(--flexops-error-500);
  --color-utility-red-600: var(--flexops-error-600);
  --color-utility-red-700: var(--flexops-error-700);

  /* yellow */
  --color-utility-yellow-50: var(--flexops-warning-50);
  --color-utility-yellow-100: var(--flexops-warning-100);
  --color-utility-yellow-200: var(--flexops-warning-200);
  --color-utility-yellow-300: var(--flexops-warning-300);
  --color-utility-yellow-400: var(--flexops-warning-400);
  --color-utility-yellow-500: var(--flexops-warning-500);
  --color-utility-yellow-600: var(--flexops-warning-600);
  --color-utility-yellow-700: var(--flexops-warning-700);

  /* green */
  --color-utility-green-50: var(--flexops-success-50);
  --color-utility-green-100: var(--flexops-success-100);
  --color-utility-green-200: var(--flexops-success-200);
  --color-utility-green-300: var(--flexops-success-300);
  --color-utility-green-400: var(--flexops-success-400);
  --color-utility-green-500: var(--flexops-success-500);
  --color-utility-green-600: var(--flexops-success-600);
  --color-utility-green-700: var(--flexops-success-700);

  /* blue */
  --color-utility-blue-50: var(--flexops-blue-50);
  --color-utility-blue-100: var(--flexops-blue-100);
  --color-utility-blue-200: var(--flexops-blue-200);
  --color-utility-blue-300: var(--flexops-blue-300);
  --color-utility-blue-400: var(--flexops-blue-400);
  --color-utility-blue-500: var(--flexops-blue-500);
  --color-utility-blue-600: var(--flexops-blue-600);
  --color-utility-blue-700: var(--flexops-blue-700);

  /* -----------------------------------------------------------------
   * Semantic: Text
   * ----------------------------------------------------------------- */
  --flexops-text-primary: var(--flexops-gray-900);
  --flexops-text-secondary: var(--flexops-gray-700);
  --flexops-text-tertiary: var(--flexops-gray-600);
  --flexops-text-quaternary: var(--flexops-gray-500);
  --flexops-text-disabled: var(--flexops-gray-500);
  --flexops-text-placeholder: var(--flexops-gray-600);
  --flexops-text-white: #FFFFFF;
  --flexops-text-brand-primary: var(--flexops-brand-600);
  --flexops-text-brand-secondary: var(--flexops-brand-500);
  /* Status TEXT reads at the -700 stop, not -600. The ramp itself is unchanged --
     this is which step the semantic role points at, not a re-tint of a designed
     scale. At -600 none of the three clears 4.5:1 across all three light surfaces:
     success 3.91/3.72/3.48, warning 3.49/3.32/3.11, error 4.83/4.60/4.30 (white /
     gray-50 / gray-100). -700 clears every one: 5.07, 4.84 and 5.86 at worst. The
     dark theme lifts these to -300 further down; without that override the steps
     tuned for a white page would sit on #1C1A21. */
  --flexops-text-error-primary: var(--flexops-error-700);
  --flexops-text-warning-primary: var(--flexops-warning-700);
  --flexops-text-success-primary: var(--flexops-success-700);

  /* -----------------------------------------------------------------
   * Semantic: Background
   * ----------------------------------------------------------------- */
  --flexops-bg-primary: #FFFFFF;
  --flexops-bg-secondary: var(--flexops-gray-50);
  --flexops-bg-tertiary: var(--flexops-gray-100);
  --flexops-bg-quaternary: var(--flexops-gray-200);
  --flexops-bg-disabled: var(--flexops-gray-100);
  --flexops-bg-active: var(--flexops-gray-50);
  --flexops-bg-brand-primary: var(--flexops-brand-50);
  --flexops-bg-brand-secondary: var(--flexops-brand-100);
  --flexops-bg-brand-solid: var(--flexops-brand-600);
  --flexops-bg-brand-solid-hover: var(--flexops-brand-700);
  --flexops-bg-brand-section: var(--flexops-brand-800);
  --flexops-bg-error-primary: var(--flexops-error-50);
  --flexops-bg-error-solid: var(--flexops-error-600);
  --flexops-bg-warning-primary: var(--flexops-warning-50);
  --flexops-bg-warning-solid: var(--flexops-warning-600);
  --flexops-bg-success-primary: var(--flexops-success-50);
  --flexops-bg-success-solid: var(--flexops-success-600);
  --flexops-bg-sky-primary: var(--flexops-sky-50);
  --flexops-bg-indigo-primary: var(--flexops-indigo-50);
  --flexops-bg-purple-primary: var(--flexops-purple-50);
  --flexops-bg-pink-primary: var(--flexops-pink-50);
  --flexops-bg-orange-primary: var(--flexops-orange-50);
  --flexops-bg-overlay: var(--flexops-gray-950);

  /* -----------------------------------------------------------------
   * Semantic: Border
   * ----------------------------------------------------------------- */
  /* The edge of an INTERACTIVE control — text inputs, dropdowns, date pickers,
     textareas, and secondary/light buttons. Separate from -primary on purpose.
     Those controls set their background to --color-bg-primary, the same colour as
     the card behind them, so the border is not decoration: it is the only thing on
     screen saying a control is there, which is exactly what WCAG 1.4.11 covers at
     3:1. -primary (gray-300) measures 1.46:1 on white and 1.88:1 on #1C1A21.
     Card, panel and grid edges keep -primary and -secondary and are unaffected;
     a divider between two cards is decorative and carries no such requirement.
     ONE value for both themes — the dark block deliberately does not override it.
     If -450 proves too soft in the warehouse, gray-500 is the fallback: change the
     line below and nothing else (4.56:1 light, 3.78:1 dark, visibly heavier). */
  --flexops-border-control: var(--flexops-gray-450);
  --flexops-border-primary: var(--flexops-gray-300);
  --flexops-border-secondary: var(--flexops-gray-200);
  --flexops-border-tertiary: var(--flexops-gray-100);
  --flexops-border-brand: var(--flexops-brand-500);
  --flexops-border-error: var(--flexops-error-500);
  --flexops-border-disabled: var(--flexops-gray-300);

  /* -----------------------------------------------------------------
   * Semantic: Foreground (icons, decorative elements)
   * ----------------------------------------------------------------- */
  --flexops-fg-primary: var(--flexops-gray-900);
  --flexops-fg-secondary: var(--flexops-gray-700);
  --flexops-fg-tertiary: var(--flexops-gray-600);
  --flexops-fg-quaternary: var(--flexops-gray-400);
  --flexops-fg-disabled: var(--flexops-gray-400);
  --flexops-fg-brand-primary: var(--flexops-brand-600);
  --flexops-fg-white: #FFFFFF;

  /* -----------------------------------------------------------------
   * Typography
   * ----------------------------------------------------------------- */
  --flexops-font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --flexops-font-display: "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --flexops-font-mono: ui-monospace, "Roboto Mono", "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

  /* -----------------------------------------------------------------
   * Type scale + optical tracking
   * -----------------------------------------------------------------
   * Sizes were previously ad-hoc per component (0.95rem, 1rem, 1.875rem),
   * so vertical rhythm could not be consistent. Each size pairs with a
   * tracking value: large text tightens, small text opens up. This is what
   * separates a typeset interface from a defaults-everywhere one.
   * ----------------------------------------------------------------- */
  --flexops-text-xs: 0.75rem;      /* 12px */
  --flexops-text-sm: 0.875rem;     /* 14px */
  --flexops-text-base: 1rem;       /* 16px */
  --flexops-text-md: var(--flexops-text-base);  /* 16px — Untitled UI's name for this step */
  --flexops-text-lg: 1.125rem;     /* 18px */
  --flexops-text-xl: 1.25rem;      /* 20px */
  --flexops-text-2xl: 1.875rem;    /* 30px */
  --flexops-text-3xl: 2.25rem;     /* 36px */

  --flexops-text-4xl: 3rem;        /* 48px */
  --flexops-text-5xl: 3.75rem;     /* 60px */
  --flexops-text-6xl: 4.5rem;      /* 72px */

  /* -----------------------------------------------------------------
   * DISPLAY TIER (Untitled UI v8)
   *
   * Untitled UI splits type in two: text-xs..text-xl for UI and body,
   * display-xs..display-2xl for headings. We had flattened both into one
   * Tailwind-style text-* ramp, which cost us the 20px step and bound
   * text-xl to 24px — Untitled UI's display-xs. A ported component asking
   * for text-xl rendered 20% large, which reads as a design choice rather
   * than a bug.
   *
   * No new sizes are introduced below: each display step names a value the
   * ramp above already carries. The text-2xl..6xl names are kept so existing
   * call sites keep working.
   * ----------------------------------------------------------------- */
  --flexops-display-xs: 1.5rem;                     /* 24px */
  --flexops-display-sm: var(--flexops-text-2xl);    /* 30px */
  --flexops-display-md: var(--flexops-text-3xl);    /* 36px */
  --flexops-display-lg: var(--flexops-text-4xl);    /* 48px */
  --flexops-display-xl: var(--flexops-text-5xl);    /* 60px */
  --flexops-display-2xl: var(--flexops-text-6xl);   /* 72px */

  /* -----------------------------------------------------------------
   * FONT WEIGHT
   *
   * Both fonts are loaded as VARIABLE across all four portals
   * (Inter:wght@100..900, Space+Grotesk:wght@300..700), so every value
   * below is a real point on the axis — including 650, which is not a
   * standard step and would round on a static font.
   *
   * 650 is deliberate: it sits on the display-tier headings set in Space
   * Grotesk, one optical notch above semibold. Do not "tidy" it to 600.
   * ----------------------------------------------------------------- */
  --flexops-font-weight-regular: 400;
  --flexops-font-weight-medium: 500;
  --flexops-font-weight-semibold: 600;
  --flexops-font-weight-display: 650;
  /* No current consumer. Present so the Untitled UI weight vocabulary is
     complete and a ported component asking for bold has something to map
     to; 700 is also the ceiling of Space Grotesk's loaded range. */
  --flexops-font-weight-bold: 700;

  --flexops-track-xs: 0.01em;
  --flexops-track-sm: 0;
  --flexops-track-base: -0.006em;
  --flexops-track-lg: -0.011em;
  --flexops-track-xl: -0.019em;
  --flexops-track-2xl: -0.024em;
  --flexops-track-3xl: -0.028em;
  /* Untitled UI tracks its whole display tier at a flat -0.02em
     (-0.96px/48, -1.2px/60, -1.44px/72 all reduce to it). The stops
     above are our own optical tuning and are deliberately left as-is. */
  --flexops-track-4xl: -0.02em;
  --flexops-track-5xl: -0.02em;
  --flexops-track-6xl: -0.02em;

  /* Display tracking. display-xs keeps the value 24px already had back when
     text-xl held that size, so nothing shifts optically. The rest alias the
     stops above. Expressed in em, not Untitled UI's px, so tracking scales
     with the size it is applied to. */
  --flexops-track-display-xs: -0.019em;
  --flexops-track-display-sm: var(--flexops-track-2xl);
  --flexops-track-display-md: var(--flexops-track-3xl);
  --flexops-track-display-lg: var(--flexops-track-4xl);
  --flexops-track-display-xl: var(--flexops-track-5xl);
  --flexops-track-display-2xl: var(--flexops-track-6xl);

  /* -----------------------------------------------------------------
   * Line height
   * -----------------------------------------------------------------
   * Every size pairs with one; a size token without a line-height is
   * half a type system, which is why component CSS kept hardcoding
   * font-size and letting line-height fall back to the browser.
   *
   * Paired to Untitled UI BY PIXEL SIZE, not by label - the two scales
   * use the same names for different sizes. Our --text-xl is 24px,
   * where Untitled UI's xl is 20px and 24px is its display-xs. Pairing
   * on the name would have given 24px text a line-height meant for
   * 20px. Mapping: xs>xs, sm>sm, base>md, lg>lg, xl>display-xs,
   * 2xl>display-sm, 3xl>display-md, 4xl>display-lg, 5xl>display-xl,
   * 6xl>display-2xl.
   * ----------------------------------------------------------------- */
  --flexops-line-height-xs: 1.125rem;   /* 18px on 12px */
  --flexops-line-height-sm: 1.25rem;    /* 20px on 14px */
  --flexops-line-height-base: 1.5rem;   /* 24px on 16px */
  --flexops-line-height-lg: 1.75rem;    /* 28px on 18px */
  --flexops-line-height-xl: 1.875rem;   /* 30px on 20px */
  --flexops-line-height-2xl: 2.375rem;  /* 38px on 30px */
  --flexops-line-height-3xl: 2.75rem;   /* 44px on 36px */
  --flexops-line-height-4xl: 3.75rem;   /* 60px on 48px */
  --flexops-line-height-5xl: 4.5rem;    /* 72px on 60px */
  --flexops-line-height-6xl: 5.625rem;  /* 90px on 72px */

  /* Display leading. display-xs keeps 32px — the leading 24px carried when
     text-xl held that size — so the one existing consumer is unchanged.
     The xl stop above moved with its size: 20px text takes 30px leading,
     which is Untitled UI's pairing. */
  --flexops-line-height-display-xs: 2rem;                        /* 32px on 24px */
  --flexops-line-height-display-sm: var(--flexops-line-height-2xl);
  --flexops-line-height-display-md: var(--flexops-line-height-3xl);
  --flexops-line-height-display-lg: var(--flexops-line-height-4xl);
  --flexops-line-height-display-xl: var(--flexops-line-height-5xl);
  --flexops-line-height-display-2xl: var(--flexops-line-height-6xl);

  /* -----------------------------------------------------------------
   * Border Radius
   * ----------------------------------------------------------------- */
  --flexops-radius-none: 0px;
  --flexops-radius-xs: 0.125rem;  /* 2px */
  --flexops-radius-sm: 0.25rem;   /* 4px */
  --flexops-radius-md: 0.375rem;  /* 6px */
  --flexops-radius-lg: 0.5rem;    /* 8px */
  --flexops-radius-xl: 0.75rem;   /* 12px */
  --flexops-radius-2xl: 1rem;     /* 16px */
  --flexops-radius-3xl: 1.5rem;   /* 24px */
  --flexops-radius-full: 9999px;

  /* -----------------------------------------------------------------
   * Shadows
   * ----------------------------------------------------------------- */
  /* xs/sm carry a wider, softer second layer so surfaces separate by ELEVATION
     rather than by a 1px hairline. The old xs (single 2px blur at 0.05) sat
     below the perceptual threshold, which left the border doing all the work
     and made every panel read as a same-weight box. */
  --flexops-shadow-xs: 0px 1px 2px rgba(10, 13, 18, 0.04), 0px 1px 3px rgba(10, 13, 18, 0.06);
  --flexops-shadow-sm: 0px 2px 4px rgba(10, 13, 18, 0.04), 0px 4px 12px -2px rgba(10, 13, 18, 0.08);
  --flexops-shadow-md: 0px 4px 6px -1px rgba(10, 13, 18, 0.1), 0px 2px 4px -2px rgba(10, 13, 18, 0.06);
  --flexops-shadow-lg: 0px 12px 16px -4px rgba(10, 13, 18, 0.08), 0px 4px 6px -2px rgba(10, 13, 18, 0.03), 0px 2px 2px -1px rgba(10, 13, 18, 0.04);
  --flexops-shadow-xl: 0px 20px 24px -4px rgba(10, 13, 18, 0.08), 0px 8px 8px -4px rgba(10, 13, 18, 0.03), 0px 3px 3px -1.5px rgba(10, 13, 18, 0.04);
  --flexops-shadow-2xl: 0px 24px 48px -12px rgba(10, 13, 18, 0.18), 0px 4px 4px -2px rgba(10, 13, 18, 0.04);
  /* The ready-made focus ring, for callers that drop it straight into box-shadow.
     Its COLOUR comes from --color-focus-ring in the alias layer below, so the dark
     theme can lighten the ring without restating the geometry. Callers that compose
     their own ring (a different width, or a ring plus an elevation shadow) must use
     --color-focus-ring and supply the offsets themselves -- this token is a whole
     shadow, and substituting it where a colour belongs yields "0 0 0 4px 0 0 0 3px
     ..." which is invalid at computed-value time, so the browser drops the ring
     entirely and focus becomes invisible rather than merely faint. */
  --flexops-shadow-focus: 0 0 0 3px var(--color-focus-ring);

  /* -----------------------------------------------------------------
   * Spacing
   * ----------------------------------------------------------------- */
  --flexops-space-0: 0;
  --flexops-space-1: 0.25rem;   /* 4px */
  --flexops-space-2: 0.5rem;    /* 8px */
  --flexops-space-3: 0.75rem;   /* 12px */
  --flexops-space-4: 1rem;      /* 16px */
  --flexops-space-5: 1.25rem;   /* 20px */
  --flexops-space-6: 1.5rem;    /* 24px */
  --flexops-space-8: 2rem;      /* 32px */
  --flexops-space-10: 2.5rem;   /* 40px */
  --flexops-space-12: 3rem;     /* 48px */
  --flexops-space-16: 4rem;     /* 64px */
  --flexops-space-20: 5rem;     /* 80px */

  /* -----------------------------------------------------------------
   * Transitions
   * ----------------------------------------------------------------- */
  /* Decelerating curve (fast out, long settle) — reads as a response rather
     than a mechanical snap. Symmetric ease-in-out is the "industrial" tell. */
  --flexops-transition-fast: 120ms cubic-bezier(0.16, 1, 0.3, 1);
  --flexops-transition: 180ms cubic-bezier(0.16, 1, 0.3, 1);
  --flexops-transition-slow: 280ms cubic-bezier(0.16, 1, 0.3, 1);

  /* -----------------------------------------------------------------
   * Semantic aliases (--color-*, --shadow-skeumorphic)
   * Used by the shared Radzen overrides and portal site.css files.
   * ----------------------------------------------------------------- */

  /* Text */
  --color-text-primary: var(--flexops-text-primary);
  --color-text-secondary: var(--flexops-text-secondary);
  --color-text-tertiary: var(--flexops-text-tertiary);
  --color-text-quaternary: var(--flexops-text-quaternary);
  --color-text-disabled: var(--flexops-text-disabled);
  --color-text-placeholder: var(--flexops-text-placeholder);
  --color-text-white: var(--flexops-text-white);

  /* Background */
  --color-bg-primary: var(--flexops-bg-primary);
  --color-bg-secondary: var(--flexops-bg-secondary);
  --color-bg-tertiary: var(--flexops-bg-tertiary);
  --color-bg-disabled: var(--flexops-bg-disabled);
  --color-bg-brand-primary: var(--flexops-bg-brand-primary);
  --color-bg-brand-solid: var(--flexops-bg-brand-solid);
  --color-bg-brand-solid_hover: var(--flexops-bg-brand-solid-hover);
  --color-bg-error-primary: var(--flexops-bg-error-primary);
  --color-bg-error-solid: var(--flexops-bg-error-solid);
  --color-bg-success-primary: var(--flexops-bg-success-primary);
  --color-bg-warning-primary: var(--flexops-bg-warning-primary);
  --color-bg-sky-primary: var(--flexops-bg-sky-primary);
  --color-bg-indigo-primary: var(--flexops-bg-indigo-primary);
  --color-bg-purple-primary: var(--flexops-bg-purple-primary);
  --color-bg-pink-primary: var(--flexops-bg-pink-primary);
  --color-bg-orange-primary: var(--flexops-bg-orange-primary);

  /* Border */
  --color-border-control: var(--flexops-border-control);
  --color-border-primary: var(--flexops-border-primary);
  --color-border-secondary: var(--flexops-border-secondary);
  --color-border-tertiary: var(--flexops-border-tertiary);
  --color-border-brand: var(--flexops-border-brand);
  --color-border-error: var(--flexops-border-error);
  --color-border-disabled: var(--flexops-border-disabled);

  /* Foreground */
  --color-fg-brand-primary: var(--flexops-fg-brand-primary);
  /* Routed through the text roles above rather than pinned to a raw stop, so these
     follow the theme. .text-success / .text-warning / .text-error colour text with
     them, and text on a dark surface needs the opposite end of the ramp from text
     on a white one -- a hard -600 here cannot express that. */
  --color-fg-success-primary: var(--flexops-text-success-primary);
  --color-fg-warning-primary: var(--flexops-text-warning-primary);
  --color-fg-error-primary: var(--flexops-text-error-primary);

  /* Text — additional semantic */
  --color-text-error-primary: var(--flexops-text-error-primary);
  --color-text-warning-primary: var(--flexops-text-warning-primary);
  --color-text-success-primary: var(--flexops-text-success-primary);
  /* The brand counterpart was missing from this set. Aliasing the raw token
     rather than --flexops-brand-600 keeps the dark-theme remap (brand-300, for
     AA legibility on dark surfaces) working for anything that uses it. */
  --color-text-brand-primary: var(--flexops-text-brand-primary);

  /* Status stop aliases */
  /* The -50 tints and blue -100 were referenced by .rz-message (toast/alert
     backgrounds) and .badge-blue but never aliased, so those surfaces resolved
     to an invalid value and painted no fill at all. */
  --color-success-50: var(--flexops-success-50);
  --color-error-50: var(--flexops-error-50);
  --color-warning-50: var(--flexops-warning-50);
  --color-blue-50: var(--flexops-blue-50);
  --color-blue-100: var(--flexops-blue-100);
  --color-success-200: var(--flexops-success-200);
  --color-success-500: var(--flexops-success-500);
  --color-success-600: var(--flexops-success-600);
  --color-success-700: var(--flexops-success-700);
  --color-error-200: var(--flexops-error-200);
  --color-error-500: var(--flexops-error-500);
  --color-error-600: var(--flexops-error-600);
  --color-error-700: var(--flexops-error-700);
  --color-warning-200: var(--flexops-warning-200);
  --color-warning-400: var(--flexops-warning-400);
  --color-warning-500: var(--flexops-warning-500);
  --color-warning-600: var(--flexops-warning-600);
  --color-warning-700: var(--flexops-warning-700);
  --color-blue-500: var(--flexops-blue-500);
  --color-blue-600: var(--flexops-blue-600);
  --color-blue-700: var(--flexops-blue-700);

  /* Charts */
  --color-chart-series-1: var(--color-brand-600);
  --color-chart-series-2: var(--color-blue-600);
  --color-chart-series-3: var(--color-success-600);
  --color-chart-series-4: var(--color-warning-600);
  --color-chart-series-5: var(--color-error-600);
  --color-chart-series-6: var(--color-gray-600);
  --color-chart-grid: var(--color-border-secondary);
  --color-chart-axis: var(--color-text-tertiary);

  /* Sky / Indigo / Purple / Pink / Orange — badge intents (Task 3 consumes
     --color-bg-{family}-primary [aliased beside the other --color-bg-*-primary
     tokens above], --color-{family}-200, --color-{family}-700) */
  --color-sky-200: var(--flexops-sky-200);
  --color-sky-700: var(--flexops-sky-700);

  --color-indigo-200: var(--flexops-indigo-200);
  --color-indigo-700: var(--flexops-indigo-700);

  --color-purple-200: var(--flexops-purple-200);
  --color-purple-700: var(--flexops-purple-700);

  --color-pink-200: var(--flexops-pink-200);
  --color-pink-700: var(--flexops-pink-700);

  --color-orange-200: var(--flexops-orange-200);
  --color-orange-700: var(--flexops-orange-700);

  /* Skeumorphic shadow */
  --shadow-skeumorphic: 0px 0px 0px 1px rgba(10, 13, 18, 0.18) inset, 0px -2px 0px 0px rgba(10, 13, 18, 0.05) inset;
  --shadow-xs-skeumorphic: var(--shadow-skeumorphic), var(--flexops-shadow-xs);

  /* Settings Hub / API Portal / Identity Portal palette aliases */
  --color-brand-25: var(--flexops-brand-25);
  --color-brand-50: var(--flexops-brand-50);
  --color-brand-100: var(--flexops-brand-100);
  --color-brand-200: var(--flexops-brand-200);
  --color-brand-300: var(--flexops-brand-300);
  --color-brand-400: var(--flexops-brand-400);
  --color-brand-500: var(--flexops-brand-500);
  --color-brand-600: var(--flexops-brand-600);
  --color-brand-700: var(--flexops-brand-700);
  --color-brand-800: var(--flexops-brand-800);
  --color-brand-900: var(--flexops-brand-900);
  --color-brand-950: var(--flexops-brand-950);

  --color-gray-25: var(--flexops-gray-25);
  --color-gray-50: var(--flexops-gray-50);
  --color-gray-100: var(--flexops-gray-100);
  --color-gray-200: var(--flexops-gray-200);
  --color-gray-300: var(--flexops-gray-300);
  --color-gray-400: var(--flexops-gray-400);
  --color-gray-500: var(--flexops-gray-500);
  --color-gray-600: var(--flexops-gray-600);
  --color-gray-700: var(--flexops-gray-700);
  --color-gray-800: var(--flexops-gray-800);
  --color-gray-900: var(--flexops-gray-900);
  --color-gray-950: var(--flexops-gray-950);

  --font-body: var(--flexops-font-body);
  --font-display: var(--flexops-font-display);
  --font-mono: var(--flexops-font-mono);

  --font-weight-regular: var(--flexops-font-weight-regular);
  --font-weight-medium: var(--flexops-font-weight-medium);
  --font-weight-semibold: var(--flexops-font-weight-semibold);
  --font-weight-display: var(--flexops-font-weight-display);
  --font-weight-bold: var(--flexops-font-weight-bold);

  --text-xs: var(--flexops-text-xs);
  --text-sm: var(--flexops-text-sm);
  --text-base: var(--flexops-text-base);
  --text-md: var(--flexops-text-md);
  --text-lg: var(--flexops-text-lg);
  --text-xl: var(--flexops-text-xl);

  /* Untitled UI markup asks for --text-display-*, not --flexops-display-*. */
  --text-display-xs: var(--flexops-display-xs);
  --text-display-sm: var(--flexops-display-sm);
  --text-display-md: var(--flexops-display-md);
  --text-display-lg: var(--flexops-display-lg);
  --text-display-xl: var(--flexops-display-xl);
  --text-display-2xl: var(--flexops-display-2xl);

  --line-height-display-xs: var(--flexops-line-height-display-xs);
  --line-height-display-sm: var(--flexops-line-height-display-sm);
  --line-height-display-md: var(--flexops-line-height-display-md);
  --line-height-display-lg: var(--flexops-line-height-display-lg);
  --line-height-display-xl: var(--flexops-line-height-display-xl);
  --line-height-display-2xl: var(--flexops-line-height-display-2xl);

  --track-display-xs: var(--flexops-track-display-xs);
  --track-display-sm: var(--flexops-track-display-sm);
  --track-display-md: var(--flexops-track-display-md);
  --track-display-lg: var(--flexops-track-display-lg);
  --track-display-xl: var(--flexops-track-display-xl);
  --track-display-2xl: var(--flexops-track-display-2xl);
  --text-2xl: var(--flexops-text-2xl);
  --text-3xl: var(--flexops-text-3xl);

  --text-4xl: var(--flexops-text-4xl);
  --text-5xl: var(--flexops-text-5xl);
  --text-6xl: var(--flexops-text-6xl);

  --track-xs: var(--flexops-track-xs);
  --track-sm: var(--flexops-track-sm);
  --track-base: var(--flexops-track-base);
  --track-lg: var(--flexops-track-lg);
  --track-xl: var(--flexops-track-xl);
  --track-2xl: var(--flexops-track-2xl);
  --track-3xl: var(--flexops-track-3xl);
  --track-4xl: var(--flexops-track-4xl);
  --track-5xl: var(--flexops-track-5xl);
  --track-6xl: var(--flexops-track-6xl);

  --line-height-xs: var(--flexops-line-height-xs);
  --line-height-sm: var(--flexops-line-height-sm);
  --line-height-base: var(--flexops-line-height-base);
  --line-height-lg: var(--flexops-line-height-lg);
  --line-height-xl: var(--flexops-line-height-xl);
  --line-height-2xl: var(--flexops-line-height-2xl);
  --line-height-3xl: var(--flexops-line-height-3xl);
  --line-height-4xl: var(--flexops-line-height-4xl);
  --line-height-5xl: var(--flexops-line-height-5xl);
  --line-height-6xl: var(--flexops-line-height-6xl);

  --radius-xs: var(--flexops-radius-xs);
  --radius-sm: var(--flexops-radius-sm);
  --radius-md: var(--flexops-radius-md);
  --radius-lg: var(--flexops-radius-lg);
  --radius-xl: var(--flexops-radius-xl);
  --radius-2xl: var(--flexops-radius-2xl);
  --radius-3xl: var(--flexops-radius-3xl);
  --radius-full: var(--flexops-radius-full);

  --shadow-xs: var(--flexops-shadow-xs);
  --shadow-sm: var(--flexops-shadow-sm);
  --shadow-md: var(--flexops-shadow-md);
  --shadow-lg: var(--flexops-shadow-lg);
  --shadow-xl: var(--flexops-shadow-xl);
  --shadow-2xl: var(--flexops-shadow-2xl);

  /* A COLOUR, not a shadow -- the name promises a colour and two of the four call
     sites use it as one. Solid, not a tint: at the previous 12% alpha the ring
     composited to #F1EBF9 on white for 1.17:1 against the 3:1 WCAG 1.4.11 floor,
     which is a ring that technically renders and practically cannot be seen. The
     surrounding work was already right -- every outline:none has a replacement --
     so nothing looked wrong in review; only compositing the alpha showed it.
     brand-600 measures 7.4:1 on --flexops-bg-primary and 7.1:1 on bg-secondary. */
  --color-focus-ring: var(--flexops-brand-600);
}

/* =====================================================================
 * Dark theme — overrides the semantic alias layer only.
 * Activated by [data-theme="dark"] on <html>. Raw stops are unchanged;
 * --color-* aliases inherit because they reference these semantics.
 * ===================================================================== */
[data-theme="dark"] {
  /* Tell the UA to render native form controls, scrollbars and autofill dark. */
  color-scheme: dark;

  /* -----------------------------------------------------------------
   * UTILITY COLOURS - dark
   *
   * The ramp INVERTS rather than shifting: 50<->950, 100<->900, 200<->800,
   * 300<->700, 400<->600, 500 holds. Every light/dark pair sums to 1000.
   * That is Untitled UI's scheme, and the reason is that a "barely there"
   * tint must stay barely there on a near-black surface -- reusing the
   * light value would make it glow.
   * ----------------------------------------------------------------- */
  /* brand */
  --color-utility-brand-50: var(--flexops-brand-950);
  --color-utility-brand-50_alt: var(--color-utility-neutral-50);
  --color-utility-brand-100: var(--flexops-brand-900);
  --color-utility-brand-100_alt: var(--color-utility-neutral-100);
  --color-utility-brand-200: var(--flexops-brand-800);
  --color-utility-brand-200_alt: var(--color-utility-neutral-200);
  --color-utility-brand-300: var(--flexops-brand-700);
  --color-utility-brand-300_alt: var(--color-utility-neutral-300);
  --color-utility-brand-400: var(--flexops-brand-600);
  --color-utility-brand-400_alt: var(--color-utility-neutral-400);
  --color-utility-brand-500: var(--flexops-brand-500);
  --color-utility-brand-500_alt: var(--color-utility-neutral-500);
  --color-utility-brand-600: var(--flexops-brand-400);
  --color-utility-brand-600_alt: var(--color-utility-neutral-600);
  --color-utility-brand-700: var(--flexops-brand-300);
  --color-utility-brand-700_alt: var(--color-utility-neutral-700);

  /* neutral */
  --color-utility-neutral-50: var(--flexops-gray-950);
  --color-utility-neutral-100: var(--flexops-gray-900);
  --color-utility-neutral-200: var(--flexops-gray-800);
  --color-utility-neutral-300: var(--flexops-gray-700);
  --color-utility-neutral-400: var(--flexops-gray-600);
  --color-utility-neutral-500: var(--flexops-gray-500);
  --color-utility-neutral-600: var(--flexops-gray-400);
  --color-utility-neutral-700: var(--flexops-gray-300);

  /* red */
  --color-utility-red-50: var(--flexops-error-950);
  --color-utility-red-100: var(--flexops-error-900);
  --color-utility-red-200: var(--flexops-error-800);
  --color-utility-red-300: var(--flexops-error-700);
  --color-utility-red-400: var(--flexops-error-600);
  --color-utility-red-500: var(--flexops-error-500);
  --color-utility-red-600: var(--flexops-error-400);
  --color-utility-red-700: var(--flexops-error-300);

  /* yellow */
  --color-utility-yellow-50: var(--flexops-warning-950);
  --color-utility-yellow-100: var(--flexops-warning-900);
  --color-utility-yellow-200: var(--flexops-warning-800);
  --color-utility-yellow-300: var(--flexops-warning-700);
  --color-utility-yellow-400: var(--flexops-warning-600);
  --color-utility-yellow-500: var(--flexops-warning-500);
  --color-utility-yellow-600: var(--flexops-warning-400);
  --color-utility-yellow-700: var(--flexops-warning-300);

  /* green */
  --color-utility-green-50: var(--flexops-success-950);
  --color-utility-green-100: var(--flexops-success-900);
  --color-utility-green-200: var(--flexops-success-800);
  --color-utility-green-300: var(--flexops-success-700);
  --color-utility-green-400: var(--flexops-success-600);
  --color-utility-green-500: var(--flexops-success-500);
  --color-utility-green-600: var(--flexops-success-400);
  --color-utility-green-700: var(--flexops-success-300);

  /* blue */
  --color-utility-blue-50: var(--flexops-blue-950);
  --color-utility-blue-100: var(--flexops-blue-900);
  --color-utility-blue-200: var(--flexops-blue-800);
  --color-utility-blue-300: var(--flexops-blue-700);
  --color-utility-blue-400: var(--flexops-blue-600);
  --color-utility-blue-500: var(--flexops-blue-500);
  --color-utility-blue-600: var(--flexops-blue-400);
  --color-utility-blue-700: var(--flexops-blue-300);

  /* Text */
  --flexops-text-primary: #F3F1F5;
  --flexops-text-secondary: #D8D4DD;
  --flexops-text-tertiary: #B0ABB8;
  --flexops-text-quaternary: #84808C;
  --flexops-text-placeholder: #84808C;

  /* Background (raise surfaces as they get more prominent) */
  --flexops-bg-primary: #1C1A21;     /* cards / panels */
  --flexops-bg-secondary: #110F15;   /* page background */
  --flexops-bg-tertiary: #2C2A33;    /* hover / inset */
  --flexops-bg-active: #2C2A33;
  --flexops-bg-brand-primary: #27163B;
  --flexops-bg-brand-secondary: #382451;

  /* Border */
  --flexops-border-primary: #4A4651;
  --flexops-border-secondary: #2C2A33;
  --flexops-border-tertiary: #2C2A33;

  /* Status text — lifted to -300, the same stop the dark badge rules already use
     (flexops-portal-base.css:179-195), so a status word reads the same weight
     whether it sits in a pill or bare in a table cell. Inheriting the light -700
     steps would put text tuned for a white page on #1C1A21: success 3.03:1,
     error 2.62:1, warning 3.17:1 — all well under 4.5. At -300 the worst case
     across the three dark surfaces is 8.73 (success), 7.28 (error), 9.14 (warning).
     --color-fg-*-primary and --color-text-*-primary both reference these, so the
     three lines below carry every status-text surface in the dark theme. */
  --flexops-text-error-primary: var(--flexops-error-300);
  --flexops-text-warning-primary: var(--flexops-warning-300);
  --flexops-text-success-primary: var(--flexops-success-300);

  /* Focus ring — lightened, not inherited. brand-600 is a dark purple: it measures
     7.4:1 on the light canvas and only 2.3:1 on #1C1A21, so carrying it into dark
     would re-hide the ring the light theme just fixed. brand-400 measures 6.3:1 on
     bg-primary and 5.2:1 on the bg-tertiary hover surface. --flexops-shadow-focus
     picks this up automatically; it references the colour rather than restating it. */
  --color-focus-ring: var(--flexops-brand-400);

  /* Foreground (icons) */
  --flexops-fg-primary: #F3F1F5;
  --flexops-fg-secondary: #D8D4DD;
  --flexops-fg-tertiary: #B0ABB8;
  --flexops-fg-quaternary: #84808C;

  /* Brand accents — lift foreground brand tokens for AA legibility on dark
     surfaces. brand-600/700 are ~2.0-2.7:1 on the dark card; brand-300 reads
     clearly. Solid brand button backgrounds keep the raw --flexops-brand-600
     token (via --rz-primary), so only text/icon accents move here. */
  --flexops-text-brand-primary: var(--flexops-brand-300);
  --flexops-text-brand-secondary: var(--flexops-brand-400);
  --flexops-fg-brand-primary: var(--flexops-brand-300);

  /* Data marks need the lighter stops on dark chart surfaces. */
  --color-chart-series-1: var(--flexops-brand-400);
  --color-chart-series-2: var(--flexops-blue-400);
  --color-chart-series-3: var(--flexops-success-400);
  --color-chart-series-4: var(--flexops-warning-400);
  --color-chart-series-5: var(--flexops-error-400);
  --color-chart-series-6: var(--flexops-gray-400);

  /* Shadows read as soft glows on dark, not drop shadows */
  --flexops-shadow-xs: 0px 1px 2px rgba(0, 0, 0, 0.4);
  --flexops-shadow-sm: 0px 1px 3px rgba(0, 0, 0, 0.5), 0px 1px 2px -1px rgba(0, 0, 0, 0.5);
  --flexops-shadow-md: 0px 4px 6px -1px rgba(0, 0, 0, 0.5);
  --flexops-shadow-lg: 0px 12px 16px -4px rgba(0, 0, 0, 0.55);
}

/* The compact toggle deliberately sets no Radzen --rz-grid-cell-* variables.
   It used to set padding/font-size/line-height here, and all three rendered nothing:
   flexops-radzen-overrides.css pins .rz-data-grid .rz-grid-table tbody td to a literal
   padding that outranks Radzen's own `padding: var(--rz-grid-cell-padding)`. Measured
   in-browser — perturbing all three moved 0 of 890 grid elements.
   The toggle's live effects are element-level and live in flexops-radzen-overrides.css:
   .rz-cell-data min-height and .rz-panel-menu link padding (~4 more grid rows and
   ~5 more nav items per 900px). Grid cell density itself is a per-screen decision,
   not a user preference — see the operator-grid modifier in Ship Manager's custom.css. */
