/*
 * Torwarden design tokens — SPEC §8.2, §8.3, §8.4, §19, §24.
 *
 * Dark only. There is no light palette, no data-theme attribute and no media
 * query: the product renders one way, and everything below is that one way.
 *
 * Two tiers, and the split is load-bearing:
 *
 *   1. Primitives (--grey-10, --red-50) — raw values, referenced only by the
 *      alias layer below. A component that names one of these is a component
 *      that will be wrong the next time the palette moves.
 *   2. Aliases (--fg-muted, --status-ok-fg) — the only thing any component is
 *      allowed to touch. A test enforces it.
 *
 * The language is Blockprint: dark neo-brutalism (§24). Four things about it
 * are worth knowing before changing a number.
 *
 *   - The border is DARKER than the surface it encloses. Everywhere else in
 *     interface design an edge is lighter than its fill; here --edge is
 *     --grey-00 and a card is --grey-15, because the look depends on the card
 *     reading as a sheet cut out and laid on the page rather than as a region
 *     of it.
 *   - The shadow is solid and un-blurred. A blurred shadow describes light; an
 *     offset one describes a second sheet underneath, which is the only claim
 *     this language makes about depth. It must never soften on hover.
 *   - The accent carries dark ink, not white. White on #8B5CF6 is 4.24:1 and
 *     fails; the canvas colour on it is 4.53:1 and passes. There is a test.
 *   - The status hues moved to the Tailwind-ish set the brief names, which
 *     shifted "ok" from 135° to 160° and put the old cyan series line 18° away
 *     from it. The series palette below was re-solved for the new hues rather
 *     than nudged.
 */

/* ---------------------------------------------------------------- primitives */
:root {
  --white: #ffffff;

  /* The elevation ladder.
     
     Slate at ~216°, every rung carrying the same cast. The cast has to be
     *consistent* — a ladder where some steps are tinted and some are not reads
     as a mistake rather than as either choice — and there is a test for the
     spread rather than for neutrality. */
  --grey-00: #05070a; /* the hard edge, and the window behind everything */
  --grey-05: #0d0f12; /* the app canvas */
  --grey-10: #12161c; /* secondary elevation: sidebar, sticky header, controls */
  --grey-15: #161a20; /* tertiary: cards and panels */
  --grey-20: #1b2029; /* raised within a card: table headers, inset wells */
  --grey-25: #2a323f; /* the internal rule that is meant to be seen */
  --grey-30: #3b4657;
  --grey-40: #64748b;
  --grey-50: #94a3b8; /* the muted-text anchor */
  --grey-60: #a8b4c4;
  --grey-70: #c3ccd8;
  --grey-80: #dde3ea;
  --grey-90: #f1f5f9; /* the label colour, used at opacity */
  --grey-99: #f8fafc;

  /* A control sits on the sidebar's elevation, not the card's, so a field is
     visibly inset into the panel that holds it. Its border is the hard edge,
     like everything else. */
  --control-bg:     #12161c;
  --control-border: #05070a;

  /* Status. The brief's set: crimson, amber, cyber green. */
  --red-50:    #ef4444;
  --orange-50: #f59e0b;
  --green-50:  #10b981;
  --teal-50:   #40c8e0;
  --blue-50:   #3b82f6;
  --purple-50: #bf5af2;
  --pink-50:   #ec4899;
  --yellow-50: #facc15;

  /* The accent, in three weights, because one violet cannot do both jobs.
     
     #8b5cf6 is the brand fill. As text on the canvas it is 3.9:1 — below the
     4.5:1 every link has to meet — so the same hue is carried up to #c4b5fd for
     anything the eye reads rather than looks at. And the fill takes dark ink:
     white on it is 4.24:1 and fails, the canvas colour is 4.53:1 and passes. */
  --violet-50: #8b5cf6; /* the brand fill: buttons, active states, gauge arcs */
  --violet-60: #a78bfa; /* the same fill, hovered */
  --violet-70: #c4b5fd; /* text, links and strokes on dark */

  /* Series colours for charts.
     
     Solved, not picked. Every hue is at least 30° from red, orange and green,
     at least 25° from every other series, and clears 4.5:1 on the canvas — and
     the contrasts are deliberately spread so the six also separate in greyscale
     and on a printout, not only by hue.
     
     Only two sit on the green side of the wheel. The band between orange and
     "ok" is 62° wide, and putting three lines in it produced three shades of
     the same olive; the rest spread across the wide blue-to-magenta arc. */
  --series-1: #80b22a; /*  82°  7.6:1 */
  --series-2: #34a72a; /* 115°  6.1:1 */
  --series-3: #3090c0; /* 200°  5.4:1 */
  --series-4: #6a79dc; /* 232°  4.9:1 */
  --series-5: #a47cde; /* 264°  6.0:1 */
  --series-6: #d476db; /* 296°  6.8:1 */

  /* The colourblind-safe alternative (§8.4), kept: the palette changed, the
     accessibility requirement did not. Blue/orange/white separate under every
     common form of colour vision deficiency, which red/green does not.
     
     Solved rather than chosen. The obvious triple — a bright blue, a bright
     amber and white — fails: under deuteranopia a bright amber flattens to
     within 1.3:1 of white, so warning and critical become the same colour for
     the people this palette exists for. */
  --cvd-ok:   #1f8cf9;
  --cvd-warn: #d68800;
  --cvd-crit: #ffffff;

  /* Type. System stacks rather than a webfont: §1 promises the product works
     fully offline, and a webfont is either an outbound request on first paint
     or half a megabyte in the binary.
     
     --display is the heavy face this language needs for headings. Arial Black
     ships on Windows and macOS and resolves to a genuinely heavy grotesk on
     Linux; where nothing matches, the stack falls back to the UI face at 800,
     which is the same shape one weight lighter rather than a different look. */
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display",
          "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono",
          "Cascadia Mono", "Fira Code", Menlo, Consolas, "Liberation Mono", monospace;
  --display: "Arial Black", "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* Type scale. */
  --t-1: 11px;
  --t-2: 12px;
  --t-3: 13px;
  --t-4: 15px;
  --t-5: 19px;
  --t-6: 26px;

  /* Spacing. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-8: 48px;

  --pad-card: 20px;

  /* Radii. Chunky, per §24: a 14px corner on a card and a full pill on a
     badge. The hard offset underneath is what keeps that from reading as
     soft — rounded and shadowed is friendly, rounded and *offset* is not. */
  --r-1: 6px;
  --r-2: 10px;
  --r-3: 14px;
  --r-pill: 999px;

  /* Motion. 200ms for state changes; the press is much faster, because a
     button that takes 200ms to go down does not feel pressed, it feels slow. */
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;
  --dur-press: 80ms;
  --ease:     cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* ------------------------------------------------------------- aliases --- */
  color-scheme: dark;

  --canvas:    var(--grey-05);
  --surface:   var(--grey-10);
  --surface-2: var(--grey-15);
  --surface-3: var(--grey-20);
  --sunken:    var(--grey-00);

  /* The hard edge, its weight, and the offset behind it. Three tokens because
     three things have to agree: the border a surface draws, the distance its
     shadow is offset, and the distance it travels when pressed. A press that
     does not land exactly on its own shadow is the one detail that gives this
     language away. */
  --edge:        var(--grey-00);
  --edge-w:      2px;
  --shadow-hard:    5px 5px 0 0 rgba(0, 0, 0, 1);
  --shadow-hard-sm: 3px 3px 0 0 rgba(0, 0, 0, 1);
  --press:    translate(5px, 5px);
  --press-sm: translate(3px, 3px);

  /* Rules *inside* a surface, which are a different thing from its edge. */
  --hairline:        var(--grey-20);
  --hairline-strong: var(--grey-25);
  --border:          var(--grey-25);
  --border-strong:   var(--grey-30);

  --fg:         rgba(241, 245, 249, .92);
  --fg-strong:  var(--white);
  --fg-muted:   rgba(241, 245, 249, .66);
  --fg-dim:     rgba(241, 245, 249, .46);
  --fg-inverse: var(--grey-05);

  /* Backdrop behind the navigation drawer on small screens. */
  --scrim: rgba(0, 0, 0, .62);

  /* Brand. These two are the only colours in the file that are deliberately
     the same in every theme and under the colourblind palette, because they
     are not describing state or surface — they are the logo. A brand mark that
     shifts hue with the theme is a brand mark nobody recognises twice.

     They are here rather than inline in the SVG so the mark is not a place
     colours hide from the stylesheet. --brand-ink is a fixed dark rather than
     a foreground token: it sits on the lime, never on a page surface, so it
     must not follow the theme the way text does.

     The lime is at hue 92°, not the 72° it started at. 72° was 34.5° from the
     warning amber — inside the 32° floor the series palette holds itself to —
     and the mark is a solid field of it sitting a few pixels above a warning
     pill in the rail. 92° is the midpoint of the amber-to-healthy-green gap,
     which is the furthest a green can get from both: 54.7° from either. Still
     unmistakably the same colour, and no longer flirting with a status. */
  --brand-lime: #81F222;
  --brand-ink:  #0D0F12;

  --accent:        var(--violet-50);
  --accent-strong: var(--violet-60);
  --accent-text:   var(--violet-70);
  /* Dark ink on the accent, not white. See the note at the top of the file. */
  --accent-fg:     var(--grey-05);
  --accent-soft:   rgba(139, 92, 246, .20);
  --accent-line:   rgba(167, 139, 250, .50);
  --focus:         var(--violet-60);

  /* A status pill in this language is a filled block with a hard edge, not a
     tinted lozenge: fill is the background, fg is the ink on it, and the two
     have to clear 4.5:1 against each other. The -bg and -line values remain for
     the places that still want a tint — a highlighted table row, a soft ring. */
  --status-ok-fg:   var(--green-50);
  --status-ok-fill: var(--green-50);
  --status-ok-ink:  #032018;
  --status-ok-bg:   rgba(16, 185, 129, .14);
  --status-ok-line: rgba(16, 185, 129, .30);
  --status-ok-glow: rgba(16, 185, 129, .45);

  --status-warn-fg:   var(--orange-50);
  --status-warn-fill: var(--orange-50);
  --status-warn-ink:  #2a1a02;
  --status-warn-bg:   rgba(245, 158, 11, .14);
  --status-warn-line: rgba(245, 158, 11, .30);
  --status-warn-glow: rgba(245, 158, 11, .45);

  --status-crit-fg:   var(--red-50);
  --status-crit-fill: var(--red-50);
  --status-crit-ink:  #2a0606;
  --status-crit-bg:   rgba(239, 68, 68, .15);
  --status-crit-line: rgba(239, 68, 68, .32);
  --status-crit-glow: rgba(239, 68, 68, .50);

  --status-unknown-fg:   var(--grey-50);
  --status-unknown-fill: var(--grey-40);
  --status-unknown-ink:  #f8fafc;
  --status-unknown-bg:   rgba(148, 163, 184, .12);
  --status-unknown-line: rgba(148, 163, 184, .26);

  /* Maintenance stays teal. Violet is the brand, and a status wearing the
     accent reads as "selected" rather than as "suppressed" — the one state
     whose whole job is to say *this is not a problem* would be shouting
     loudest. */
  --status-maint-fg:   var(--teal-50);
  --status-maint-fill: var(--teal-50);
  --status-maint-ink:  #04222a;
  --status-maint-bg:   rgba(64, 200, 224, .14);
  --status-maint-line: rgba(64, 200, 224, .30);

  /* Metric identity, not metric severity. A gauge is violet because it is the
     CPU gauge, the same way a chart series is violet — these alias the series
     palette, which is the one solved to sit clear of every status hue. When a
     gauge crosses the hot line it drops these and takes the status colour. */
  --metric-cpu:     var(--series-5);
  --metric-memory:  var(--series-3);
  --metric-storage: var(--series-1);
  /* Throughput had no token, so the hosts table drew it in --series-2 — a green
     sitting one column away from three meters where green means "fine". Blue
     is the one identity hue no status colour is anywhere near. */
  --metric-network: var(--series-3);

  /* The grid is drawn to be seen. At 7% it was a hint that something was
     there, which is the opposite of what a dashed rule is for: a dash is only
     legible once each segment is, and half-visible dashes read as noise. */
  --chart-grid:      rgba(255, 255, 255, .11);
  /* The baseline the plot sits on. Not --edge: the card edge works because it
     has a shadow under it, and inside a card the same near-black just
     disappears. This is a rule that has to be visible on its own. */
  --chart-baseline:  var(--grey-30);
  --chart-axis:      var(--grey-50);
  --chart-crosshair: rgba(255, 255, 255, .32);
  --chart-band:      rgba(139, 92, 246, .16);

  /* Vibrancy. The fill is deliberately opaque enough to read text against on
     any background — a popover that is genuinely translucent is a popover whose
     text sits on whatever happens to be behind it. */
  --glass:      rgba(18, 22, 28, .94);
  --glass-edge: var(--grey-00);
  --blur:       blur(18px) saturate(150%);

  /* The soft shadows this language mostly does not use. They remain for the two
     things that genuinely float above the page rather than sitting on it. */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-2: 10px 10px 0 0 rgba(0, 0, 0, 1);
  --glow:        0 0 0 2px var(--accent);
  --accent-glow: rgba(196, 181, 253, .60);

  /* The sidebar's width, and the width it collapses to. A token because the
     grid and the collapsed rail both need it and they must not disagree. */
  --rail-w: 232px;
}

/* The colourblind-safe alternative, applied over the aliases above. */
:root[data-contrast="cvd"] {
  --status-ok-fg:     var(--cvd-ok);
  --status-ok-fill:   var(--cvd-ok);
  --status-ok-line:   var(--cvd-ok);
  --status-warn-fg:   var(--cvd-warn);
  --status-warn-fill: var(--cvd-warn);
  --status-warn-line: var(--cvd-warn);
  --status-crit-fg:   var(--cvd-crit);
  --status-crit-fill: var(--cvd-crit);
  --status-crit-line: var(--cvd-crit);
}
