/* AppMon design tokens. Loaded before appmon.css; nothing else defines a
   colour, a shadow, a duration or a z-index.

   ---------------------------------------------------------------------------
   Why this file exists, and why the values are not the deck's
   ---------------------------------------------------------------------------

   The 0.9 palette was lifted from the client's slide deck. Measured, its
   status trio does not survive the people who have to read it:

     deck UP  #196B24  and  deck DOWN  #C00000
       - CVD delta-E under deuteranopia ............... 0.057   (fails)
       - greyscale lightness ................. 0.44 vs 0.45      (identical)

   Green and red were the same swatch to a deuteranope -- roughly one man in
   twelve -- and the same swatch again on any dimmed screen or mono printout.
   For a product whose entire job is to say "working" or "not working", that
   is a defect, not a preference.

   The trio below was chosen by searching the space of green/amber/red triads
   for the one maximising the WORST pairwise separation across normal vision,
   deuteranopia, protanopia and tritanopia, subject to each colour still
   answering to its own name and still being visible on a white page:

     worst pair, all vision types .................... 0.135   (2.4x better)
     greyscale ladder ......... down 0.43 < up 0.58 < degraded 0.76

   That ladder is load-bearing and deliberate: ordered by lightness, darker
   means worse. Severity survives greyscale, a dimmed wallboard and a
   photocopy, with no colour perception required at all.

   Colour is still never the only channel. Every status also carries a word
   and a shape (see .dot in appmon.css): filled circle UP, half-filled
   DEGRADED, filled square DOWN, hollow ring UNKNOWN.

   ---------------------------------------------------------------------------
   Why every colour is declared twice
   ---------------------------------------------------------------------------

   Authoring is in OKLCH -- the ramps are even because they are even in a
   perceptual space, not because they were nudged by eye. But AppMon deploys
   into a corporate estate with no build step and no guaranteed browser floor,
   so each token is declared first as sRGB hex and then re-declared in OKLCH
   behind an @supports guard. The two are the same colour by construction:
   both are emitted from the same script, with chroma clamped into sRGB first,
   so neither form is a clipped approximation of the other. */

:root {
  /* --- primitives: cobalt (brand) ----------------------------------------
     Anchored on the brand seed oklch(0.742 0.140 247). Hue is held between
     248 and 255 across the ramp -- the slight walk toward blue at the dark
     end keeps the deep steps from going purple, which is what a fixed hue
     does in OKLCH once chroma drops. */
  --cobalt-50: #f3f9fe;
  --cobalt-100: #e1f0ff;
  --cobalt-200: #c1defc;
  --cobalt-300: #91c3f6;
  --cobalt-400: #59a3ed;
  --cobalt-500: #2b82d5;
  --cobalt-600: #0265b6;
  --cobalt-700: #004e93;
  --cobalt-800: #01396d;
  --cobalt-900: #032449;
  --cobalt-950: #03152d;

  /* --- primitives: slate (neutrals) --------------------------------------
     Tinted 0.004-0.014 chroma toward the brand's own hue, never toward warm.
     Enough that the greys belong to the cobalt; not enough to read as blue.
     A dead-neutral grey here is what makes enterprise tooling look like
     enterprise tooling. */
  --slate-50: #f6f9fb;
  --slate-100: #eff1f4;
  --slate-200: #dee2e6;
  --slate-300: #c8cdd2;
  --slate-400: #a0a5ab;
  --slate-500: #80858b;
  --slate-600: #646a70;
  --slate-700: #4b5057;
  --slate-800: #32373e;
  --slate-900: #20252b;
  --slate-950: #12171c;

  /* --- primitives: status ------------------------------------------------
     Two tiers per status, because one value cannot do both jobs. `*-block`
     is the colour as an object -- a dot, a wash, a rule, a sparkline stroke.
     `*-ink` is the same status set as words, dark enough to clear 4.5:1 on
     white. DOWN is the one status where they coincide; a red dark enough to
     read as words is already the right red for a dot. */
  --status-up-block: #02926e;
  --status-up-ink: #016b50;
  --status-degraded-block: #f69828;
  --status-degraded-ink: #a35301;
  --status-down-block: #990116;
  --status-down-ink: #990116;
  --status-unknown-block: #979fa8;
  --status-unknown-ink: #646a70;
}

@supports (color: oklch(0.5 0.1 250)) {
  :root {
    --cobalt-50: oklch(0.978 0.01 248);
    --cobalt-100: oklch(0.948 0.026 248);
    --cobalt-200: oklch(0.89 0.052 249);
    --cobalt-300: oklch(0.8 0.09 250);
    --cobalt-400: oklch(0.7 0.132 250);
    --cobalt-500: oklch(0.598 0.15 251);
    --cobalt-600: oklch(0.505 0.15 252);
    --cobalt-700: oklch(0.425 0.131 253);
    --cobalt-800: oklch(0.345 0.105 253);
    --cobalt-900: oklch(0.262 0.078 254);
    --cobalt-950: oklch(0.196 0.055 255);

    --slate-50: oklch(0.98 0.004 250);
    --slate-100: oklch(0.958 0.005 250);
    --slate-200: oklch(0.912 0.007 250);
    --slate-300: oklch(0.845 0.009 251);
    --slate-400: oklch(0.72 0.01 251);
    --slate-500: oklch(0.615 0.011 252);
    --slate-600: oklch(0.52 0.012 252);
    --slate-700: oklch(0.43 0.013 252);
    --slate-800: oklch(0.335 0.014 253);
    --slate-900: oklch(0.262 0.014 253);
    --slate-950: oklch(0.202 0.013 254);

    --status-up-block: oklch(0.585 0.118 168);
    --status-up-ink: oklch(0.47 0.095 168);
    --status-degraded-block: oklch(0.76 0.16 64);
    --status-degraded-ink: oklch(0.53 0.131 55);
    --status-down-block: oklch(0.43 0.174 25);
    --status-down-ink: oklch(0.43 0.174 25);
    --status-unknown-block: oklch(0.7 0.016 252);
    --status-unknown-ink: oklch(0.52 0.012 252);
  }
}

/* ---------------------------------------------------------------------------
   Semantic layer. Everything in appmon.css reads from here, never from a
   primitive -- so a primitive can be retuned without hunting through rules,
   and so a token's name says what it is FOR rather than what colour it is.
   --------------------------------------------------------------------------- */

:root {
  /* Surfaces. The page is literally white: the brand carries the colour, and
     a tinted near-white ground would put the same hue in two places at once
     and flatten both. --color-surface is the recessed tone (table heads,
     inactive tabs, badge fills), not the page. */
  --color-page: #fff;
  --color-panel: #fff;
  --color-surface: var(--slate-100);
  --color-surface-sunken: var(--slate-50);

  /* Lines. Three, because they do three different jobs and WCAG only cares
     about one of them. --color-border draws panel edges and table rules,
     which are decorative and may be quiet. --color-field-border draws the
     boundary of an input, which is a real UI component and must clear 3:1
     against the page (it measures 3.71:1). Using the quiet one on a text
     field is the single most common contrast failure in admin UI. */
  --color-border: var(--slate-200);
  --color-border-strong: var(--slate-300);
  --color-field-border: var(--slate-500);

  /* Text. Measured against the white page: 18.0:1, 5.5:1, 3.7:1. The subtle
     tier is for disabled and decorative text only -- never body copy, and
     never a placeholder, which is real text and gets the muted tier. */
  --color-text: var(--slate-950);
  --color-text-muted: var(--slate-600);
  --color-text-subtle: var(--slate-500);
  --color-text-on-brand: #fff;

  /* Brand. Split by job, not by shade: the chrome band, the fill under a
     primary button, and the colour of a link are three different decisions
     that happen to be the same family. */
  --color-brand-chrome: var(--cobalt-800);
  --color-brand: var(--cobalt-700);
  --color-brand-hover: var(--cobalt-800);
  --color-brand-active: var(--cobalt-900);
  --color-brand-wash: var(--cobalt-50);
  --color-link: var(--cobalt-600);
  --color-focus: var(--cobalt-600);

  /* Status, as the rest of the app names it. */
  --color-up: var(--status-up-block);
  --color-degraded: var(--status-degraded-block);
  --color-down: var(--status-down-block);
  --color-unknown: var(--status-unknown-block);
  --color-up-ink: var(--status-up-ink);
  --color-degraded-ink: var(--status-degraded-ink);
  --color-down-ink: var(--status-down-ink);
  --color-unknown-ink: var(--status-unknown-ink);

  /* Back-compatible aliases. The 0.9 stylesheet spoke in --color-navy and
     --color-muted; both still resolve, so no rule had to be rewritten just
     to change palette. New rules should prefer the named roles above. */
  --color-navy: var(--color-brand-chrome);
  --color-muted: var(--color-text-muted);

  /* Elevation. Two steps and no more. A monitoring page is a document, not a
     stack of floating cards, and the shadow is tinted with the brand rather
     than black so it sits on the page instead of dirtying it. */
  --shadow-sm: 0 1px 2px color-mix(in srgb, var(--cobalt-950) 8%, transparent);
  --shadow-md:
    0 1px 2px color-mix(in srgb, var(--cobalt-950) 6%, transparent),
    0 4px 12px color-mix(in srgb, var(--cobalt-950) 8%, transparent);

  /* Radii. */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-pill: 999px;

  /* Motion. Product range: long enough to be seen, short enough to stay out
     of the way of someone triaging an outage. Reduced-motion is handled once,
     in appmon.css.

     Two curves only, both decelerating. Nothing in this product overshoots:
     bounce and elastic draw attention to the animation rather than to the
     thing that changed, which is the opposite of what a status display is
     for. --ease-out carries interaction; --ease-out-expo is reserved for the
     one place something announces itself, the status settle. */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-instant: 90ms;
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --dur-slow: 260ms;
  /* The status settle. Longer than anything else here on purpose: it is the
     only motion in the product that has to be noticed from across a desk
     rather than merely felt under a cursor. Still under the 500ms ceiling. */
  --dur-settle: 450ms;

  /* Z-index, named in stacking order so no rule ever needs to invent a 999.
     --z-sticky is what the matrix's frozen row headers use; --z-chrome is the
     frozen top bar and footer, and sits ABOVE it deliberately. Both are
     sticky, the matrix comes later in the document, and equal z-indexes are
     resolved by document order -- so a row header would scroll OVER the nav
     without the gap between these two values. */
  --z-base: 0;
  --z-sticky: 100;
  --z-chrome: 150;
  --z-dropdown: 200;
  --z-overlay: 300;
  --z-modal: 400;
  --z-toast: 500;
}
