/* ═══════════════════════════════════════════════════════════════════════════
   KSF DESIGN LANGUAGE — v1
   The single source of truth for every KSF web property.

   Import it first, before any app stylesheet:
       <link rel="stylesheet" href="/css/ksf.css" />
       <link rel="stylesheet" href="/css/app.css" />

   Two layers:
     1. TOKENS  — bare custom properties (--page, --ink, --accent …). Apps
                  reference these instead of hard-coding hex values.
     2. PRIMITIVES — .ksf-* classes for the handful of components every KSF
                  app rebuilds anyway (button, card, field, badge, chip).
                  Prefixed so they never collide with existing app classes.

   House rules, in order of importance:
     · Low contrast is deliberate. Ink is #e6e4db, never #fff; the page is
       #131312, never #000. Do not "fix" this — glare is the thing we are
       avoiding.
     · Colour carries meaning: --accent = interactive, --ok/--warn/--down =
       status. Never decorative.
     · Pick radii and spacing from the scales below. A new magic number is a
       bug, not a design decision.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── 1. TOKENS ───────────────────────────────────────────────────────────── */
:root {
  color-scheme: dark;

  /* Planes — page sits lowest, each step reads as slightly nearer the viewer */
  --page: #131312;          /* app background */
  --surface: #1a1a19;       /* cards, panels, chart plane */
  --surface-2: #242423;     /* inputs, controls, raised chips */
  --surface-hover: #2e2e2c; /* hover/active state of an interactive surface */

  /* Ink — four tiers, dimmest last. Below --faint, use an icon, not text. */
  --ink: #e6e4db;           /* primary text */
  --ink-2: #c3c2b7;         /* secondary text */
  --muted: #898781;         /* labels, axis text, captions */
  --faint: #605f59;         /* placeholders, disabled, decorative glyphs */

  /* Lines & fills */
  --line: #2c2c2a;          /* hairline border, gridline */
  --line-strong: #383835;   /* baseline, divider that must read as a break */
  --ring: rgba(230, 228, 219, 0.13);  /* border on raised/hovered elements */
  --wash: rgba(230, 228, 219, 0.05);  /* faintest fill, zebra striping */

  /* Accent — the only blue. Interactive affordances and nothing else. */
  --accent: #3987e5;
  --accent-hover: #5c9cea;
  --accent-wash: rgba(57, 135, 229, 0.13);

  /* Status — meaning-bearing, matched to the dataviz palette */
  --ok: #0ca30c;
  --warn: #fab219;
  --down: #d03b3b;
  --ok-wash: rgba(12, 163, 12, 0.13);
  --warn-wash: rgba(250, 178, 25, 0.13);
  --down-wash: rgba(208, 59, 59, 0.13);

  /* Categorical series — for charts with more than one line/bar/slice.
     Ordered: use --series-1 first and only add colours as series appear.
     These are hues, not statuses: never colour a "failed" series red by
     coincidence of ordering. Taken from the validated dataviz palette
     already in use by homelab_dashboard's charts. */
  --series-1: #3987e5;      /* blue — same value as --accent */
  --series-2: #199e70;      /* green */
  --series-3: #c98500;      /* amber */
  --series-4: #e66767;      /* red */
  --series-5: #9085e9;      /* purple */

  /* Type — Inter when it loads, system stack when it doesn't */
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --text-xs: 0.72rem;
  --text-sm: 0.8rem;
  --text-base: 0.9rem;
  --text-md: 1rem;
  --text-lg: 1.15rem;
  --text-xl: 1.5rem;
  --text-display: 4.6rem;

  /* Space — 4px rhythm. Gaps and padding come from here. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 22px;
  --sp-6: 28px;

  /* Radius — four steps plus a pill. Nothing in between. */
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-pill: 999px;

  /* Elevation — shadow is for things that float, not for every card */
  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.2);
  --shadow-pop: 0 8px 32px rgba(0, 0, 0, 0.45);

  /* Motion — one curve, two durations */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 0.2s;
  --dur-slow: 0.35s;
}

/* ─── 2. BASE ─────────────────────────────────────────────────────────────── */
/* Opt-in: add class="ksf" to <body> (or :root) so importing the tokens alone
   never restyles an app that isn't ready for it. */
body.ksf {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.ksf h1, .ksf h2, .ksf h3 { font-weight: 600; letter-spacing: -0.01em; }
.ksf a { color: var(--accent); text-decoration: none; }
.ksf a:hover { color: var(--accent-hover); text-decoration: underline; }
.ksf code, .ksf pre { font-family: var(--font-mono); font-size: var(--text-sm); }

/* One focus treatment everywhere, keyboard-only */
.ksf :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.ksf ::-webkit-scrollbar { width: 6px; height: 6px; }
.ksf ::-webkit-scrollbar-track { background: transparent; }
.ksf ::-webkit-scrollbar-thumb { background: var(--faint); border-radius: 3px; }
.ksf ::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* Section label — the small uppercase heading above a group of things */
.ksf-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.09em;
}

/* ─── 3. PRIMITIVES ───────────────────────────────────────────────────────── */

/* Card — the default container. Shadow only when it floats above content. */
.ksf-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-4);
}
.ksf-card.raised { box-shadow: var(--shadow-card); }
.ksf-card.interactive { transition: all var(--dur) var(--ease); cursor: pointer; }
.ksf-card.interactive:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
  box-shadow: 0 4px 16px var(--accent-wash);
}

/* Button — secondary by default; .primary for the one action that matters. */
.ksf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 7px 14px;
  background: var(--surface-2);
  border: 1px solid var(--ring);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--dur) var(--ease);
}
.ksf-btn svg { width: 14px; height: 14px; }
.ksf-btn:hover { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); }
.ksf-btn:disabled { opacity: 0.4; pointer-events: none; }
.ksf-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.ksf-btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.ksf-btn.danger:hover { background: var(--down-wash); border-color: var(--down); color: var(--down); }
.ksf-btn.ghost { background: none; border-color: transparent; color: var(--muted); }
.ksf-btn.ghost:hover { background: var(--surface-2); color: var(--ink); border-color: var(--ring); }

/* Icon-only button — square, same states as .ksf-btn */
.ksf-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  transition: all var(--dur) var(--ease);
}
.ksf-icon-btn svg { width: 15px; height: 15px; }
.ksf-icon-btn:hover { color: var(--ink); border-color: var(--accent); }
.ksf-icon-btn.danger:hover { color: var(--down); border-color: var(--down); background: var(--down-wash); }

/* Field — text inputs, selects, textareas */
.ksf-field {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 7px 11px;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--text-sm);
}
.ksf-field::placeholder { color: var(--faint); }
.ksf-field:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }

/* Badge — a status word. Colour is the message; never use it decoratively. */
.ksf-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 2px 8px;
  border-radius: var(--r-xs);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  background: var(--wash);
  color: var(--muted);
}
.ksf-badge.ok { background: var(--ok-wash); color: var(--ok); }
.ksf-badge.warn { background: var(--warn-wash); color: var(--warn); }
.ksf-badge.down { background: var(--down-wash); color: var(--down); }
.ksf-badge.accent { background: var(--accent-wash); color: var(--accent); }

/* Chip — a selectable pill (filter, search engine, tag) */
.ksf-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 5px 13px;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--muted);
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: all var(--dur) var(--ease);
}
.ksf-chip:hover { border-color: var(--accent); color: var(--ink); }
.ksf-chip.active { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); }

/* Status dot — the smallest possible up/down signal */
.ksf-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--faint);
  flex-shrink: 0;
}
.ksf-dot.ok { background: var(--ok); box-shadow: 0 0 6px var(--ok-wash); }
.ksf-dot.warn { background: var(--warn); }
.ksf-dot.down { background: var(--down); box-shadow: 0 0 6px var(--down-wash); }

/* Meter — a proportion. Fill colour follows status, not decoration. */
.ksf-meter { height: 6px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.ksf-meter > span { display: block; height: 100%; border-radius: 3px; background: var(--accent); transition: width var(--dur-slow) var(--ease); }
.ksf-meter > span.warn { background: var(--warn); }
.ksf-meter > span.down { background: var(--down); }

/* Toast — transient confirmation, bottom-centre */
.ksf-toast {
  position: fixed;
  bottom: var(--sp-6);
  left: 50%;
  transform: translateX(-50%) translateY(100px);
  z-index: 2000;
  padding: 10px 20px;
  background: var(--surface-2);
  border: 1px solid var(--ring);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-pop);
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 500;
  opacity: 0;
  pointer-events: none;
  transition: all var(--dur-slow) var(--ease);
}
.ksf-toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }
.ksf-toast.success { border-color: var(--ok); color: var(--ok); }
.ksf-toast.error { border-color: var(--down); color: var(--down); }

/* Modal — dimmed backdrop plus a floating panel */
.ksf-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5vh 4vw;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: all var(--dur) var(--ease);
}
.ksf-modal.active { opacity: 1; visibility: visible; }
.ksf-modal-panel {
  width: 100%;
  max-width: 700px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--ring);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
  transform: translateY(-16px) scale(0.98);
  transition: transform 0.25s var(--ease);
}
.ksf-modal.active .ksf-modal-panel { transform: translateY(0) scale(1); }

@media (prefers-reduced-motion: reduce) {
  .ksf *, .ksf *::before, .ksf *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
