/* ============================================================================
 * tokens.css — portable design system
 * ----------------------------------------------------------------------------
 * A single, dependency-free stylesheet meant to be copied verbatim into any of
 * my sites. It defines:
 *
 *   1. Brand dials      — four custom properties that re-skin everything
 *   2. Colour ramps     — perceptually even OKLCH scales derived from the dials
 *   3. Semantic tokens  — what the ramps *mean* (bg, surface, border, text, …)
 *   4. Scales           — space, radius, type, shadow, motion, z-index
 *   5. Base styles      — reset, typography, focus, scrollbars, selection
 *   6. Primitives       — `ui-*` classes for the handful of controls every
 *                         site needs (button, input, select, check, card, …)
 *
 * To re-brand a site, override the four dials in §1 and nothing else:
 *
 *     :root { --hue-brand: 265; --chroma-brand: 0.16; }
 *
 * Everything below §1 is site-agnostic. Site-specific rules belong in the
 * site's own stylesheet, loaded *after* this one, and should consume the
 * semantic tokens (§3) rather than raw ramp steps (§2).
 *
 * Colours are OKLCH so the ramps stay perceptually even at any hue and a hue
 * change never silently wrecks contrast. Requires a 2023+ browser.
 * ========================================================================= */

/* ── §1  Brand dials ─────────────────────────────────────────────────────── */
/*
 * Six numbers decide how the whole system looks. Set them either way:
 *
 *   1. Pick a named profile from §1b:  <html data-brand="moss">
 *   2. Or write your own in the site's own stylesheet, which loads after this
 *      file and therefore wins:        :root { --hue-brand: 312; … }
 *
 * Use (1) when one of the profiles fits, (2) when the site has its own
 * identity. Mixing is fine — a profile sets a baseline and a later `:root`
 * block overrides whichever dials you care about.
 */

:root {
  /* Accent hue/chroma. 58 = copper-amber, 268 = indigo, 145 = green, 28 = red. */
  --hue-brand: 250;
  --chroma-brand: 0.045;

  /* Neutral ramp tint. A little chroma keeps greys from looking dead; the hue
     decides whether the UI feels cool (240–280) or warm (40–80). */
  --hue-neutral: 250;
  --chroma-neutral: 0.008;

  /* Shape and fit. A hue change alone still reads as the same product in a new
     colour — corner radius and control density are what actually make a
     survival game and a sci-fi tool feel like different things.
     --radius-scale: 0 = square, 1 = default, 1.5+ = soft.
     --density: multiplies the space scale only. Type stays put, because a
     scaled 10px label stops being legible. */
  --radius-scale: 1;
  --density: 1;
}

/* ── §1b  Named profiles ─────────────────────────────────────────────────── */
/*
 * A profile is just a bundle of the six dials above under a name. Add one by
 * copying a block; nothing else in this file needs to know it exists.
 */

/* Rustic survival — warm copper on cool slate. (Vintage Story map viewer.) */
[data-brand="copper"] {
  --hue-brand: 58;   --chroma-brand: 0.135;
  --hue-neutral: 265; --chroma-neutral: 0.014;
  --radius-scale: 1;  --density: 1;
}

/* Software tools — violet on slate, the safe default for something utilitarian. */
[data-brand="indigo"] {
  --hue-brand: 268;  --chroma-brand: 0.150;
  --hue-neutral: 265; --chroma-neutral: 0.012;
  --radius-scale: 1;  --density: 1;
}

/* Cozy / farming / nature — green on warm olive, softened corners, roomier. */
[data-brand="moss"] {
  --hue-brand: 145;  --chroma-brand: 0.110;
  --hue-neutral: 150; --chroma-neutral: 0.012;
  --radius-scale: 1.6; --density: 1.15;
}

/* Gritty / combat — hot ember on warm char, hard corners, tighter. */
[data-brand="ember"] {
  --hue-brand: 28;   --chroma-brand: 0.165;
  --hue-neutral: 30;  --chroma-neutral: 0.014;
  --radius-scale: 0.35; --density: 0.92;
}

/* Sci-fi / hardware — cold cyan on blue-grey, near-square, engineered. */
[data-brand="glacier"] {
  --hue-brand: 215;  --chroma-brand: 0.110;
  --hue-neutral: 235; --chroma-neutral: 0.010;
  --radius-scale: 0.25; --density: 1;
}

/* Unbranded — what you get with no profile at all. */
[data-brand="slate"] {
  --hue-brand: 250;  --chroma-brand: 0.045;
  --hue-neutral: 250; --chroma-neutral: 0.008;
  --radius-scale: 1;  --density: 1;
}

/* ── §2  Colour ramps ────────────────────────────────────────────────────── */

:root {
  /* Neutral — numbered like ink: 50 is lightest, 950 darkest. */
  --n-50:  oklch(0.980 calc(var(--chroma-neutral) * 0.3) var(--hue-neutral));
  --n-100: oklch(0.950 calc(var(--chroma-neutral) * 0.4) var(--hue-neutral));
  --n-200: oklch(0.885 calc(var(--chroma-neutral) * 0.7) var(--hue-neutral));
  --n-300: oklch(0.775 calc(var(--chroma-neutral) * 1.0) var(--hue-neutral));
  --n-400: oklch(0.665 calc(var(--chroma-neutral) * 1.1) var(--hue-neutral));
  --n-500: oklch(0.560 calc(var(--chroma-neutral) * 1.2) var(--hue-neutral));
  --n-600: oklch(0.430 calc(var(--chroma-neutral) * 1.3) var(--hue-neutral));
  --n-700: oklch(0.345 calc(var(--chroma-neutral) * 1.3) var(--hue-neutral));
  --n-750: oklch(0.300 calc(var(--chroma-neutral) * 1.2) var(--hue-neutral));
  --n-800: oklch(0.262 calc(var(--chroma-neutral) * 1.1) var(--hue-neutral));
  --n-850: oklch(0.228 calc(var(--chroma-neutral) * 1.0) var(--hue-neutral));
  --n-900: oklch(0.198 calc(var(--chroma-neutral) * 0.9) var(--hue-neutral));
  --n-950: oklch(0.165 calc(var(--chroma-neutral) * 0.8) var(--hue-neutral));

  /* Accent — 500 is the canonical brand colour. */
  --a-300: oklch(0.870 calc(var(--chroma-brand) * 0.65) var(--hue-brand));
  --a-400: oklch(0.800 calc(var(--chroma-brand) * 0.95) var(--hue-brand));
  --a-500: oklch(0.720 var(--chroma-brand)              var(--hue-brand));
  --a-600: oklch(0.630 calc(var(--chroma-brand) * 1.05) var(--hue-brand));
  --a-700: oklch(0.520 calc(var(--chroma-brand) * 0.95) var(--hue-brand));

  /* Status hues are fixed: they must read the same on every site. */
  --success:  oklch(0.735 0.145 152);
  --warning:  oklch(0.800 0.145  85);
  --danger:   oklch(0.660 0.185  25);
  --info:     oklch(0.720 0.120 245);
}

/* ── §3  Semantic tokens ─────────────────────────────────────────────────── */
/* Dark is the default. §3b re-maps these for `data-theme="light"`.           */

:root {
  color-scheme: dark;

  /* Grounds, back to front. */
  --bg:            var(--n-950);  /* page / deepest inset               */
  --bg-sunken:     var(--n-950);  /* inputs, wells, tracks              */
  --bg-panel:      var(--n-900);  /* sidebars, chrome                   */
  --bg-surface:    var(--n-850);  /* cards, modals                      */
  --bg-raised:     var(--n-800);  /* hovered rows, popovers             */
  --bg-overlay:    color-mix(in oklab, var(--n-900) 82%, transparent);

  /* Hairlines. */
  --border:        var(--n-800);
  --border-strong: var(--n-700);
  --border-accent: color-mix(in oklab, var(--a-500) 45%, transparent);

  /* Ink. */
  --text:          var(--n-200);
  --text-muted:    var(--n-400);
  --text-subtle:   var(--n-500);
  --text-inverted: var(--n-950);

  /* Accent. */
  --accent:          var(--a-500);
  --accent-hover:    var(--a-400);
  --accent-active:   var(--a-600);
  --accent-contrast: oklch(0.20 0.02 var(--hue-brand));
  --accent-soft:     color-mix(in oklab, var(--a-500) 14%, transparent);
  --accent-softer:   color-mix(in oklab, var(--a-500)  7%, transparent);

  /* Status, plus a tinted fill for each. */
  --success-soft: color-mix(in oklab, var(--success) 15%, transparent);
  --warning-soft: color-mix(in oklab, var(--warning) 15%, transparent);
  --danger-soft:  color-mix(in oklab, var(--danger)  15%, transparent);
  --info-soft:    color-mix(in oklab, var(--info)    15%, transparent);

  /* Neutral washes — for hover states that must not pick up the brand hue. */
  --wash-1: color-mix(in oklab, var(--n-50) 4%, transparent);
  --wash-2: color-mix(in oklab, var(--n-50) 8%, transparent);
  --wash-3: color-mix(in oklab, var(--n-50) 13%, transparent);
}

/* §3b — light theme. Opt in with <html data-theme="light">. */
:root[data-theme="light"] {
  color-scheme: light;

  --bg:            var(--n-50);
  --bg-sunken:     var(--n-100);
  --bg-panel:      var(--n-50);
  --bg-surface:    #fff;
  --bg-raised:     var(--n-100);
  --bg-overlay:    color-mix(in oklab, #fff 85%, transparent);

  --border:        var(--n-200);
  --border-strong: var(--n-300);

  --text:          var(--n-900);
  --text-muted:    var(--n-600);
  --text-subtle:   var(--n-500);
  --text-inverted: var(--n-50);

  --accent:          var(--a-700);
  --accent-hover:    var(--a-600);
  --accent-active:   oklch(0.44 0.10 var(--hue-brand));
  --accent-contrast: #fff;

  --wash-1: color-mix(in oklab, var(--n-950) 3%, transparent);
  --wash-2: color-mix(in oklab, var(--n-950) 6%, transparent);
  --wash-3: color-mix(in oklab, var(--n-950) 10%, transparent);
}

/* ── §4  Scales ──────────────────────────────────────────────────────────── */

:root {
  /* Space — a 4px base with a half-step at the bottom where UI is densest.
     Scaled by --density so a profile can loosen or tighten the whole UI. */
  --space-0: 0;
  --space-1: calc(2px  * var(--density));
  --space-2: calc(4px  * var(--density));
  --space-3: calc(6px  * var(--density));
  --space-4: calc(8px  * var(--density));
  --space-5: calc(12px * var(--density));
  --space-6: calc(16px * var(--density));
  --space-7: calc(20px * var(--density));
  --space-8: calc(24px * var(--density));
  --space-9: calc(32px * var(--density));
  --space-10: calc(48px * var(--density));

  /* Radius. Scaled by --radius-scale; `full` is a pill and never scales. */
  --radius-xs:   calc(3px  * var(--radius-scale));
  --radius-sm:   calc(5px  * var(--radius-scale));
  --radius-md:   calc(8px  * var(--radius-scale));
  --radius-lg:   calc(12px * var(--radius-scale));
  --radius-xl:   calc(16px * var(--radius-scale));
  --radius-full: 999px;

  /* Type. */
  --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
               "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono",
               Consolas, "Liberation Mono", monospace;

  --text-2xs: 10px;
  --text-xs:  11px;
  --text-sm:  12px;
  --text-md:  13px;
  --text-lg:  15px;
  --text-xl:  18px;
  --text-2xl: 24px;

  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.7;

  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semi:   600;
  --weight-bold:   700;

  --tracking-tight: -0.01em;
  --tracking-wide:   0.04em;
  --tracking-wider:  0.08em;

  /* Elevation. Dark UIs need a rim light as well as a drop shadow, otherwise
     raised surfaces read as holes. */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.28);
  --shadow-md: 0 2px 6px rgb(0 0 0 / 0.32), 0 1px 2px rgb(0 0 0 / 0.24);
  --shadow-lg: 0 8px 28px rgb(0 0 0 / 0.42), 0 2px 8px rgb(0 0 0 / 0.28);
  --shadow-xl: 0 20px 56px rgb(0 0 0 / 0.50), 0 4px 12px rgb(0 0 0 / 0.30);
  --rim: inset 0 1px 0 color-mix(in oklab, var(--n-50) 6%, transparent);
  --glow-accent: 0 0 0 1px var(--border-accent),
                 0 4px 16px color-mix(in oklab, var(--a-500) 22%, transparent);

  /* Motion. */
  --dur-fast: 110ms;
  --dur-base: 180ms;
  --dur-slow: 320ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* Layering — one place to resolve stacking arguments. */
  --z-base: 1;
  --z-overlay: 5;
  --z-tooltip: 10;
  --z-sticky: 80;
  --z-drawer: 90;
  --z-toggle: 100;
  --z-modal: 200;

  /* Focus. Two rings so it stays visible on both light and dark fills. */
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

/* ── §5  Base ────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { font-weight: var(--weight-semi); line-height: var(--leading-tight); }
a { color: var(--accent); text-decoration-color: color-mix(in oklab, var(--accent) 40%, transparent); }
a:hover { color: var(--accent-hover); }
code, kbd, samp, pre { font-family: var(--font-mono); font-size: 0.92em; }
img, svg, canvas, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
:where(button, [role="button"]) { cursor: pointer; }
[hidden] { display: none !important; }

/* One focus treatment for the whole app. Keyboard only — pointer users never
   see it, so controls stay clean without sacrificing accessibility. */
:focus { outline: none; }
:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

::selection { background: color-mix(in oklab, var(--accent) 35%, transparent); }

/* Scrollbars — thin and self-effacing, visible on hover. */
* { scrollbar-width: thin; scrollbar-color: var(--wash-2) transparent; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--wash-2);
  border: 2px solid transparent;
  background-clip: content-box;
  border-radius: var(--radius-full);
}
::-webkit-scrollbar-thumb:hover { background: var(--wash-3); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── §6  Primitives ──────────────────────────────────────────────────────── */

/* Buttons ------------------------------------------------------------------ */

.ui-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.ui-btn:active:not(:disabled) { transform: translateY(1px); }
.ui-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

.ui-btn--primary {
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: var(--weight-semi);
  box-shadow: var(--rim);
}
.ui-btn--primary:hover:not(:disabled) { background: var(--accent-hover); }

.ui-btn--secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-muted);
}
.ui-btn--secondary:hover:not(:disabled) {
  background: var(--wash-1);
  border-color: var(--n-600);
  color: var(--text);
}

.ui-btn--ghost { background: transparent; color: var(--text-muted); }
.ui-btn--ghost:hover:not(:disabled) { background: var(--wash-2); color: var(--text); }

.ui-btn--danger { background: var(--danger); color: var(--n-950); font-weight: var(--weight-semi); }
.ui-btn--danger:hover:not(:disabled) { filter: brightness(1.08); }

/* Square icon button. */
.ui-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.ui-icon-btn:hover { background: var(--wash-2); color: var(--text); border-color: var(--n-600); }

/* Fields ------------------------------------------------------------------- */

.ui-label {
  display: block;
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}

.ui-input,
.ui-select,
.ui-textarea {
  width: 100%;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: var(--space-4) var(--space-4);
  font-size: var(--text-md);
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.ui-input:hover, .ui-select:hover, .ui-textarea:hover { border-color: var(--border-strong); }
.ui-input:focus-visible,
.ui-select:focus-visible,
.ui-textarea:focus-visible { border-color: var(--accent); box-shadow: var(--focus-ring); }
.ui-input::placeholder, .ui-textarea::placeholder { color: var(--text-subtle); }
.ui-input:disabled, .ui-select:disabled { opacity: 0.5; cursor: not-allowed; }

/* Native select arrow, drawn so it matches the token palette. */
.ui-select {
  appearance: none;
  padding-right: var(--space-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) calc(50% + 1px), calc(100% - 10px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* Checkbox ----------------------------------------------------------------- */

.ui-check {
  appearance: none;
  width: 16px;
  height: 16px;
  flex: none;
  position: relative;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-xs);
  background: var(--bg-sunken);
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.ui-check:hover { border-color: var(--n-600); }
.ui-check:checked { background: var(--accent); border-color: var(--accent); }
.ui-check:checked::after {
  content: "";
  position: absolute;
  left: 4.5px; top: 1px;
  width: 4px; height: 9px;
  border: solid var(--accent-contrast);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}

/* Range -------------------------------------------------------------------- */

.ui-range {
  appearance: none;
  height: 4px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: var(--n-800);
}
.ui-range::-webkit-slider-thumb {
  appearance: none;
  width: 14px; height: 14px;
  border-radius: var(--radius-full);
  background: var(--accent);
  border: 2px solid var(--bg-panel);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out);
}
.ui-range::-webkit-slider-thumb:hover { transform: scale(1.15); }
.ui-range::-moz-range-thumb {
  width: 14px; height: 14px;
  border-radius: var(--radius-full);
  background: var(--accent);
  border: 2px solid var(--bg-panel);
  cursor: pointer;
}

/* Surfaces ----------------------------------------------------------------- */

.ui-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm), var(--rim);
}

/* Frosted floating panel — popovers, legends, HUD overlays. */
.ui-panel {
  background: var(--bg-overlay);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md), var(--rim);
}

.ui-divider { height: 1px; background: var(--border); border: none; }

/* An all-caps micro-heading for dense sidebars. */
.ui-eyebrow {
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--text-subtle);
}

.ui-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 2px var(--space-3);
  border-radius: var(--radius-full);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semi);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  background: var(--wash-2);
  color: var(--text-muted);
}
.ui-badge--accent  { background: var(--accent-soft);  color: var(--accent); }
.ui-badge--success { background: var(--success-soft); color: var(--success); }
.ui-badge--warning { background: var(--warning-soft); color: var(--warning); }
.ui-badge--danger  { background: var(--danger-soft);  color: var(--danger); }

/* Modal -------------------------------------------------------------------- */

.ui-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: color-mix(in oklab, var(--n-950) 62%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: ui-fade-in var(--dur-base) var(--ease-out);
}

.ui-modal {
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl), var(--rim);
  animation: ui-rise var(--dur-base) var(--ease-out);
}

@keyframes ui-fade-in { from { opacity: 0; } }
@keyframes ui-rise { from { opacity: 0; transform: translateY(8px) scale(0.985); } }
@keyframes ui-spin { to { transform: rotate(360deg); } }

.ui-spinner {
  width: 28px; height: 28px;
  border-radius: var(--radius-full);
  border: 3px solid var(--wash-2);
  border-top-color: var(--accent);
  animation: ui-spin 0.8s linear infinite;
}

/* Utilities ---------------------------------------------------------------- */

.ui-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.ui-num  { font-variant-numeric: tabular-nums; }
.ui-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.ui-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
