/*
 * KD-Plant — Copyright (c) 2026 Kevin Dennis. All rights reserved.
 * Proprietary and confidential. Not open source. See LICENSE.
 */
/* =====================================================================
   The tokens. One file, loaded by every page.

   These lived inside dashboard.css, which meant a page that did not load
   the dashboard's stylesheet had no palette and had to invent one. Five
   more vocabularies grew that way - --lg-* on the sign-in pages, --md-* on
   My Day, --fp-* on the first-password screen - each a hand-copy of the
   same colours, and they had already drifted apart. Moving the tokens out
   lets every page take the same set without taking the dashboard with it.

   Nothing here is new below the marked line: the values are exactly the
   ones dashboard.css declared. What follows the line is the part that was
   missing - the scales. There was no type scale, no spacing scale and no
   radius scale, so pages picked their own: 118 distinct font sizes, 48
   radii and 125 shadow values across the views.
   ===================================================================== */

:root {
  /* The typeface, named once. Every page reached for 'Inter' by hand, so a
     change of face meant editing nine files and finding the two that were
     missed. IBM Plex Sans was drawn for an engineering company: squared
     terminals, low contrast, and a mono sibling that lines a column of
     readings up without looking like a typewriter.

     The mono is not decoration. A work order number, a meter reading and a
     cost are all things somebody compares down a column, and proportional
     digits make that harder than it needs to be. */
  --db-font: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --db-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --db-bg: transparent;
  --db-surface: #ffffff;
  --db-surface-2: #f8fafc;
  --db-surface-3: #f1f5f9;
  --db-border: #e9edf3;
  --db-border-strong: #cbd5e1;
  --db-text: #0f172a;
  --db-text-2: #475569;
  /* Was slate-400, which measures 2.6:1 on white. It is the colour of every
     timestamp, every "by <name>", every unit and count in the product, so it
     is read constantly and was under the 4.5:1 body text needs. slate-500
     reads at 4.8:1 and is still clearly the quietest of the three. */
  --db-text-3: #64748b;
  --db-accent: #4f46e5;
  --db-accent-soft: #eef2ff;
  --db-on-accent: #fff;
  --db-good: #10b981;
  --db-good-soft: #ecfdf5;
  --db-warn: #f59e0b;
  --db-warn-soft: #fffbeb;
  --db-bad: #f43f5e;
  --db-bad-soft: #fff1f2;
  /* The signal colours above are chosen to be seen at a glance - a dot, a
     bar, a border, a chart series - and they are right for that. Set as text
     on their own pale ground they measure 2.1 to 3.3:1, so every green, amber
     and red chip in light mode was below AA. These are the same hues taken
     dark enough to read, and they are only ever used for text. */
  --db-accent-fg: #4338ca;
  --db-good-fg: #047857;
  --db-warn-fg: #b45309;
  --db-bad-fg: #be123c;
  /* The sidebar and the phone drawer are dark navy in both themes, so their
     text does not follow the page. These two are deliberately the same in
     light and dark and exist so no rule has to state a bare colour to reach
     them. */
  --db-on-dark: #ffffff;
  --db-on-dark-2: #94a3b8;
  --db-grid: #f1f5f9;
  --db-shadow: 0 1px 2px rgba(15, 23, 42, .04), 0 6px 20px rgba(15, 23, 42, .05);
  --db-shadow-lg: 0 12px 32px rgba(15, 23, 42, .12);
  --db-radius: 14px;

  /* ---------------------------------------------------------------- *
     Encoding palette - one channel per job.

     Every value below was produced by iterating against the palette
     validator, not chosen by eye. Each ramp passes lightness-monotonicity,
     adjacent-ΔL, light-end contrast and single-hue in the mode it is
     declared for. The dark steps are RE-ANCHORED for the dark surface,
     never a flip of the light ones - the step nearest the surface has to
     clear contrast against that surface.
     ---------------------------------------------------------------- */

  /* Priority - ORDINAL, one hue, low → urgent.
     light-end 2.69:1 · hue spread 7° · all ΔL gaps ≥ .06 */
  --pri-low: #fb7185;
  --pri-medium: #f43f5e;
  --pri-high: #be123c;
  --pri-urgent: #881337;

  /* Capacity load - SEQUENTIAL magnitude, one hue.
     light-end 2.98:1 · hue spread 0° */
  --load-1: #818cf8;   /* ≤ 50% */
  --load-2: #6366f1;   /* ~ 75% */
  --load-3: #4f46e5;   /* ~ 90% */
  --load-4: #3730a3;   /* at capacity */
  --load-track: #eef0f6;

  /* Work type - NOMINAL categorical, 4 slots.
     worst adjacent CVD ΔE 17.1 · normal-vision 18.1
     Sanitation was added last and had to clear the existing three under
     simulated protanopia and deuteranopia, which ruled out the obvious teal
     (14.1 against reactive) and settled on cyan at 27.4. */
  --type-pm: #10b981;
  --type-planned: #0ea5e9;
  --type-project: #7c3aed;
  --type-reactive: #f43f5e;
  --type-sanitation: #a21caf;

  /* Status - RESERVED. Never used for identity, always with icon + label. */
  --st-good: #0ca30c;
  --st-warning: #fab219;
  --st-serious: #ec835a;
  --st-critical: #d03b3b;
  --st-good-soft: #ecfdf5;
  --st-warning-soft: #fffbeb;
  --st-critical-soft: #fef2f2;
  /* The four above are picked to be told apart at a glance on a board, which
     is what a status colour is for. As text they run from 1.8 to 4.4:1, so
     these are the same four taken dark enough to read and used only for
     words. --st-serious has no soft ground of its own; it sits on the page. */
  --st-good-fg: #047857;
  --st-warning-fg: #b45309;
  --st-serious-fg: #c2410c;
  --st-critical-fg: #b91c1c;

  /* Availability is structure, not data - neutral only, never a hue. */
  --avail-off: #f1f3f7;
  --avail-off-line: #e2e6ee;
  --avail-leave: #eceef3;
  --avail-leave-line: #d8dce6;
}

html[data-theme="dark"] {
  --pri-low: #fda4af;
  --pri-medium: #fb7185;
  --pri-high: #f43f5e;
  --pri-urgent: #be123c;

  --load-1: #a5b4fc;
  --load-2: #818cf8;
  --load-3: #6366f1;
  --load-4: #4f46e5;
  --load-track: #232c42;

  --type-pm: #059669;
  --type-planned: #0284c7;
  --type-project: #a78bfa;
  --type-reactive: #e11d48;
  --type-sanitation: #e879f9;

  --st-good-soft: #10281f;
  --st-warning-soft: #2a2109;
  --st-critical-soft: #2c1119;
  --st-good-fg: #34d399;
  --st-warning-fg: #fbbf24;
  --st-serious-fg: #fdba74;
  --st-critical-fg: #fb7185;

  --avail-off: #171f31;
  --avail-off-line: #202a40;
  --avail-leave: #1a2133;
  --avail-leave-line: #242e46;
}

html[data-theme="dark"] {
  --db-bg: transparent;
  --db-surface: #131a2a;
  --db-surface-2: #0f1626;
  --db-surface-3: #1b2439;
  --db-border: #26314a;
  --db-border-strong: #3a4763;
  --db-text: #e8edf7;
  --db-text-2: #a6b2c9;
  --db-text-3: #7e8ca6;
  --db-accent: #818cf8;
  --db-accent-soft: #1e263f;
  --db-on-accent: #0f172a;
  --db-good: #34d399;
  --db-good-soft: #10281f;
  --db-warn: #fbbf24;
  --db-warn-soft: #2a2109;
  --db-bad: #fb7185;
  --db-bad-soft: #2c1119;
  /* On a dark ground the signal colours already read at 5 to 9.5:1, so the
     text variants are the signal colours. They exist here so a rule can name
     one token and be right in both themes. */
  --db-accent-fg: #818cf8;
  --db-good-fg: #34d399;
  --db-warn-fg: #fbbf24;
  --db-bad-fg: #fb7185;
  --db-grid: #222c42;
  --db-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 6px 20px rgba(0, 0, 0, .25);
  --db-shadow-lg: 0 12px 32px rgba(0, 0, 0, .45);

  /* Native browser UI has to be told the page is dark. Without this the
     date-picker panel, select dropdowns, and scrollbars render as white
     rectangles on a dark screen - the specific tell that a dark theme was
     bolted on rather than designed. One declaration fixes every native
     control in the app. */
  color-scheme: dark;
}

/* =====================================================================
   The scales. Everything above this line is the palette as it already
   was; everything below it is new.

   A page had no scale to reach for, so it invented one: counted across
   the views and stylesheets there are 118 distinct font sizes, 48 radii
   and 125 shadow values, which is why two cards side by side rarely
   agree. These are deliberately short. A scale nobody can hold in their
   head is a scale people work around.
   ===================================================================== */

:root {
  /* Type. Six sizes and two line heights. The names say the job, not the
     number, so a label stays a label when the scale is retuned. */
  --db-fs-cap: 0.68rem;      /* uppercase micro-label, tracked */
  --db-fs-sm: 0.78rem;       /* table cell, secondary text */
  --db-fs-md: 0.86rem;       /* body, form controls */
  --db-fs-lg: 1rem;          /* card title */
  --db-fs-xl: 1.35rem;       /* page heading */
  --db-fs-2xl: 1.9rem;       /* a number that is the point of the card */
  --db-lh-tight: 1.25;
  --db-lh: 1.5;
  --db-track-cap: 0.07em;    /* letter-spacing for the uppercase labels */

  /* Spacing. A four-pixel base, which is what the existing padding
     values round to most often. */
  --db-sp-1: 4px;
  --db-sp-2: 8px;
  --db-sp-3: 12px;
  --db-sp-4: 16px;
  --db-sp-5: 20px;
  --db-sp-6: 24px;
  --db-sp-7: 32px;
  --db-sp-8: 48px;

  /* Radius. Controls and chips are tighter than cards; cards are tighter
     than the popup. Three values and a pill. */
  --db-r-sm: 6px;
  --db-r: 10px;
  --db-r-lg: 14px;
  --db-r-pill: 999px;

  /* Density. A row is 40px at a desk and 48px under a thumb. */
  --db-row-h: 40px;
  --db-ctl-h: 32px;

  /* Motion, in one place, so a preference for less of it is honoured in
     one place too. */
  --db-dur-fast: 120ms;
  --db-dur: 180ms;
  --db-ease: cubic-bezier(.2, .8, .3, 1);

  /* The focus ring. Most of the product's own controls had none. */
  --db-focus: var(--db-accent, #4f46e5);
  --db-focus-ring: 0 0 0 3px color-mix(in srgb, var(--db-accent, #4f46e5) 28%, transparent);

  /* Lifecycle, kept apart from the validation colours. A completed job is
     not a "success" and a cancelled one is not a "danger"; reusing those
     made a closed work order read as an alarm. */
  --db-life-open: #b45309;
  --db-life-open-soft: #fffbeb;
  --db-life-progress: #1d4ed8;
  --db-life-progress-soft: #eff6ff;
  --db-life-hold: #6d28d9;
  --db-life-hold-soft: #f5f3ff;
  --db-life-done: #047857;
  --db-life-done-soft: #ecfdf5;
  --db-life-cancelled: #64748b;
  --db-life-cancelled-soft: #f1f5f9;
}

/* Touch: the phone, and any board driven by a thumb. */
html[data-density="comfortable"], .is-touch {
  --db-row-h: 48px;
  --db-ctl-h: 40px;
  --db-fs-md: 1rem;
}

html[data-theme="dark"] {
  --db-focus-ring: 0 0 0 3px color-mix(in srgb, var(--db-accent, #818cf8) 34%, transparent);

  /* Re-anchored for a dark ground rather than inverted: the tint carries
     the meaning and the text has to stay legible on it. */
  --db-life-open: #fbbf24;
  --db-life-open-soft: #2a2109;
  --db-life-progress: #93c5fd;
  --db-life-progress-soft: #14213d;
  --db-life-hold: #c4b5fd;
  --db-life-hold-soft: #221a3d;
  --db-life-done: #6ee7b7;
  --db-life-done-soft: #10281f;
  --db-life-cancelled: #94a3b8;
  --db-life-cancelled-soft: #1b2439;
}

@media (prefers-reduced-motion: reduce) {
  :root { --db-dur-fast: 0ms; --db-dur: 0ms; }
}
