/* ============================================================================
   FOUNDRYSIDE — tokens.css

   Foundryside is John Morrissey's professional identity. It is not a product
   site and it is not derived from any project it indexes.

   The colour block below is GENERATED. Do not hand-edit it:

       node src/palette-check.mjs --emit

   That script derives every value and verifies seven constraints before it
   will emit anything — contrast on both grounds, channel/channel separation,
   channel/state separation, sRGB gamut, and every token that renders on a dark
   panel. Run `npm run palette` to check the current values.

   The rule the palette is built on:

       WARM IS STATUS. COOL IS IDENTITY.

   Green, amber and red belong to state. Nothing else on the site is those
   hues. The nine project channels live in the cool arc, 206°–326°.

   Channel index maps to project in INDEX ORDER, deliberately arbitrarily.
   Choosing a hue for what a project is about is what produced the previous
   palette's defects: errorworks' identity colour measured ΔE 0.0196 from
   --state-fail, i.e. its edge was indistinguishable from "this failed".

   One light theme with dark panels. No dark mode, no toggle, no JavaScript.
   Every value that ships is plain sRGB hex — oklch() is used only inside the
   generator.
   ============================================================================ */

/* ---- Type faces. IBM Plex Sans + Mono, bundled so the page renders offline
   and makes no third-party requests. */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("fonts/IBMPlexSans-Variable.woff2") format("woff2");
  font-weight: 100 700;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/IBMPlexMono-Medium.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

:root {
  /* ======================= GENERATED BLOCK BEGINS ======================= */

  /* ---- Ground ramp. The light surfaces. ---- */
  --ground-0: #FBFCFE;
  --ground-1: #F2F5F7;
  --ground-2: #E8EBEF;
  --ground-3: #D8DDE2;
  --ground-4: #C3C8CE;

  /* ---- Ink ramp. Text on light; ink-900/800 are also the DARK panel grounds. ---- */
  --ink-900: #0D131A;
  --ink-800: #19212A;
  --ink-700: #2D3640;
  --ink-500: #606A74;
  --ink-400: #818A94;
  --ink-300: #A0A9B2;

  /* ---- Channels. One per project, assigned in INDEX ORDER -- deliberately
     arbitrary. Deriving a hue from what a project is about is how the site
     ended up with errorworks wearing a red that measured dE 0.0196 from
     --state-fail. All nine sit in the cool arc 206-326 deg: warm is status,
     cool is identity, and the two never overlap.
       -N        the 4px rule on light   >= 3.0:1
       -N-text   as text on light        >= 4.5:1
       -N-inv    rule AND text on dark   >= 4.5:1  ---- */
  --channel-1: #006771;        /* 206 deg -- weft */
  --channel-1-text: #00525A;
  --channel-1-inv: #7FBEC6;
  --channel-2: #008AA8;        /* 221 deg -- elspeth */
  --channel-2-text: #00657C;
  --channel-2-inv: #8BD8F2;
  --channel-3: #0074A3;        /* 236 deg -- skill-packs */
  --channel-3-text: #00587D;
  --channel-3-inv: #89C7ED;
  --channel-4: #035CA3;        /* 251 deg -- errorworks */
  --channel-4-text: #004983;
  --channel-4-inv: #8AB6E6;
  --channel-5: #5477CE;        /* 266 deg -- semantic-defects */
  --channel-5-text: #3554A8;
  --channel-5-inv: #B3CBFF;
  --channel-6: #5F5EB9;        /* 281 deg -- hamlet */
  --channel-6-text: #46429B;
  --channel-6-inv: #B4B8F6;
  --channel-7: #63459F;        /* 296 deg -- keisei */
  --channel-7-text: #503189;
  --channel-7-inv: #B6A7E3;
  --channel-8: #9661B8;        /* 311 deg -- murk */
  --channel-8-text: #733E93;
  --channel-8-inv: #DDBCF5;
  --channel-9: #934A96;        /* 326 deg -- simic */
  --channel-9-text: #762E79;
  --channel-9-inv: #DCABDD;

  /* ---- State. The ONLY warm hues on the site. ---- */
  --state-ok: #337538;
  --state-ok-inv: #80D58D;
  --state-warn: #885A00;
  --state-warn-inv: #EABB4F;
  --state-fail: #AF302B;
  --state-fail-inv: #FF8A82;
  --state-unknown: #43474B;
  --state-unknown-inv: #ACB2B7;

  /* ---- Accent. Foundryside's OWN colour. It was previously
     var(--thread-indigo), annotated 'canon -- weft / the spine': the site
     was wearing one of its own projects' colours as its identity. ---- */
  --accent: #234F8E;
  --accent-hover: #123D7B;
  --accent-press: #053069;
  --accent-wash: #E5EEFB;
  --accent-inv: #9AC0F8;

  /* ---- Text on light. ink-400 measures 2.93:1 and is NOT a text value. ---- */
  --text-muted: #4A5157;
  --text-faint: #606A74;

  /* ---- Chrome for the dark panels. Every light-ground alias is unusable
     there: --text-muted measures 1.90:1 on ink-900. ---- */
  --text-inverse: #FBFCFE;
  --text-inverse-muted: #A0A9B2;
  --nav-link: #A0A9B2;
  --nav-link-active: #FBFCFE;
  --focus-ring-inv: #9AC0F8;
  --btn-inv-bg: #9AC0F8;
  --btn-inv-fg: #0D131A;
  /* Decorative separator: 1.5:1 is a perceptibility floor, NOT a WCAG bar.
     WCAG 1.4.11 covers boundaries that identify a component -- those use
     --border-component-inv, which does carry 3:1. */
  --border-hairline-inv: #3D4650;
  --border-component-inv: #68717C;

  /* ======================== GENERATED BLOCK ENDS ======================== */

  /* ---- Surface + text aliases ---- */
  --surface-page: var(--ground-1);
  --surface-card: var(--ground-0);
  --surface-raised: var(--ground-0);
  --surface-sunken: var(--ground-2);
  --surface-inset: var(--ground-3);
  --surface-dark: var(--ink-900);
  --surface-dark-raised: var(--ink-800);

  --text-body: var(--ink-900);
  --text-link: var(--accent);

  --border-hairline: var(--ground-3);
  --border-strong: var(--ground-4);
  --border-ink: var(--ink-900);

  --focus-ring: var(--accent);

  /* ---- Type. Mono carries the structural furniture — labels, identifiers,
     chips, metadata, the wordmark and every slug. Sans is for prose and
     headings only. ---- */
  --font-sans: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --text-display: clamp(2rem, 1.5rem + 2.4vw, 2.75rem);   /* 44px at desk, 32px small */
  --text-h1: 2rem;           /* 32px */
  --text-h2: 1.5rem;         /* 24px */
  --text-h3: 1.125rem;       /* 18px */
  --text-body-size: 1rem;    /* 16px */
  --text-small: 0.875rem;    /* 14px */
  --text-micro: 0.75rem;     /* 12px */

  --leading-tight: 1.15;
  --leading-snug: 1.35;
  --leading-body: 1.6;
  --leading-loose: 1.75;

  --tracking-display: -0.02em;
  --tracking-normal: 0;
  --tracking-label: 0.08em;  /* uppercase mono labels */

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /* ---- Space. 4px base; dense, mono-forward layouts. ---- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;

  --measure-column: 980px;   /* centred reading column; dark panels go full-bleed */
  --measure-prose: 68ch;
  --rule-state: 4px;         /* the 4px left rule: identity by rule, not fill */

  /* ---- Shape. Squared off: the rule reads as a machined edge, not a tab. ---- */
  --radius-sm: 2px;
  --radius-md: 2px;
  --radius-lg: 2px;
  --radius-pill: 999px;

  --border-width: 1px;
  --border-thick: 2px;

  --shadow-none: none;
  --shadow-overlay: 0 2px 0 0 var(--border-strong);
  --shadow-dialog: 0 8px 24px -12px rgba(13, 19, 26, 0.32);

  /* ---- Motion. Engineered, not glossy: short, flat, no bounce. ---- */
  --duration-fast: 90ms;
  --duration-base: 140ms;
  --duration-slow: 220ms;
  --ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0ms;
    --duration-base: 0ms;
    --duration-slow: 0ms;
  }
}

/* ---- Channel scopes. Set the local channel for a row, masthead, tag or
   button. A component with no scope falls back to the accent.
     --ch        the 4px rule on a light ground
     --ch-text   the channel rendered as small text on a light ground
     --ch-inv    the channel on a DARK panel, as rule or as text
   Generated with the block above. */
.channel-1 { --ch: var(--channel-1); --ch-text: var(--channel-1-text); --ch-inv: var(--channel-1-inv); }
.channel-2 { --ch: var(--channel-2); --ch-text: var(--channel-2-text); --ch-inv: var(--channel-2-inv); }
.channel-3 { --ch: var(--channel-3); --ch-text: var(--channel-3-text); --ch-inv: var(--channel-3-inv); }
.channel-4 { --ch: var(--channel-4); --ch-text: var(--channel-4-text); --ch-inv: var(--channel-4-inv); }
.channel-5 { --ch: var(--channel-5); --ch-text: var(--channel-5-text); --ch-inv: var(--channel-5-inv); }
.channel-6 { --ch: var(--channel-6); --ch-text: var(--channel-6-text); --ch-inv: var(--channel-6-inv); }
.channel-7 { --ch: var(--channel-7); --ch-text: var(--channel-7-text); --ch-inv: var(--channel-7-inv); }
.channel-8 { --ch: var(--channel-8); --ch-text: var(--channel-8-text); --ch-inv: var(--channel-8-inv); }
.channel-9 { --ch: var(--channel-9); --ch-text: var(--channel-9-text); --ch-inv: var(--channel-9-inv); }
