/* ─────────────────────────────────────────────────────────────────────────
   LVL Nine — shared design tokens and shell layout for the toolkit pages.
   Linked from every kit. Self-hosted fonts and design tokens, no
   third-party CSS at runtime.
   ───────────────────────────────────────────────────────────────────────── */

/* ─── Self-hosted fonts ──────────────────────────────────────────────────
   Alcyone is the LVL brand sans-serif. Sixteen cuts (Thin → Black plus
   italics) hosted on Cloudflare R2 at static-assets.lvl9.uk/alcyone/.
   Browser lazy-loads each weight on first @font-face match. See Brand
   Guidelines typography section for the full download package, or
   public/assets/fonts/ALCYONE-R2-MANIFEST.md for upload instructions.
   JetBrains Mono remains self-hosted at /assets/fonts/.
   ───────────────────────────────────────────────────────────────────────── */

/* Alcyone — uprights */
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 100;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Thin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 200;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-ExtraLight.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Light.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Regular.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Medium.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-SemiBold.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Bold.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-Black.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Alcyone — italics */
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 100;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-ThinItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 200;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-ExtraLightItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-LightItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-RegularItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-MediumItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 600;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-SemiBoldItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-BoldItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alcyone';
  font-style: italic;
  font-weight: 900;
  font-display: swap;
  src: url('https://static-assets.lvl9.uk/alcyone/web-ps/woff2/Alcyone-BlackItalic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-variable-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

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

/* ─── Light tokens (WCAG AA throughout) ─── */
:root {
  /* Surfaces */
  --bg:           #1A1C24;   /* Wave AF: cooled ink — sidebar, less violet */
  --nav-bg:       #1A1C24;   /* Wave AF: cooled ink */
  --page-bg:      #FAFAFA;   /* content — near white, neutral */
  --elevated:     #F4F5F7;   /* Wave AF: cool gray, neutral (was #F2F0F8 violet-tinted) */
  /* Slightly darker than --elevated for header/footer strips on console
     panels (KitPanel). ~3% lower in lightness — same hue family. */
  --elevated-strong: #EBEDF0;

  /* Rules — Wave AF: slightly crisper edges (Cloudscape grammar) */
  --rule:         rgba(0,0,0,0.10);
  --rule-strong:  rgba(0,0,0,0.18);

  /* Sidebar (always dark) */
  --nav-text:        #E8E4F2;
  --nav-text-muted:  rgba(232,228,242,0.58); /* WCAG AA on sidebar bg */
  --nav-text-group:  rgba(232,228,242,0.58);
  --nav-rule:        rgba(255,255,255,0.10);
  /* Wave AC retired the active-row pill background in favour of a 2px
     left-border. --nav-active-bg is kept for backward-compat — outside
     consumers may still reference it; remove only at a major bump. */
  --nav-active-bg:   rgba(155,138,251,0.18);   /* Wave AF: cooler, matches new dark accent */
  --nav-active-text: #B7A8E5;                  /* Wave AF: cooled by ~5% saturation */

  /* Text — all meet WCAG AA on --page-bg (#FAFAFA).
     Ratios verified by `npm run audit:contrast` (see scripts/audit-contrast.mjs). */
  --text-primary:   #110F1C;   /* 18.1:1 on page-bg — AAA */
  --text-secondary: #3D3555;   /* 10.9:1 on page-bg — AAA */
  --text-muted:     #6B6080;   /* 5.6:1 on page-bg — AA   */

  /* Accent — Wave AF: enterprise violet, less saturated than v0.8 #5B21B6 */
  --accent:         #4C1D95;   /* deeper, less saturated, still LVL Nine */
  --accent-pale:    rgba(76,29,149,0.14);
  --accent-rule:    rgba(76,29,149,0.22);

  /* Status */
  --green:          #166534;   /* 6.8:1 on page-bg — AA   */
  --green-pale:     rgba(22,101,52,0.14);
  --green-rule:     rgba(22,101,52,0.25);
  --amber:          #854D0E;   /* 6.6:1 on page-bg — AA   */
  --amber-pale:     rgba(133,77,14,0.14);
  --amber-rule:     rgba(133,77,14,0.25);
  --red:            #991B1B;   /* 8.0:1 on page-bg — AAA  */
  --red-pale:       rgba(153,27,27,0.14);
  --red-rule:       rgba(153,27,27,0.25);

  /* Code blocks */
  --code-bg:   #EFEDF3;        /* Wave AF: cooled, kept barely tinted as deliberate "this is code" identity */
  --code-text: #2A1F4E;        /* 12.6:1 on code-bg — AAA */
  --code-rule: rgba(91,33,182,0.14);

  /* Type */
  --font: 'Alcyone', system-ui, sans-serif;
  --mono: 'JetBrains Mono', 'Courier New', monospace;

  /* Type scale (px). Single source of truth for all kits. */
  --fs-display: 48px;        /* editorial display — Web Design Kit's CSS sample row references it; SectionHeader's clamp(30px,4vw,44px) sits between this and h1 */
  --fs-h1:      24px;        /* Wave AA: kits go operational (was 36px); Brand Guidelines hero stays editorial via clamp() in EditorialPrimitives.tsx */
  --fs-h2:      20px;
  --fs-h3:      16px;
  --fs-body-lg: 16px;        /* lead/intro paragraphs — unchanged across density */
  --fs-body:    14px;        /* Wave Z: kits go operational at 14px (was 15px). Brand Guidelines opts back into 15px via [data-density="comfortable"] below. */
  --fs-body-sm: 13px;
  --fs-label:   11px;
  --fs-code:    12px;

  /* Spacing scale. Use these instead of hard-coded px values. */
  --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;

  /* Radius scale. */
  --radius-xs:   4px;   /* compact tags, chips */
  --radius-sm:   6px;   /* buttons, small controls */
  --radius-md:   8px;   /* inputs, dropdowns */
  --radius-lg:   7px;   /* cards, panels, code blocks */
  --radius-xl:   10px;  /* modals, sheets */
  --radius-full: 999px; /* pills, switches */

  /* Shadows. Light mode is restrained on purpose. */
  --shadow-1: 0 1px 2px rgba(17,15,28,0.04);
  --shadow-2: 0 1px 3px rgba(17,15,28,0.06), 0 4px 12px rgba(17,15,28,0.03);
  --shadow-3: 0 6px 24px rgba(17,15,28,0.08), 0 2px 6px rgba(17,15,28,0.04);

  /* Motion. Honoured by kits and overridden by prefers-reduced-motion below. */
  --dur-1: 100ms;       /* micro: hover tints, focus shifts */
  --dur-2: 150ms;       /* default: button hover, simple state */
  --dur-3: 250ms;       /* layout: reveals, panel opens */
  --dur-4: 400ms;       /* heavy: progress fills, content swaps */
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);  /* default: enter-and-settle */
  --ease-emphasis: cubic-bezier(0.3, 0, 0, 1);  /* slightly snappier */
  --ease-exit:     cubic-bezier(0.4, 0, 1, 1);  /* leave-screen */
}

/* ─── Dark tokens (WCAG AA throughout) ─── */
@media (prefers-color-scheme: dark) {
  :root {
    --bg:           #121420;   /* Wave AF: cooled ink */
    --nav-bg:       #121420;   /* Wave AF: cooled ink */
    --page-bg:      #1B1D29;   /* Wave AF: cool deep ink, less violet */
    --elevated:     #212330;   /* Wave AF: cool gray, neutral (one shade darker than original mirror to keep dark_textMuted AA) */
    /* dark mirror of --elevated-strong; see :root for the contract. */
    --elevated-strong: #2D3041;

    --rule:         rgba(255,255,255,0.10);   /* Wave AF: crisper edges */
    --rule-strong:  rgba(255,255,255,0.18);

    --nav-text:        #E8E4F2;
    --nav-text-muted:  rgba(232,228,242,0.58);
    --nav-text-group:  rgba(232,228,242,0.58);
    --nav-rule:        rgba(255,255,255,0.10);
    --nav-active-bg:   rgba(155,138,251,0.18);   /* Wave AF: cooler, matches new dark accent */
    --nav-active-text: #B7A8E5;                   /* Wave AF: cooled by ~5% saturation */

    --text-primary:   #EDE9F8;   /* ~14:1 */
    --text-secondary: #B8B0D4;   /* ~7.2:1 */
    --text-muted:     #8E86AC;   /* WCAG AA on page-bg and elevated */

    --accent:         #9B8AFB;   /* Wave AF: cooler counterpart to new light accent */
    --accent-pale:    rgba(155,138,251,0.18);
    --accent-rule:    rgba(155,138,251,0.32);

    --code-bg:   #212330;        /* Wave AF: matches new --elevated */
    --code-text: #C5C2E8;        /* Wave AF: cooler counterpart */
    --code-rule: rgba(160,124,248,0.20);

    --green:          #6EE7A0;
    --green-pale:     rgba(110,231,160,0.18);
    --green-rule:     rgba(110,231,160,0.28);
    --amber:          #FCD34D;
    --amber-pale:     rgba(252,211,77,0.18);
    --amber-rule:     rgba(252,211,77,0.28);
    --red:            #FCA5A5;
    --red-pale:       rgba(252,165,165,0.18);
    --red-rule:       rgba(252,165,165,0.28);

    /* Dark mode shadows: heavier so they're visible on dark surfaces. */
    --shadow-1: 0 1px 2px rgba(0,0,0,0.30);
    --shadow-2: 0 1px 3px rgba(0,0,0,0.35), 0 4px 16px rgba(0,0,0,0.20);
    --shadow-3: 0 8px 32px rgba(0,0,0,0.45), 0 2px 8px rgba(0,0,0,0.25);
  }
}

/* ─── Force overrides (used by the in-app theme toggle) ─── */
body.force-light {
  --bg:#1A1C24; --nav-bg:#1A1C24; --page-bg:#FAFAFA; --elevated:#F4F5F7;
  --elevated-strong:#EBEDF0; /* light mirror; see :root for the contract */
  --rule:rgba(0,0,0,0.10); --rule-strong:rgba(0,0,0,0.18);
  --nav-text:#E8E4F2; --nav-text-muted:rgba(232,228,242,0.58);
  --nav-text-group:rgba(232,228,242,0.58); --nav-rule:rgba(255,255,255,0.10);
  --nav-active-bg:rgba(155,138,251,0.18); --nav-active-text:#B7A8E5;
  --text-primary:#110F1C; --text-secondary:#3D3555; --text-muted:#6B6080;
  --accent:#4C1D95; --accent-pale:rgba(76,29,149,0.08); --accent-rule:rgba(76,29,149,0.22);
  --code-bg:#EFEDF3; --code-text:#2A1F4E; --code-rule:rgba(91,33,182,0.14);
  --green:#166534; --green-pale:rgba(22,101,52,0.08); --green-rule:rgba(22,101,52,0.25);
  --amber:#854D0E; --amber-pale:rgba(133,77,14,0.08); --amber-rule:rgba(133,77,14,0.25);
  --red:#991B1B; --red-pale:rgba(153,27,27,0.08); --red-rule:rgba(153,27,27,0.25);
  --shadow-1: 0 1px 2px rgba(17,15,28,0.04);
  --shadow-2: 0 1px 3px rgba(17,15,28,0.06), 0 4px 12px rgba(17,15,28,0.03);
  --shadow-3: 0 6px 24px rgba(17,15,28,0.08), 0 2px 6px rgba(17,15,28,0.04);
}
body.force-dark {
  --bg:#121420; --nav-bg:#121420; --page-bg:#1B1D29; --elevated:#212330;
  --elevated-strong:#2D3041; /* dark mirror; see :root for the contract */
  --rule:rgba(255,255,255,0.10); --rule-strong:rgba(255,255,255,0.18);
  --nav-text:#E8E4F2; --nav-text-muted:rgba(232,228,242,0.58);
  --nav-text-group:rgba(232,228,242,0.58); --nav-rule:rgba(255,255,255,0.10);
  --nav-active-bg:rgba(155,138,251,0.18); --nav-active-text:#B7A8E5;
  --text-primary:#EDE9F8; --text-secondary:#B8B0D4; --text-muted:#8E86AC;
  --accent:#9B8AFB; --accent-pale:rgba(155,138,251,0.12); --accent-rule:rgba(155,138,251,0.32);
  --code-bg:#212330; --code-text:#C5C2E8; --code-rule:rgba(160,124,248,0.20);
  --green:#6EE7A0; --green-pale:rgba(110,231,160,0.10); --green-rule:rgba(110,231,160,0.28);
  --amber:#FCD34D; --amber-pale:rgba(252,211,77,0.10); --amber-rule:rgba(252,211,77,0.28);
  --red:#FCA5A5; --red-pale:rgba(252,165,165,0.10); --red-rule:rgba(252,165,165,0.28);
  --shadow-1: 0 1px 2px rgba(0,0,0,0.30);
  --shadow-2: 0 1px 3px rgba(0,0,0,0.35), 0 4px 16px rgba(0,0,0,0.20);
  --shadow-3: 0 8px 32px rgba(0,0,0,0.45), 0 2px 8px rgba(0,0,0,0.25);
}

/* ─── Density opt-in ────────────────────────────────────────────────────
   Wave Z: kits run "compact" by default (14px body, 36px controls). Brand
   Guidelines pins data-density="comfortable" at its app root to opt back
   into the editorial register (15px body, 44px row hook). Future
   primitives (KitTable rows, etc.) can read the attribute presence
   without redefining type tokens.
   ───────────────────────────────────────────────────────────────────────── */
[data-density="comfortable"] { --fs-body: 15px; }

/* ─── Base ─── */
html { font-size: 16px; }
body {
  font-family: var(--font);
  /* page-bg, not --bg: --bg is the sidebar colour (deep violet-charcoal),
     and using it as the body bg made slow renders flash that colour
     before the .main grid laid out. */
  background: var(--page-bg);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 99px; }
.main ::-webkit-scrollbar-thumb { background: var(--rule-strong); }

button { cursor: pointer; font-family: var(--font); }

pre { white-space: pre-wrap; word-break: break-word; }

/* ─── Screen-reader only utility ─── */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ─── Skip link ─── */
.skip-link {
  position: absolute;
  top: -100%;
  left: 16px;
  z-index: 9999;
  padding: 10px 18px;
  background: var(--accent);
  color: #fff;
  font-family: var(--font);
  font-size: 14px;
  font-weight: 600;
  border-radius: 0 0 6px 6px;
  text-decoration: none;
  transition: top 0.1s;
}
.skip-link:focus { top: 0; outline: 3px solid #fff; outline-offset: 2px; }

/* ─── Global focus indicator ─── */
:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}
:focus:not(:focus-visible) { outline: none; }

/* ─── Sidebar nav row hover ──────────────────────────────────────────────
   Non-active rows show a 1px left-line at 50% opacity on hover.
   Active rows already have a 2px solid left-border (set inline) and
   are skipped here. Implemented with inset box-shadow so the hover
   indicator never shifts text horizontally — the 2px transparent
   left-border baseline stays put.
   ───────────────────────────────────────────────────────────────────────── */
.sidebar-nav-row:hover:not([aria-current="page"]) {
  box-shadow: inset 1px 0 0 0 color-mix(in srgb, var(--nav-active-text) 50%, transparent);
}

/* ─── Status-dot pulse ───────────────────────────────────────────────────
   Subtle "live" animation on operational/incident dots. The pulse is a
   GPU-friendly scale+fade on a ::after pseudo-element rather than a
   box-shadow animation (which would force a repaint on every frame).
   Speed and opacity are tuned per status: green is slow and quiet
   (everything is fine, don't be loud), amber is medium (something
   needs attention), red is faster (active incident).
   The animation is suppressed entirely under prefers-reduced-motion
   by the global rule below — no per-rule guard needed.
   Status meaning is also conveyed by colour AND label text, so a user
   with motion off loses nothing semantic. */
.lvl9-status-dot {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  border-radius: 50%;
  /* The dot itself sits at full opacity; the halo is the pseudo. */
}
.lvl9-status-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: currentColor;
  opacity: 0;
  pointer-events: none;
  /* will-change hints the compositor; only set on the animating layer. */
  will-change: transform, opacity;
}
.lvl9-status-dot[data-pulse="green"]::after {
  animation: lvl9-pulse 2.4s var(--ease-standard) infinite;
}
.lvl9-status-dot[data-pulse="amber"]::after {
  animation: lvl9-pulse-strong 1.8s var(--ease-standard) infinite;
}
.lvl9-status-dot[data-pulse="red"]::after {
  animation: lvl9-pulse-strong 1.2s var(--ease-standard) infinite;
}
@keyframes lvl9-pulse {
  0%   { transform: scale(1);   opacity: 0.55; }
  70%  { transform: scale(2.4); opacity: 0;    }
  100% { transform: scale(2.4); opacity: 0;    }
}
@keyframes lvl9-pulse-strong {
  0%   { transform: scale(1);   opacity: 0.7;  }
  70%  { transform: scale(2.8); opacity: 0;    }
  100% { transform: scale(2.8); opacity: 0;    }
}

/* IconStatus spinner. Class-based so the prefers-reduced-motion blanket
   below collapses the duration to ~0ms. Status meaning is also conveyed
   by colour AND label text — a user with motion off loses nothing. */
.lvl9-spin {
  animation: lvl9-spin 1s linear infinite;
  transform-origin: 50% 50%;
}
@keyframes lvl9-spin {
  to { transform: rotate(360deg); }
}

/* KitTable skeleton shimmer. Class-based so the prefers-reduced-motion
   blanket below collapses the duration to ~0ms — under reduce-motion the
   skeleton becomes a static grey bar, which still communicates "loading".
   The gradient derives from --rule and --rule-strong so the bar reads
   correctly on light, dark, and forced-theme surfaces; no per-mode rule
   needed. */
.lvl9-shimmer {
  background-image: linear-gradient(
    90deg,
    var(--rule) 0%,
    var(--rule-strong) 50%,
    var(--rule) 100%
  );
  background-size: 200% 100%;
  background-position: 100% 0;
  border-radius: var(--radius-xs);
  animation: lvl9-shimmer 1.4s linear infinite;
}
@keyframes lvl9-shimmer {
  to { background-position: -100% 0; }
}

/* ─── KitButton hover (Wave AG) ──────────────────────────────────────────
   :hover can't live in the inline style of the React component, so we
   read the variant's hover values from CSS variables that KitButton
   emits inline. Disabled buttons opt out via :not(:disabled). All
   variants share the same rule — only the consumed variables change. */
.lvl9-kit-btn:hover:not(:disabled) {
  background:    var(--lvl9-kit-btn-bg-hover);
  border-color:  var(--lvl9-kit-btn-border-hover);
  color:         var(--lvl9-kit-btn-color-hover);
  filter:        var(--lvl9-kit-btn-filter-hover);
}

/* ─── KitInput focus + placeholder (Wave AG) ─────────────────────────────
   The focus state is the Cloudscape signature: a 2px solid ring drawn
   via box-shadow so it sits OUTSIDE the border without shifting the
   input. --lvl9-kit-input-ring flips between --accent and --red based
   on aria-invalid; the rule reads whichever the component set. */
.lvl9-kit-input::placeholder {
  color: var(--text-muted);
  opacity: 1;
}
.lvl9-kit-input:focus-visible {
  border-color: var(--lvl9-kit-input-ring);
  box-shadow: 0 0 0 2px var(--lvl9-kit-input-ring);
  outline: none;
}

/* ─── KitTable row hover (Wave AG) ───────────────────────────────────────
   Body rows tint to --elevated on hover. Selected rows inherit the same
   tint — the 2px accent rail on the first cell wins visually, so we
   don't fight it with a separate selected:hover state. Header,
   skeleton, and empty-state rows opt out by not carrying the class. */
.lvl9-kit-table-row {
  transition: background var(--dur-1) var(--ease-standard);
}
.lvl9-kit-table-row:hover {
  background: var(--elevated);
}

/* ─── Reduced motion ─── */
/*
  Honour the user's preference. We don't kill *all* motion (some
  transitions communicate state); we collapse them to near-instant
  so they're not disorienting. Status-dot pulses fall under this rule
  too — the dots stay coloured but stop animating.
*/
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─── Shell layout ─── */
.layout { display: flex; min-height: 100vh; }

.sidebar {
  width: 240px;
  min-width: 240px;
  height: 100vh;
  position: sticky;
  top: 0;
  background: var(--nav-bg);
  border-right: 1px solid var(--nav-rule);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-shrink: 0;
}

.main {
  flex: 1;
  min-width: 0;
  background: var(--page-bg);
}

.content {
  max-width: 740px;
  padding: 64px 72px 120px;
  margin: 0 auto;
}

/* Wider content column for kits with longer code samples. */
.content--wide { max-width: 820px; }

/* Mobile header — only visible below 700px (paired with .mobile-nav).
   Re-introduces the kit name and the theme toggle that desktop users
   see in the sidebar. The header sits over --page-bg, not --bg, so its
   theme-toggle button uses page-bg-aware colours rather than the
   dark sidebar palette. */
.mobile-header {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 24px 12px;
  background: var(--page-bg);
  border-bottom: 1px solid var(--rule);
}
.mobile-header-name {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.mobile-header-name strong {
  font-family: var(--font);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.025em;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mobile-header-name small {
  font-family: var(--font);
  font-size: 10px;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.mobile-nav {
  display: none;
  padding: 16px 24px 18px;
  background: var(--page-bg);
  border-bottom: 1px solid var(--rule);
}
.mobile-nav label {
  display: block;
  margin-bottom: 6px;
  color: var(--text-muted);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
}
.mobile-nav select {
  width: 100%;
  min-height: 44px;
  border: 1px solid var(--rule-strong);
  border-radius: 8px;
  background: var(--elevated);
  color: var(--text-primary);
  font: 600 15px/1.3 var(--font);
  padding: 0 12px;
}

@media (max-width: 960px) {
  .sidebar { width: 210px; min-width: 210px; }
  .content { padding: 48px 40px 80px; }
}
@media (max-width: 700px) {
  .sidebar { display: none; }
  .mobile-header { display: flex; }
  .mobile-nav { display: block; }
  .content { padding: 24px 24px 80px; }
}

/* ─── KitAppShell layout (Wave AD) ──────────────────────────────────────
   The console-flavoured shell that hosts internal-tool surfaces. Layout
   chrome is owned here so consumers can compose without redefining
   widths or breakpoints. The shell itself runs full-viewport (height:
   100vh, top-bar + side-nav pinned via flex, body scrolls). Side nav
   collapses from 240px to a 56px icon rail below the 960px breakpoint.
   The consumer is expected to pass two children inside `sideNav` — a
   wide list and an icon rail — and let the rules below pick the right
   one for the current viewport. Page header padding tightens with the
   content gutter at narrow widths so titles don't shove against the
   edge.
   ───────────────────────────────────────────────────────────────────────── */

/* Wide-side / narrow-side nav switcher. Consumers wrap the desktop list
   in .lvl9-app-nav-wide and the icon rail in .lvl9-app-nav-narrow.
   Default state shows wide and hides narrow; the breakpoint flips them. */
.lvl9-app-nav-wide   { display: block; }
.lvl9-app-nav-narrow { display: none; }

@media (max-width: 960px) {
  .lvl9-app-shell-sidenav {
    width: 56px !important;
    min-width: 56px !important;
  }
  .lvl9-app-nav-wide   { display: none; }
  .lvl9-app-nav-narrow { display: block; }
  .lvl9-app-shell-page-header { padding: 20px 24px 16px; }
}
/* Manual collapse override (Wave AH). The KitAppShell consumer flips
   `sideNavCollapsed` and the shell adds this class to the side nav at
   any viewport — same 56px icon-rail visual that the 960px breakpoint
   already produces, just driven by the KitSideNavToggle button instead
   of viewport width. */
.lvl9-app-shell-sidenav-forced-narrow {
  width: 56px !important;
  min-width: 56px !important;
}
.lvl9-app-shell-sidenav-forced-narrow .lvl9-app-nav-wide   { display: none; }
.lvl9-app-shell-sidenav-forced-narrow .lvl9-app-nav-narrow { display: block; }
/* Docked inspector hides below 1280px so the main column doesn't get
   crushed between the side nav and the inspector. Consumers that need
   the inspector visible at narrow widths should use inspectorMode="overlay"
   — that path renders the floating <aside role="dialog"> regardless of
   viewport width. */
@media (max-width: 1279px) {
  .lvl9-app-shell-inspector-docked { display: none; }
}
@media (max-width: 700px) {
  .lvl9-app-shell-page-header { padding: 16px 20px 14px; }
}

/* ─── Interactive widget hovers (Wave AH) ────────────────────────────────
   Ghost-style top-bar buttons (KitSideNavToggle, KitNotificationsBell)
   sit on --bg (the dark sidebar/top-bar surface) so hover gets a
   subtle white wash. Notification rows sit in the popover (page-bg
   surface) so hover lifts to --elevated. */
.lvl9-sidenav-toggle:hover { background: rgba(255,255,255,0.06); }
.lvl9-notif-row:hover { background: var(--elevated); }

/* ─── Cloudscape TopNavigation sticky (Wave AI.2.1.5) ────────────────────
   Cloudscape ships TopNavigation as a flow-positioned sibling of AppLayout.
   AppLayout's headerSelector measures its height for side-nav offset but
   does NOT pin it to the viewport. Make the anchor wrapper sticky so the
   bar stays in view as the page scrolls; z-index keeps it above AppLayout's
   side nav and content. */
#cs-top-nav-anchor {
  position: sticky;
  top: 0;
  z-index: 1000;
}
