/* ═══════════════════════════════════════════════════════════════════════════
   IG1 UI — GENERATED FILE. DO NOT EDIT.

   Built from pkg/ui/src/ by pkg/ui/build.sh. Edit the sources and re-run:
       pkg/ui/build.sh

   This copy is VENDORED into the surface because the portal and docs images
   build from different contexts and neither can COPY out of the repo root.
   `pkg/ui/build.sh --check` (wired into tests/test_pkg_ui.py) fails if this
   file has drifted from the sources, so the duplication cannot rot silently.
   ═════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────── src/01-tokens.css ───────────────────────────── */
/* ═══════════════════════════════════════════════════════════════════════════
   IG1 UI — 01 · TOKENS
   The single source of colour, type, space and elevation for every IG1
   surface: the customer console (UI/chart), the developer hub
   (services/docs) and the shared one-pagers.

   ── THE CONTRACT, AND WHY IT IS SHAPED LIKE THIS ────────────────────────────
   The SEMANTIC NAMES BELOW ARE FROZEN. console.css already carries ~1800 lines
   written against --bg / --surface / --line / --ink-* / --shadow-*, and
   app.js generates markup referencing the classes those rules style. Renaming a
   token would mean touching every one of them and every JS template literal
   with it. So this file changes what the names MEAN, never what they are
   CALLED: retune the values and 1800 lines restyle themselves, with no class
   rename and nothing for app.js to keep in step with.
   docs.css used a strict SUBSET of the same names, so both surfaces converge
   here without either one being renamed.

   ── LIGHT IS DESIGNED, NOT INVERTED ─────────────────────────────────────────
   The reference the look comes from (team shares/*.html) is dark-only, so there
   was nothing to copy for light mode. Inverting a dark palette produces the
   classic result: grey mud, borders that vanish, and an accent that fails
   contrast. Each theme therefore has hand-picked values, and three families
   differ by more than a swap:
     · LINES are tinted with the ink colour, never pure black or white, so a
       hairline reads as a boundary rather than a smudge.
     · ELEVATION is shadow in light (objects float above paper) and SURFACE
       LIFT in dark (objects glow slightly brighter than their ground), because
       a shadow on a near-black page is invisible.
     · The ACCENT splits in two. Lime #bddb20 is a fill colour: on white it
       carries roughly 1.5:1 against text, so it can back a shape but can never
       BE text. --accent-ink is the readable form and is a genuinely different
       colour per theme — a darkened lime on light, the raw lime on dark.
       Any rule putting accent on a text node uses --accent-ink. Always.

   ── HOW A THEME IS SELECTED ─────────────────────────────────────────────────
   theme.js (pkg/ui/src/theme.js) resolves light | dark | system and always
   stamps data-theme="light" or "dark" on <html> before first paint. The
   prefers-color-scheme block below therefore only covers the window where the
   script has not run yet, or has been blocked — the page still opens in the
   right theme instead of flashing white. Both are kept deliberately: neither
   alone is sufficient.
   ═════════════════════════════════════════════════════════════════════════ */

:root {
  /* ─── Brand constants — identical in BOTH themes ─────────────────────────
     These are the artwork's own values. The lockup in UI/logo/ is drawn with
     #BDDB22; --lime-400 (#bddb20) is the ig1.com charte value and the two are
     indistinguishable on screen. Both are kept: --lime-400 is the UI accent,
     --brand-lime is the artwork's exact ink, for the rare rule that must match
     the SVG pixel for pixel. */
  --brand-lime: #bddb22;
  --lime-400: #bddb20;
  --lime-500: #a8c41c;
  --lime-600: #93ad18;
  --lime-700: #7d9414;
  /* --lime-ink is the READABLE lime, and its value changed on 2026-08-20.
     It was #6d8210, which measures 4.07:1 on the light page and 4.33:1 on
     white — both BELOW the 4.5:1 AA floor for body text, while console.css
     used it for small text in five places (trend deltas, .status-badge.live,
     .actor-chip.agent, .tag-lime). That was a real accessibility defect in the
     light theme, not a style preference. #5f720e is the same hue and
     saturation and measures 5.07:1 / 5.38:1. Numbers are WCAG 2.1 relative
     luminance, recomputed in pkg/ui/contrast.py — re-run it before changing
     any accent value. */
  --lime-ink: #5f720e;

  /* Neutral ramp. Cool-cast, so lime reads as the only warm thing on screen.
     50→300 carry the light theme, 700→950 the dark; the middles serve both. */
  --c-50:  #f7f8fa;
  --c-100: #eef0f3;
  --c-200: #dcdfe5;
  --c-300: #bcc2cb;
  --c-400: #848c98;   /* 3.39:1 on white — the caption floor is 3.0; #949ca8 was 2.77 */
  --c-500: #6e7681;
  --c-600: #565e69;
  --c-700: #3d444d;
  --c-800: #262c34;
  --c-900: #161b22;
  --c-950: #0d1117;
  --c-1000: #090c10;

  /* Secondary hues. Semantic only — they never carry brand meaning, they mark
     STATE (next / planned / notable) and data series. Taken from the reference
     one-pagers, where they already sat beside a bright accent. */
  --blue:   #58a6ff;
  --purple: #a371f7;
  --amber:  #d29922;
  --rose:   #f27b7b;

  /* ─── Type ──────────────────────────────────────────────────────────────
     Lato is the ig1.com charte face and stays the console's body font; the
     system stack carries every surface that must not hit the network (the docs
     hub loads no webfont at all). The mono face is doing real work in this
     design language — eyebrows, stat values, tags and code all use it. */
  --sans: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", "SFMono-Regular", "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  /* Type scale. Display sizes are fluid so a hero never needs a media query;
     body sizes are fixed so text does not shift under the reader. */
  --fs-display: clamp(40px, 6vw, 66px);
  --fs-h1:      clamp(30px, 3.8vw, 42px);
  --fs-h2:      clamp(23px, 2.6vw, 30px);
  --fs-h3:      19px;
  --fs-h4:      16px;
  --fs-body:    15px;
  --fs-sm:      13.5px;
  --fs-xs:      12px;
  --fs-2xs:     10.5px;

  --lh-tight:   1.06;
  --lh-heading: 1.22;
  --lh-body:    1.6;

  --ls-display: -0.035em;
  --ls-heading: -0.02em;
  --ls-eyebrow: 0.18em;

  /* ─── Space, radius, motion ─────────────────────────────────────────────
     A 4px base. --space-section is the vertical rhythm the reference gets much
     of its calm from: big, unapologetic gaps between sections. */
  --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-section: clamp(56px, 7vw, 88px);

  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius:    12px;
  --radius-lg: 14px;
  --radius-xl: 18px;
  --radius-pill: 999px;

  --measure: 68ch;          /* readable line length for prose */
  --page-max: 1120px;       /* the one-pagers' content width */

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast: 120ms;
  --dur: 200ms;
}

/* ═══════════════════════════════════════════════════════════════════════════
   LIGHT — the default. Paper, not grey: the page is a hair off-white and cards
   are pure white, so a card reads as lifted without needing a heavy border.
   ═════════════════════════════════════════════════════════════════════════ */
:root {
  color-scheme: light;

  --bg:         var(--c-50);
  --inset:      var(--c-100);
  --surface:    #ffffff;
  --surface-2:  var(--c-50);
  --surface-3:  var(--c-100);

  /* Ink-tinted, never pure black — a black hairline on white reads as dirt. */
  --line:       rgba(13, 17, 23, 0.09);
  --line-2:     rgba(13, 17, 23, 0.16);
  --line-hi:    rgba(13, 17, 23, 0.24);

  --ink-strong: var(--c-950);
  --ink:        var(--c-800);
  --ink-1:      var(--c-700);
  --ink-2:      var(--c-600);
  --ink-3:      var(--c-500);
  --ink-mute:   var(--c-400);
  --ink-faint:  var(--c-300);

  /* Accent. --accent fills; --accent-ink is the ONLY one allowed on a text
     node. Raw lime measures 1.48:1 on this page — not "low contrast", but
     genuinely unreadable — while --lime-ink clears AA at 5.07:1. The split is
     not stylistic: it is the difference between a colour that can back a shape
     and a colour that can be read. */
  --accent:      var(--lime-400);
  --accent-2:    var(--lime-500);
  --accent-ink:  var(--lime-ink);
  --accent-on:   var(--c-950);          /* text ON an accent fill */
  --accent-a6:   rgba(189, 219, 32, 0.10);
  --accent-a10:  rgba(189, 219, 32, 0.16);
  --accent-a16:  rgba(189, 219, 32, 0.22);
  --accent-a28:  rgba(189, 219, 32, 0.34);
  --accent-a40:  rgba(157, 184, 20, 0.50);

  --topbar-bg:   rgba(255, 255, 255, 0.82);
  --topbar-line: rgba(13, 17, 23, 0.07);
  --menu-bg:     rgba(255, 255, 255, 0.97);
  --menu-bg-2:   rgba(255, 255, 255, 0.95);
  --popover-bg:  rgba(255, 255, 255, 0.98);
  --scrim:       rgba(13, 17, 23, 0.42);

  /* Elevation by SHADOW — the light-mode half of the pair. */
  --shadow-nav:     0 1px 16px rgba(13, 17, 23, 0.06);
  --shadow-card:    0 1px 2px rgba(13, 17, 23, 0.04), 0 8px 28px rgba(13, 17, 23, 0.06);
  --shadow-card-1:  0 12px 40px rgba(13, 17, 23, 0.06);
  --shadow-card-2:  0 8px 32px rgba(13, 17, 23, 0.06);
  --shadow-menu:    0 12px 40px rgba(13, 17, 23, 0.08), 0 2px 8px rgba(13, 17, 23, 0.04);
  --shadow-pop:     0 12px 40px rgba(13, 17, 23, 0.10), 0 2px 8px rgba(13, 17, 23, 0.04);
  --shadow-drawer:  -16px 0 48px rgba(13, 17, 23, 0.14);
  --shadow-sidebar: 8px 0 32px rgba(13, 17, 23, 0.10);
  --shadow-mmenu:   0 16px 48px rgba(13, 17, 23, 0.12);
  --shadow-lift:    0 10px 30px rgba(13, 17, 23, 0.10);

  /* Decoration. The hero glow and the grid are the two effects that carry the
     reference's "modern" feel; in light they are barely-there tints, because
     the same alpha that glows on black turns to haze on white. */
  --glow-hero:   radial-gradient(ellipse at center,
                   rgba(189, 219, 32, 0.20) 0%,
                   rgba(189, 219, 32, 0.07) 42%,
                   transparent 72%);
  --glow-accent: 0 0 12px rgba(157, 184, 20, 0.35);
  --grid-line:   rgba(13, 17, 23, 0.045);
  --grad-text:   linear-gradient(92deg, var(--c-950) 0%, var(--lime-700) 58%, #2f6f8f 100%);
  --grad-accent: linear-gradient(90deg, var(--lime-500), var(--lime-400));

  /* Status. Deliberately desaturated so a table of them is calm, not a
     Christmas tree — the accent is the only saturated colour on the page. */
  --ok:      #3f7d3a;
  --ok-bg:   rgba(63, 125, 58, 0.10);
  --ok-bd:   rgba(63, 125, 58, 0.34);
  --warn:    #8a6810;   /* was #9a7412 = 4.30:1 on white, under AA; this is 5.16:1 */
  --warn-bg: rgba(230, 180, 40, 0.14);
  --warn-bd: rgba(190, 145, 30, 0.40);
  --err:     #b83a2c;
  --err-bg:  rgba(184, 58, 44, 0.09);
  --err-bd:  rgba(184, 58, 44, 0.32);
  --err-a7:  rgba(184, 58, 44, 0.07);
  --err-a10: rgba(184, 58, 44, 0.10);
  --err-a28: rgba(184, 58, 44, 0.28);
  --info:    var(--c-600);

  --blue-ink:   #1f6feb;
  --purple-ink: #7c4dd0;
  --amber-ink:  #9a7412;
}

/* ═══════════════════════════════════════════════════════════════════════════
   DARK — the reference's own ground. Near-black with a cool cast, cards a step
   lighter, hairlines made of white alpha. Elevation is LIFT, not shadow.
   Written once as a custom-property block and applied by both selectors below.
   ═════════════════════════════════════════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
  /* Pre-script fallback only: theme.js stamps data-theme before first paint,
     and :not([data-theme="light"]) makes sure an explicit light choice still
     wins here if the OS says dark. */
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg:         var(--c-1000);
    --inset:      #06090d;
    --surface:    var(--c-950);
    --surface-2:  var(--c-900);
    --surface-3:  #1c222b;

    --line:       rgba(255, 255, 255, 0.08);
    --line-2:     rgba(255, 255, 255, 0.16);
    --line-hi:    rgba(255, 255, 255, 0.24);

    --ink-strong: #f0f3f6;
    --ink:        #c9d1d9;
    --ink-1:      #b1bac4;
    --ink-2:      #8b949e;
    --ink-3:      #6e7681;
    --ink-mute:   #6e7681;
    --ink-faint:  #484f58;

    --accent:      var(--lime-400);
    --accent-2:    var(--lime-500);
    --accent-ink:  var(--lime-400);
    --accent-on:   var(--c-950);
    --accent-a6:   rgba(189, 219, 32, 0.06);
    --accent-a10:  rgba(189, 219, 32, 0.10);
    --accent-a16:  rgba(189, 219, 32, 0.16);
    --accent-a28:  rgba(189, 219, 32, 0.28);
    --accent-a40:  rgba(189, 219, 32, 0.45);

    --topbar-bg:   rgba(9, 12, 16, 0.86);
    --topbar-line: rgba(255, 255, 255, 0.09);
    --menu-bg:     rgba(22, 27, 34, 0.98);
    --menu-bg-2:   rgba(22, 27, 34, 0.96);
    --popover-bg:  rgba(22, 27, 34, 0.99);
    --scrim:       rgba(1, 4, 9, 0.66);

    --shadow-nav:     0 1px 0 rgba(255, 255, 255, 0.04);
    --shadow-card:    0 1px 0 rgba(255, 255, 255, 0.03), 0 16px 40px rgba(1, 4, 9, 0.55);
    --shadow-card-1:  0 16px 40px rgba(1, 4, 9, 0.5);
    --shadow-card-2:  0 10px 30px rgba(1, 4, 9, 0.45);
    --shadow-menu:    0 16px 48px rgba(1, 4, 9, 0.6);
    --shadow-pop:     0 16px 48px rgba(1, 4, 9, 0.66);
    --shadow-drawer:  -16px 0 48px rgba(1, 4, 9, 0.6);
    --shadow-sidebar: 8px 0 32px rgba(1, 4, 9, 0.5);
    --shadow-mmenu:   0 16px 48px rgba(1, 4, 9, 0.6);
    --shadow-lift:    0 16px 40px rgba(1, 4, 9, 0.55);

    --glow-hero:   radial-gradient(ellipse at center,
                     rgba(189, 219, 32, 0.16) 0%,
                     rgba(189, 219, 32, 0.05) 40%,
                     transparent 70%);
    --glow-accent: 0 0 12px rgba(189, 219, 32, 0.40);
    --grid-line:   rgba(255, 255, 255, 0.025);
    --grad-text:   linear-gradient(92deg, #ffffff 0%, var(--lime-400) 55%, var(--blue) 100%);
    --grad-accent: linear-gradient(90deg, var(--lime-400), var(--lime-500));

    --ok:      #7ac074;
    --ok-bg:   rgba(122, 192, 116, 0.14);
    --ok-bd:   rgba(122, 192, 116, 0.42);
    --warn:    #e6b428;
    --warn-bg: rgba(230, 180, 40, 0.16);
    --warn-bd: rgba(230, 180, 40, 0.45);
    --err:     #e07a6d;
    --err-bg:  rgba(224, 122, 109, 0.14);
    --err-bd:  rgba(224, 122, 109, 0.42);
    --err-a7:  rgba(224, 122, 109, 0.10);
    --err-a10: rgba(224, 122, 109, 0.14);
    --err-a28: rgba(224, 122, 109, 0.38);
    --info:    #b8b8c1;

    --blue-ink:   var(--blue);
    --purple-ink: var(--purple);
    --amber-ink:  var(--amber);
  }
}

/* The explicit choice. Same values; this block is what theme.js drives, and it
   must win in BOTH directions — including an OS in light mode with the toggle
   set to dark, which the media query above cannot express. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:         var(--c-1000);
  --inset:      #06090d;
  --surface:    var(--c-950);
  --surface-2:  var(--c-900);
  --surface-3:  #1c222b;

  --line:       rgba(255, 255, 255, 0.08);
  --line-2:     rgba(255, 255, 255, 0.16);
  --line-hi:    rgba(255, 255, 255, 0.24);

  --ink-strong: #f0f3f6;
  --ink:        #c9d1d9;
  --ink-1:      #b1bac4;
  --ink-2:      #8b949e;
  --ink-3:      #6e7681;
  --ink-mute:   #6e7681;
  --ink-faint:  #484f58;

  --accent:      var(--lime-400);
  --accent-2:    var(--lime-500);
  --accent-ink:  var(--lime-400);
  --accent-on:   var(--c-950);
  --accent-a6:   rgba(189, 219, 32, 0.06);
  --accent-a10:  rgba(189, 219, 32, 0.10);
  --accent-a16:  rgba(189, 219, 32, 0.16);
  --accent-a28:  rgba(189, 219, 32, 0.28);
  --accent-a40:  rgba(189, 219, 32, 0.45);

  --topbar-bg:   rgba(9, 12, 16, 0.86);
  --topbar-line: rgba(255, 255, 255, 0.09);
  --menu-bg:     rgba(22, 27, 34, 0.98);
  --menu-bg-2:   rgba(22, 27, 34, 0.96);
  --popover-bg:  rgba(22, 27, 34, 0.99);
  --scrim:       rgba(1, 4, 9, 0.66);

  --shadow-nav:     0 1px 0 rgba(255, 255, 255, 0.04);
  --shadow-card:    0 1px 0 rgba(255, 255, 255, 0.03), 0 16px 40px rgba(1, 4, 9, 0.55);
  --shadow-card-1:  0 16px 40px rgba(1, 4, 9, 0.5);
  --shadow-card-2:  0 10px 30px rgba(1, 4, 9, 0.45);
  --shadow-menu:    0 16px 48px rgba(1, 4, 9, 0.6);
  --shadow-pop:     0 16px 48px rgba(1, 4, 9, 0.66);
  --shadow-drawer:  -16px 0 48px rgba(1, 4, 9, 0.6);
  --shadow-sidebar: 8px 0 32px rgba(1, 4, 9, 0.5);
  --shadow-mmenu:   0 16px 48px rgba(1, 4, 9, 0.6);
  --shadow-lift:    0 16px 40px rgba(1, 4, 9, 0.55);

  --glow-hero:   radial-gradient(ellipse at center,
                   rgba(189, 219, 32, 0.16) 0%,
                   rgba(189, 219, 32, 0.05) 40%,
                   transparent 70%);
  --glow-accent: 0 0 12px rgba(189, 219, 32, 0.40);
  --grid-line:   rgba(255, 255, 255, 0.025);
  --grad-text:   linear-gradient(92deg, #ffffff 0%, var(--lime-400) 55%, var(--blue) 100%);
  --grad-accent: linear-gradient(90deg, var(--lime-400), var(--lime-500));

  --ok:      #7ac074;
  --ok-bg:   rgba(122, 192, 116, 0.14);
  --ok-bd:   rgba(122, 192, 116, 0.42);
  --warn:    #e6b428;
  --warn-bg: rgba(230, 180, 40, 0.16);
  --warn-bd: rgba(230, 180, 40, 0.45);
  --err:     #e07a6d;
  --err-bg:  rgba(224, 122, 109, 0.14);
  --err-bd:  rgba(224, 122, 109, 0.42);
  --err-a7:  rgba(224, 122, 109, 0.10);
  --err-a10: rgba(224, 122, 109, 0.14);
  --err-a28: rgba(224, 122, 109, 0.38);
  --info:    #b8b8c1;

  --blue-ink:   var(--blue);
  --purple-ink: var(--purple);
  --amber-ink:  var(--amber);
}

/* ─── Compatibility aliases ────────────────────────────────────────────────
   Names the console and the hub already use, kept pointing at the new scale so
   no existing rule has to be edited to keep working. New code should use the
   names above; these exist so the migration could be one step instead of two.
   --lime-a* was console.css's spelling of the accent tints; --lime-a40 is not
   an alias of --accent-a40 by accident — both spellings existed with the same
   intent and are now one value. */
:root,
:root[data-theme="dark"] {
  --lime-a6:  var(--accent-a6);
  --lime-a8:  var(--accent-a6);
  --lime-a10: var(--accent-a10);
  --lime-a12: var(--accent-a10);
  --lime-a15: var(--accent-a16);
  --lime-a16: var(--accent-a16);
  --lime-a28: var(--accent-a28);
  --lime-a30: var(--accent-a28);
  --lime-a40: var(--accent-a40);
  --lime-a50: var(--accent-a40);
  --on-dark:  #ffffff;
}

/* ───────────────────────────── src/02-base.css ───────────────────────────── */
/* ═══════════════════════════════════════════════════════════════════════════
   IG1 UI — 02 · BASE
   Reset, document defaults, typography, focus. Everything here is on ELEMENT
   selectors only — no classes — so it is safe to load underneath a surface's
   own stylesheet: any existing class rule outranks it on specificity, and the
   console's 1800 lines keep winning wherever they already have an opinion.
   ═════════════════════════════════════════════════════════════════════════ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* Someone navigating by keyboard past a sticky topbar lands under it without
   this; 76px clears the tallest bar either surface has. */
:target { scroll-margin-top: 76px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* Both surfaces are exported to PDF (the one-pagers ship as .pdf next to the
     .html). Without these the dark ground is dropped and light text prints
     onto white paper — invisible. */
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  color: var(--ink-strong);
  font-weight: 800;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

p, ul, ol, dl, pre, figure, blockquote { margin: 0 0 var(--space-4); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent-ink);
  text-decoration: none;
}
a:hover { text-decoration: underline; }

strong, b { color: var(--ink-strong); font-weight: 700; }
small { font-size: var(--fs-xs); }

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-8) 0;
}

code, kbd, samp, pre { font-family: var(--mono); }
code {
  font-size: 0.92em;
  color: var(--accent-ink);
  background: var(--accent-a6);
  padding: 0.12em 0.4em;
  border-radius: var(--radius-xs);
}
pre {
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  overflow-x: auto;          /* wide code scrolls itself; the page never does */
  font-size: var(--fs-sm);
  line-height: 1.7;
}
pre code {
  color: inherit;
  background: none;
  padding: 0;
  border-radius: 0;
  font-size: inherit;
}

img, svg, video, canvas { max-width: 100%; }
img, svg, video { height: auto; }

table { border-collapse: collapse; width: 100%; }

/* Form controls do not inherit the document face on their own. */
input, button, textarea, select { font: inherit; color: inherit; }

/* ── Focus ────────────────────────────────────────────────────────────────
   :focus-visible only, so a mouse click does not draw a ring but a Tab does.
   Two-tone: the accent ring plus a --bg outer band, so it stays visible
   against an accent-coloured control where a single lime ring would vanish. */
:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

::selection {
  background: var(--accent-a28);
  color: var(--ink-strong);
}

/* ── Scrollbars ───────────────────────────────────────────────────────────
   A default light scrollbar on a near-black page is a bright stripe down the
   edge. color-scheme (set per theme in 01-tokens) already fixes the native
   ones; these style the WebKit overlay to match the surface. */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-2);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--radius-pill);
}
::-webkit-scrollbar-thumb:hover { background-clip: content-box; background-color: var(--line-hi); }

/* ── Motion ───────────────────────────────────────────────────────────────
   The design leans on transitions and a couple of gradients. Anyone who has
   asked the OS to stop moving things gets a static page — including the
   smooth-scroll above, which is motion the user did not initiate. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ───────────────────────────── src/03-components.css ───────────────────────────── */
/* ═══════════════════════════════════════════════════════════════════════════
   IG1 UI — 03 · COMPONENTS

   ── WHY EVERY CLASS HERE STARTS WITH ui- ────────────────────────────────────
   These are ADDITIVE. The console owns .card / .stat-grid / .status-badge and
   the hub owns .card / .eyebrow / .surface-chip; three of those names collide
   across the two surfaces with different meanings. A shared package that
   claimed the bare names would silently restyle components it had never seen,
   in markup generated from app.js template literals, with the damage showing
   up only in whichever view a human happened to open.
   So: nothing here can collide, and adopting a component is an explicit edit.
   The EXISTING components are modernised by the token retune in 01-tokens.css
   instead — same class names, new palette, new elevation — which is what makes
   a one-pass restyle of 1800 lines survivable.

   Everything is token-driven, so each component is correct in both themes
   without a single theme-specific selector below this line.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── Layout ─────────────────────────────────────────────────────────────── */
.ui-page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--space-10) var(--space-10);
}
.ui-section { margin-top: var(--space-section); }
.ui-prose { max-width: var(--measure); }

/* ── Eyebrow ────────────────────────────────────────────────────────────────
   Mono, uppercase, wide tracking, accent. The reference leans on this hard —
   it is what makes a heading feel labelled rather than just big.
   NOTE the padding-less letter-spacing trap: tracking adds space AFTER the
   last glyph, so a centred eyebrow looks off-centre. text-indent compensates. */
.ui-eyebrow {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--accent-ink);
  margin-bottom: var(--space-3);
  text-indent: 0.18em;
}
.ui-eyebrow.is-blue   { color: var(--blue-ink); }
.ui-eyebrow.is-purple { color: var(--purple-ink); }
.ui-eyebrow.is-muted  { color: var(--ink-3); }

/* ── Hero ───────────────────────────────────────────────────────────────────
   Two stacked decorations, both pointer-events:none so they never eat a click:
   ::before is the brand glow, ::after a faint grid faded out by a mask. The
   grid is what stops a large flat dark area from looking empty. */
.ui-hero {
  position: relative;
  overflow: hidden;
  padding: var(--space-16) 0 var(--space-12);
}
.ui-hero::before {
  content: "";
  position: absolute;
  top: -260px; left: 50%;
  width: 900px; height: 560px;
  transform: translateX(-50%);
  background: var(--glow-hero);
  pointer-events: none;
}
.ui-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, #000 30%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, #000 30%, transparent 75%);
  pointer-events: none;
}
.ui-hero > * { position: relative; z-index: 1; }
.ui-hero.is-centered { text-align: center; }

.ui-display {
  font-size: var(--fs-display);
  font-weight: 800;
  letter-spacing: var(--ls-display);
  line-height: var(--lh-tight);
  color: var(--ink-strong);
  margin: 0 0 var(--space-5);
}

/* Gradient text. -webkit-text-fill-color:transparent is what actually reveals
   the gradient; `color` stays set as the fallback for anything that does not
   support background-clip, and the print block at the foot restores it. */
.ui-grad {
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--accent-ink);
}

.ui-lede {
  font-size: 17px;
  line-height: var(--lh-body);
  color: var(--ink-2);
  max-width: 640px;
  margin: 0 0 var(--space-8);
}
.ui-hero.is-centered .ui-lede { margin-left: auto; margin-right: auto; }
.ui-lede strong { color: var(--ink-1); font-weight: 600; }

/* ── Section heading ────────────────────────────────────────────────────── */
.ui-section-head { margin-bottom: var(--space-8); }
.ui-section-head h2 { margin-bottom: var(--space-3); }
.ui-section-sub {
  font-size: var(--fs-body);
  color: var(--ink-2);
  line-height: var(--lh-body);
  max-width: 640px;
  margin: 0;
}

/* ── Stat strip ─────────────────────────────────────────────────────────────
   The hairline trick: a 1px GRID GAP over a --line background, so the dividers
   are the gaps themselves. One rounded, clipped container, no per-cell borders
   to double up at the seams. */
.ui-stats {
  display: grid;
  grid-template-columns: repeat(var(--ui-stats-cols, 4), 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.ui-stat {
  background: var(--surface);
  padding: var(--space-6) var(--space-5);
  text-align: center;
}
.ui-stat-v {
  display: block;
  font-family: var(--mono);
  font-size: 32px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink-strong);
  line-height: 1.1;
}
.ui-stat-v .u { font-size: 15px; color: var(--ink-3); font-weight: 600; }
.ui-stat.is-hot .ui-stat-v { color: var(--accent-ink); }
.ui-stat-k {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Card ───────────────────────────────────────────────────────────────── */
.ui-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}
.ui-card {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.ui-card.is-raised { box-shadow: var(--shadow-card); }
.ui-card.is-interactive:hover {
  border-color: var(--accent-a40);
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
}
/* The 3px gradient cap. Painted only when a hue modifier asks for it, so a
   plain card stays plain. */
.ui-card.has-cap::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--grad-accent);
}
.ui-card.has-cap.is-blue::before   { background: var(--blue); }
.ui-card.has-cap.is-purple::before { background: var(--purple); }
.ui-card-num {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.1em;
  color: var(--ink-mute);
  margin-bottom: var(--space-3);
}
.ui-card h3 { margin-bottom: var(--space-2); }
.ui-card p { font-size: var(--fs-sm); color: var(--ink-2); line-height: var(--lh-body); }

/* ── Badge / pill ───────────────────────────────────────────────────────── */
.ui-badge {
  display: inline-block;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 4px 11px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  color: var(--accent-ink);
  background: var(--accent-a10);
  border: 1px solid var(--accent-a40);
}
.ui-badge.is-blue   { color: var(--blue-ink);   background: rgba(88, 166, 255, 0.10);  border-color: rgba(88, 166, 255, 0.5); }
.ui-badge.is-purple { color: var(--purple-ink); background: rgba(163, 113, 247, 0.10); border-color: rgba(163, 113, 247, 0.5); }
.ui-badge.is-muted  { color: var(--ink-2);      background: var(--inset);              border-color: var(--line-2); }
.ui-badge.is-ok     { color: var(--ok);         background: var(--ok-bg);              border-color: var(--ok-bd); }
.ui-badge.is-warn   { color: var(--warn);       background: var(--warn-bg);            border-color: var(--warn-bd); }
.ui-badge.is-err    { color: var(--err);        background: var(--err-bg);             border-color: var(--err-bd); }

/* ── Dot ────────────────────────────────────────────────────────────────── */
.ui-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--accent);
  box-shadow: var(--glow-accent);
}
.ui-dot.is-blue   { background: var(--blue);   box-shadow: 0 0 8px rgba(88, 166, 255, 0.7); }
.ui-dot.is-purple { background: var(--purple); box-shadow: 0 0 8px rgba(163, 113, 247, 0.7); }
.ui-dot.is-ok     { background: var(--ok);     box-shadow: none; }
.ui-dot.is-err    { background: var(--err);    box-shadow: none; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.ui-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: var(--fs-sm);
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.ui-btn:hover { text-decoration: none; transform: translateY(-1px); }
.ui-btn:active { transform: translateY(0); }
.ui-btn-primary {
  background: var(--accent);
  color: var(--accent-on);          /* 12:1 — the dark ink, never white */
  border-color: var(--accent);
}
.ui-btn-primary:hover { background: var(--accent-2); border-color: var(--accent-2); }
.ui-btn-secondary {
  background: var(--surface);
  color: var(--ink-strong);
  border-color: var(--line-2);
}
.ui-btn-secondary:hover { border-color: var(--line-hi); background: var(--surface-2); }
.ui-btn-ghost { background: transparent; color: var(--ink-2); }
.ui-btn-ghost:hover { color: var(--ink-strong); background: var(--inset); }

/* ── Table ──────────────────────────────────────────────────────────────── */
.ui-table-wrap { overflow-x: auto; border-radius: var(--radius-lg); }
.ui-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.ui-table thead th {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: left;
  padding: var(--space-3) var(--space-5);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line-2);
}
.ui-table tbody td {
  padding: var(--space-3) var(--space-5);
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.ui-table tbody tr:last-child td { border-bottom: none; }
.ui-table tbody tr:hover td { background: var(--accent-a6); }

/* ── Terminal ───────────────────────────────────────────────────────────── */
.ui-terminal {
  background: var(--inset);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-card-2);
}
.ui-term-bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px var(--space-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.ui-term-bar .c { width: 10px; height: 10px; border-radius: 50%; background: var(--line-hi); }
.ui-term-title { margin-left: var(--space-2); font-family: var(--mono); font-size: var(--fs-2xs); color: var(--ink-3); }
.ui-term-body {
  padding: var(--space-4);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  line-height: 1.85;
  overflow-x: auto;
}
.ui-term-body .p   { color: var(--purple-ink); }
.ui-term-body .cmd { color: var(--ink-strong); }
.ui-term-body .out { color: var(--ink-2); }
.ui-term-body .ok  { color: var(--accent-ink); }
.ui-term-body .hl  { color: var(--blue-ink); }

/* ── Band (pull-quote / call to action) ─────────────────────────────────── */
.ui-band {
  border: 1px solid var(--accent-a28);
  border-radius: var(--radius-xl);
  padding: var(--space-12) var(--space-10);
  text-align: center;
  background:
    radial-gradient(ellipse 60% 120% at 50% -20%, var(--accent-a10), transparent 70%),
    var(--surface);
}
.ui-band-q {
  font-size: clamp(20px, 2.6vw, 27px);
  font-weight: 700;
  letter-spacing: var(--ls-heading);
  line-height: 1.35;
  color: var(--ink-strong);
  max-width: 780px;
  margin: 0 auto var(--space-3);
}
.ui-band-a { font-size: var(--fs-xs); color: var(--ink-3); }

/* ── Callout ────────────────────────────────────────────────────────────── */
.ui-note {
  border-left: 3px solid var(--accent);
  background: var(--accent-a6);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--space-4) var(--space-5);
  font-size: var(--fs-sm);
  color: var(--ink-1);
}
.ui-note.is-warn { border-left-color: var(--warn); background: var(--warn-bg); }
.ui-note.is-err  { border-left-color: var(--err);  background: var(--err-bg); }

/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .ui-page { padding: 0 var(--space-5) var(--space-8); }
  .ui-stats { grid-template-columns: repeat(2, 1fr); }
  .ui-hero { padding: var(--space-10) 0 var(--space-8); }
}
@media (max-width: 520px) {
  .ui-stats { grid-template-columns: 1fr; }
}

/* ── Print ──────────────────────────────────────────────────────────────────
   The one-pagers are exported to PDF and live beside their .pdf in the repo.
   Two things break there and both are fixed here rather than per-document:
   Chrome drops background-clip:text when printing, so gradient headlines come
   out INVISIBLE; and A4 at 794px would trip the 900px breakpoint above and
   print the mobile layout. */
@media print {
  .ui-grad {
    background: none;
    -webkit-text-fill-color: var(--accent-ink);
    color: var(--accent-ink);
  }
  .ui-hero::before, .ui-hero::after { display: none; }
  .ui-page { max-width: none; padding: 0 24px 24px; }
  .ui-section { margin-top: var(--space-12); }
  .ui-stats { grid-template-columns: repeat(var(--ui-stats-cols, 4), 1fr); }
  .ui-card, .ui-table, .ui-terminal, .ui-band, .ui-stats { break-inside: avoid; }
  .ui-section { break-inside: avoid-page; }
  tr { break-inside: avoid; }
}
