/*
 * design.css — the ONE source of truth for colour, type, space and state.
 * @license LQD Research LLC   @author Scott Martin
 *
 * ── Why this file is a rule and not a stylesheet ────────────────────
 * The engine's GUI recorded the lesson twice, in the same sentence:
 * `display:flex` beat the `hidden` attribute, was fixed in one stylesheet, and
 * then written again in the second. Ad-hoc CSS drifts, and it drifts silently —
 * two panels render at once and the tab switch sets a flag that changes nothing.
 *
 * So the rules here are GATED, not followed:
 *
 *   1. Every colour, size, space and duration is a token declared in this file.
 *      No other stylesheet may contain a raw hex, rgb() or px font-size.
 *   2. Visibility is the `hidden` ATTRIBUTE. `[hidden]` is declared exactly
 *      once, here, with `!important`. No stylesheet sets `display` on anything
 *      that can be hidden, and no script writes `style.display`.
 *   3. Every density level defines the same variables. Every dark token has a
 *      light counterpart and the reverse.
 *
 * `check-web.ts` fails the build on each of them. A design system nobody checks
 * is a naming convention.
 *
 * ── The palettes are built, not inverted ────────────────────────────
 * Dark is a second designed palette: the surfaces step UP toward the viewer as
 * they do in light, rather than being a channel-flipped copy in which shadows
 * glow and the risk colours lose their ordering. The risk ramp in particular is
 * chosen twice, because "high" must read as more urgent than "medium" against
 * both grounds and a naive inversion does not preserve that.
 */

/* ═══════════════════════════════════════════════════════════════
   TOKENS — LIGHT
   ═══════════════════════════════════════════════════════════════ */
:root {
  /* Surfaces, stepping up toward the reader. */
  --surface-sunken:  #eef0f4;
  --surface-base:    #f7f8fa;
  --surface-raised:  #ffffff;
  --surface-overlay: #ffffff;
  --surface-hover:   #eaecf1;
  --surface-active:  #e0e4ec;

  /* Ink, from statement to whisper. */
  --ink-strong: #10131a;
  --ink:        #303643;
  --ink-muted:  #5d6577;
  --ink-faint:  #8990a2;   /* 3.01:1 on --surface-base; #8a91a3 was 2.97 */
  --ink-invert: #ffffff;

  /* Lines. */
  --line:        #dfe3ea;
  --line-strong: #c3c9d6;

  /* The one accent. A database tool with a palette is a database tool nobody
     can read; one accent, used only for "this is where you are" and "this is
     the action". */
  --accent:        #2f5fd8;
  --accent-hover:  #2750bb;
  --accent-quiet:  #e7edfc;
  --accent-ink:    #ffffff;

  /* Risk, ordered. These four are the product's most important colours: they
     carry a plan's cost in one glance and they must stay ORDERED under any
     rendering, which is why they are picked rather than derived. */
  --risk-low:      #187d49;   /* 4.53:1 on --risk-low-bg; #1a7f4b was 4.41 */
  --risk-low-bg:   #e4f4ea;
  --risk-medium:   #996300;   /* 4.54:1 on --risk-medium-bg; #9a6400 was 4.48 */
  --risk-medium-bg:#fdf1dc;
  --risk-high:     #b3341f;
  --risk-high-bg:  #fce9e5;
  --risk-critical: #7d1d3f;
  --risk-critical-bg:#f8e2e9;

  /* Evidence, which is a DIFFERENT scale from risk and must not look like it.
     Risk is how much this can hurt; evidence is how much to believe it. Sharing
     a ramp would make a believable-but-harmless finding look dangerous. */
  --evidence-measured: #2f5fd8;   /* plan-actuals, statistics — it ran */
  --evidence-modelled: #6b5bd2;   /* plan-estimate — the planner said so */
  --evidence-static:   #7a8194;   /* a rule read the text */

  --ok:      #187d49;   /* 4.53:1 on --ok-bg; #1a7f4b was 4.41 */
  --ok-bg:   #e4f4ea;
  --warn-bg: #fdf1dc;

  /* Type. Prose and data never share a font — the rule is legibility, not
     taste: a column of numbers in a proportional face cannot be scanned. */
  --font-prose: ui-sans-serif, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono:  ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-lg: 15px;
  --text-xl: 19px;

  --weight-normal: 400;
  --weight-medium: 550;
  --weight-bold:   680;

  /* Space, on a 4px grid. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  --radius-sm: 4px;
  --radius:    6px;
  --radius-lg: 10px;

  --shadow-raised:  0 1px 2px rgba(16, 19, 26, 0.06), 0 1px 1px rgba(16, 19, 26, 0.04);
  --shadow-overlay: 0 8px 28px rgba(16, 19, 26, 0.14), 0 2px 6px rgba(16, 19, 26, 0.08);

  /* Motion. Short enough that nothing waits on it — "fast is the first
     feature", and an animation is latency you chose. */
  --motion-fast: 90ms;
  --motion:      140ms;

  --rail-w:  52px;
  --side-w:  228px;
  --plan-w:  360px;

  color-scheme: light;
}

/* ═══════════════════════════════════════════════════════════════
   TOKENS — DARK, designed rather than inverted
   ═══════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] {
  --surface-sunken:  #0d1015;
  --surface-base:    #14181f;
  --surface-raised:  #1b2029;
  --surface-overlay: #222833;
  --surface-hover:   #232935;
  --surface-active:  #2c3341;

  --ink-strong: #f2f4f8;
  --ink:        #d3d8e2;
  --ink-muted:  #99a1b3;
  --ink-faint:  #6b7488;
  --ink-invert: #0d1015;

  --line:        #262d38;
  --line-strong: #39424f;

  --accent:       #6f9bff;
  --accent-hover: #86adff;
  --accent-quiet: #1b2740;
  --accent-ink:   #0d1015;

  --risk-low:       #4bc98a;
  --risk-low-bg:    #12291f;
  --risk-medium:    #e0a951;
  --risk-medium-bg: #2c2315;
  --risk-high:      #ff8a70;
  --risk-high-bg:   #331a16;
  --risk-critical:  #ff7aa2;
  --risk-critical-bg:#33161f;

  --evidence-measured: #6f9bff;
  --evidence-modelled: #a494ff;
  --evidence-static:   #8b93a6;

  --ok:      #4bc98a;
  --ok-bg:   #12291f;
  --warn-bg: #2c2315;

  --shadow-raised:  0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-overlay: 0 10px 34px rgba(0, 0, 0, 0.55);

  color-scheme: dark;
}

/* The default when nobody has chosen. An explicit `data-theme` always wins,
   which is why this is guarded rather than written as a bare media block. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --surface-sunken:  #0d1015;
    --surface-base:    #14181f;
    --surface-raised:  #1b2029;
    --surface-overlay: #222833;
    --surface-hover:   #232935;
    --surface-active:  #2c3341;
    --ink-strong: #f2f4f8;
    --ink:        #d3d8e2;
    --ink-muted:  #99a1b3;
    --ink-faint:  #6b7488;
    --ink-invert: #0d1015;
    --line:        #262d38;
    --line-strong: #39424f;
    --accent:       #6f9bff;
    --accent-hover: #86adff;
    --accent-quiet: #1b2740;
    --accent-ink:   #0d1015;
    --risk-low:       #4bc98a;
    --risk-low-bg:    #12291f;
    --risk-medium:    #e0a951;
    --risk-medium-bg: #2c2315;
    --risk-high:      #ff8a70;
    --risk-high-bg:   #331a16;
    --risk-critical:  #ff7aa2;
    --risk-critical-bg:#33161f;
    --evidence-measured: #6f9bff;
    --evidence-modelled: #a494ff;
    --evidence-static:   #8b93a6;
    --ok:      #4bc98a;
    --ok-bg:   #12291f;
    --warn-bg: #2c2315;
    --shadow-raised:  0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-overlay: 0 10px 34px rgba(0, 0, 0, 0.55);
    color-scheme: dark;
  }
}

/* ═══════════════════════════════════════════════════════════════
   DENSITY — one toggle, three levels, every surface honours it
   ═══════════════════════════════════════════════════════════════ */
:root, :root[data-density="cosy"] {
  --row-h:    28px;
  --cell-pad: var(--space-2);
  --gap:      var(--space-3);
  --text-ui:  var(--text-md);
}
:root[data-density="compact"] {
  --row-h:    22px;
  --cell-pad: var(--space-1);
  --gap:      var(--space-2);
  --text-ui:  var(--text-sm);
}
:root[data-density="comfortable"] {
  --row-h:    36px;
  --cell-pad: var(--space-3);
  --gap:      var(--space-4);
  --text-ui:  var(--text-lg);
}

/* ═══════════════════════════════════════════════════════════════
   STATE — the rule that cost two bugs to learn
   ═══════════════════════════════════════════════════════════════ */

/* Declared ONCE, with `!important`, so no later `display:` can beat it. The
   engine's GUI lost this twice; here it is a gated invariant. */
[hidden] { display: none !important; }

/* ═══════════════════════════════════════════════════════════════
   PRIMITIVES
   ═══════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--surface-base);
  color: var(--ink);
  font-family: var(--font-prose);
  font-size: var(--text-ui);
  -webkit-font-smoothing: antialiased;
}

/* Data is monospace and tabular; prose is not. `tabular-nums` is the half people
   forget — a proportional-digit monospace column still fails to align. */
.mono, code, pre, .grid td, .grid th, .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.num { text-align: right; }

button {
  font: inherit;
  color: var(--ink);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: var(--space-1) var(--space-3);
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
button:hover:not(:disabled) { background: var(--surface-hover); }
button:disabled { opacity: 0.5; cursor: default; }
button.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  font-weight: var(--weight-medium);
}
button.primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
button.danger { border-color: var(--risk-high); color: var(--risk-high); }

/* Confirmation is a BUTTON STATE, not a popup. Nothing in this product is
   modal — database work is exploratory and a dialog breaks the thought. */
button[data-confirm="armed"] {
  background: var(--risk-high); border-color: var(--risk-high); color: var(--ink-invert);
}

input, textarea, select {
  font: inherit;
  color: var(--ink);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
}
input:focus-visible, textarea:focus-visible, button:focus-visible, select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

a { color: var(--accent); }

/* ── Risk and evidence, as components rather than as ad-hoc colour ── */
.risk { font-weight: var(--weight-medium); }
.risk[data-risk="low"]      { color: var(--risk-low); }
.risk[data-risk="medium"]   { color: var(--risk-medium); }
.risk[data-risk="high"]     { color: var(--risk-high); }
.risk[data-risk="critical"] { color: var(--risk-critical); }

.pill {
  display: inline-block;
  /* A pill is sometimes a `<button>` — the plan count is, because on a narrow
     screen it opens the panel. `border: 0` and the inherited font are what stop
     a button from arriving with the platform's own chrome and looking like a
     different component from the span beside it. */
  border: 0;
  font-family: inherit;
  padding: 0 var(--space-2);
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 18px;
}
.pill[data-risk="low"]      { background: var(--risk-low-bg);      color: var(--risk-low); }
.pill[data-risk="medium"]   { background: var(--risk-medium-bg);   color: var(--risk-medium); }
.pill[data-risk="high"]     { background: var(--risk-high-bg);     color: var(--risk-high); }
.pill[data-risk="critical"] { background: var(--risk-critical-bg); color: var(--risk-critical); }

/* Evidence is its own ramp. `plan-actuals` ran; `static` read the text. */
.evidence { font-size: var(--text-xs); font-family: var(--font-mono); }
.evidence[data-evidence="plan-actuals"]  { color: var(--evidence-measured); }
.evidence[data-evidence="statistics"]    { color: var(--evidence-measured); }
.evidence[data-evidence="plan-estimate"] { color: var(--evidence-modelled); }
.evidence[data-evidence="default"]       { color: var(--evidence-static); }
.evidence[data-evidence="static"]        { color: var(--evidence-static); }

/* ── The refusal component ──────────────────────────────────────────
   The engine refuses on purpose, and a UI that renders that as a red box
   saying "Error" has destroyed the product's main asset. A refusal always
   carries three things: what was refused, WHY, and what would make it pass. */
.refusal {
  border: 1px solid var(--risk-high);
  border-left-width: 3px;
  background: var(--risk-high-bg);
  border-radius: var(--radius);
  padding: var(--space-3);
  color: var(--ink-strong);
}
.refusal h4 { margin: 0 0 var(--space-2); font-size: var(--text-ui); }
.refusal .why { font-family: var(--font-mono); font-size: var(--text-sm); white-space: pre-wrap; }
.refusal .remedy { margin-top: var(--space-2); color: var(--ink-muted); }

/* ── Skeletons, never spinners ──────────────────────────────────────
   A spinner says "something is happening"; a skeleton says "this shape is
   coming, here is how much". And where the engine knows the count, the count is
   shown instead of either. */
.skeleton {
  background: linear-gradient(90deg, var(--surface-hover) 25%,
              var(--surface-active) 37%, var(--surface-hover) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.1s ease infinite;
  border-radius: var(--radius-sm);
  height: var(--row-h);
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
  * { transition-duration: 1ms !important; }
}
