@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;800&display=swap');
/* =====================================================================
   kaizen-style v4 — ELEMENT GRAMMAR pass (2026-07-09, model: claude-fable-5)
   v4 adds the locked element vocabulary (DS23): kz-chip / kz-timeline / kz-row
   / kz-table+kz-num / kz-empty / kz-skeleton / kz-field / kz-stat / kz-steps /
   kz-envnote — all ADDITIVE; encoded user-consensus rules; see the v4 block.
   Previous: v3 ACCENT + ICONS pass (2026-07-07)
   · every theme now ships a real accent + a per-theme --on-accent, ALL pairs
     measured (WCAG 2.1): text-on-accent >= 4.5:1 — ratios inline below.
   · shared inline-SVG icon set (DS19) injected by kaizen.js; style via .kz-icon.
   · touchpanel accent-glow card hover (DS18) is now KIT-standard (was a hub override).
   Previous: v2 CRAFT ELEVATION (2026-07-06) · ratio-audited rev
   House style, served at style.kaizenlogic.com. Single source for ALL
   Kaizen hosted apps. Edit HERE; every linked app updates.

   PROPORTION LAW (this rev): every value derives from the shared scales —
     · type  = named tokens only (display / title=h2 / lead / h3 / lg / body
               / sm / hint / label / micro). No raw off-scale font sizes.
     · space = 4·8 ramp {4 8 12 16 22 24 32 44 48 64 96} + gap 14 + pad 18.
     · radius= ONE scale by role: 8 small (buttons/inputs) · 10 default
               (cards/menus) · 12 brand coin. (DECISIONS DS6.)
     · button= ONE scale: 44 default (page) · 36 compact (.kz-btn-sm, bars).
     · control height = 44 tap · 36 compact. Icon glyphs sized deliberately.
   Adoption (2 tags):
     <link rel="stylesheet" href="https://style.kaizenlogic.com/kaizen.css">
     <script src="https://style.kaizenlogic.com/kaizen.js" defer></script>
   ===================================================================== */

/* ---------- tokens: Nightman (default, dark) ---------- */
:root{
  --bg:#0a0a0b; --panel:#101012; --panel2:#161618; --line:#26262a;
  --ink:#f4f4f5; --mut:#8a8a92; --dim:#5a5a62;
  /* ACCENT (v3): nightman carries the brand blue (BL11) — the default theme finally
     has an accent instead of a white box. MEASURED: on-accent #0a0a0b on #4f8ef7 = 6.17:1,
     accent vs bg = 6.17:1. Dark glyph on lit key = the touchpanel language (DS18). */
  --stripe:#4f8ef7; --ok:#3ad29f; --bad:#ff5a5f; --warn:#f5b14c; --trace:#9aa0ff;
  /* L162/DS48 audit — nightman already held both new floors, no retune. MEASURED panel/bg/panel2:
     --trace #9aa0ff (hue 236, blue info lane, aliases nothing) 8.01/8.34/7.61 · --mut #8a8a92
     (body copy, DS48(2) floor 4.5) 5.55/5.78/5.28. --dim #5a5a62 is 2.78 on --panel and carries
     NO floor — in exchange DS48(4) forbids it from ever being the sole carrier of meaning. */
  --on-accent:#0a0a0b;
  /* elevation — restrained, premium (not a drop-shadow contest) */
  --kz-shadow-1:0 1px 2px rgba(0,0,0,.28);
  --kz-shadow-2:0 6px 24px -8px rgba(0,0,0,.45);
  --kz-shadow-pop:0 18px 48px -12px rgba(0,0,0,.55);
  --kz-scrim:rgba(6,6,8,.56);
  /* BRAND LOCKUP — one FIXED treatment, identical on every theme (Bud 2026-07-06:
     the logo NEVER recolors). Derived from the real white-on-black masters in
     Brand/real-logos/; the whole lockup carries its own dark "coin". */
  --kz-coin-bg:#000000;
  --kz-coin-ink:#ffffff;
  --kz-coin-edge:rgba(255,255,255,.18);
  --kz-coin-radius:12px;
  /* motion (DESIGN_SYSTEM.md 6.1; DP-9) */
  --kz-dur-fast:120ms; --kz-dur:200ms; --kz-dur-lg:300ms;
  --kz-ease-decel:cubic-bezier(.16,1,.3,1);           /* enter/settle — strong decelerate */
  --kz-ease-standard:cubic-bezier(.4,0,.2,1);
}
/* ---------- themes (ids stable — they live in users' localStorage) ---------- */
[data-theme="daylight"]{--bg:#f5f7fa;--panel:#ffffff;--panel2:#eef2f7;--line:#d9e0e8;--ink:#1b2733;--mut:#586675;--dim:#93a1b0;--stripe:#2059e0;--on-accent:#ffffff;/* 5.89:1 on accent; accent vs bg 5.49:1 (was #2f6df6: white text 4.53:1 — too close to the wire) */--trace:#1f57cf;--ok:#0e7449;--warn:#8a5a06;--bad:#c22a3f;/* L160 AA: status hues on a white panel were BELOW 4.5 (ok 3.48 · warn 4.15 · bad 4.57 panel but 4.06 panel2 · trace 4.53 panel but 4.03 panel2). Shade tuned, meaning untouched (DS24 families held: ok 155 green · warn 38 amber · bad 352 red · trace 221 blue). NOW panel/bg/panel2: ok 5.81/5.42/5.17 · warn 5.92/5.52/5.27 · bad 5.68/5.29/5.05 · trace 6.32/5.89/5.62 */--kz-scrim:rgba(20,28,40,.34);--kz-shadow-1:0 1px 2px rgba(20,28,40,.10);--kz-shadow-2:0 8px 28px -10px rgba(20,28,40,.22);--kz-shadow-pop:0 24px 56px -16px rgba(20,28,40,.30);}
[data-theme="synthwave"]{--bg:#190b2e;--panel:#241141;--panel2:#2d1850;--line:#3d2470;--ink:#f4e9ff;--mut:#a98fd0;--dim:#6b549a;--stripe:#ff5dcd;--on-accent:#190b2e;/* 6.82:1 */--trace:#36e0ff;--ok:#3ddc97;/* DS24: ok stays GREEN in every theme (was cyan #36e0ff = the info color — semantic collision); 9.65:1 on panel *//* L162/DS48 audit — no retune needed: --trace #36e0ff (hue 189) 10.77/11.75/9.77 panel/bg/panel2, --mut #a98fd0 6.11/6.67/5.54. */--warn:#ffd24a;--bad:#ff5d8f;--kz-scrim:rgba(10,4,22,.60);}
[data-theme="matrix"]{--bg:#020a04;--panel:#06180c;--panel2:#0a2613;--line:#11401d;--ink:#b6ffb6;--mut:#4f9f5f;--dim:#2f7040;--stripe:#39ff14;--on-accent:#020a04;/* 14.78:1 */--trace:#2ce0ee;--ok:#39ff14;--warn:#ffd24a;/* DS24: warn stays AMBER in every theme (was lime #c8ff4a — read as a second green); 12.74:1 on panel */--bad:#ff6a6f;/* L162/DS48(1): --trace WAS #39ff14 — the same pixel as --ok AND --stripe, so the info lane and the success lane were indistinguishable. Now a phosphor cyan (hue 184 — terminal-adjacent, the ANSI bright-cyan the theme quotes, not a corporate blue), distinct from ok/stripe green 111 · warn 45 · bad 358. panel/bg/panel2: trace 11.37/12.41/10.01. DS48(2) --mut #4f9f5f 5.65/6.16/4.97 (body-copy floor 4.5). */--kz-scrim:rgba(1,6,3,.62);}
[data-theme="plumber"]{--bg:#5c94fc;--panel:#ffffff;--panel2:#eaf1ff;--line:#b3c9ee;--ink:#15233f;--mut:#33507e;--dim:#294573;--stripe:#e52521;--on-accent:#ffffff;/* 4.55:1 on accent; accent vs WHITE PANEL 4.55:1 (buttons live on panels; on the raw blue bg the red key reads by shadow+border, luminance-close by theme design) */--trace:#1a5ac9;--ok:#18782c;--warn:#925c00;--bad:#cd1c18;/* L160 AA: was ok/trace 3.49 · warn 4.10 · bad 4.01 on --panel2 — below 4.5. Shade tuned only (hues 133/38/1 held). NOW panel/panel2: ok+trace 5.58/4.92 · warn 5.60/4.94 · bad 5.56/4.91. --bad no longer aliases --stripe (accent 4.55 pair in DS20 untouched); --bg is the raw blue key wall, not a text surface in this theme (its own --mut is 2.74 there) *//* L162/DS48(1): --trace WAS #18782c — the same pixel as --ok, so info and success were one lane. Now a royal blue (hue 218) that quotes the theme's own sky key without touching it; distinct from ok 133 · warn 38 · bad/stripe 1. panel/panel2: trace 6.27/5.53. --bg stays the key wall (DS48(3) named list). DS48(2) --mut #33507e 8.11/7.15. */--kz-scrim:rgba(8,20,45,.42);--kz-shadow-1:0 1px 2px rgba(8,20,45,.14);--kz-shadow-2:0 10px 30px -10px rgba(8,20,45,.28);--kz-shadow-pop:0 24px 56px -16px rgba(8,20,45,.36);}
[data-theme="dk"]{--bg:#3a9a5c;--panel:#ffffff;--panel2:#e7f2e7;--line:#bcd9bc;--ink:#16280f;--mut:#3c5a30;--dim:#2e4824;--stripe:#d8541f;--on-accent:#120e06;/* 4.77:1 (white was 4.03 — fails; dark glyph on the orange key) */--trace:#14539b;--ok:#23762b;--warn:#905c00;--bad:#b04519;/* L160 AA: was ok 3.46 · warn 3.49 · bad 4.03 on --panel — below 4.5. Shade tuned only (hues 126/38/17 held). NOW panel/panel2: ok 5.68/4.94 · warn 5.65/4.91 · bad 5.66/4.92 (trace was #7a4a1f at 7.43/6.46 — measured fine but BROWN, in no status family at all; see L162 below). --bg is the green key wall, not a text surface (its own --mut is 2.21 there) *//* L162/DS48(1): --trace WAS #7a4a1f, a brown — out of the blue/cyan info family entirely and never flagged because nothing pinned --trace. Now a steel blue (hue 212 — the arcade girder blue, deep like daylight's because this is a white-panel theme), distinct from ok 126 · warn 38 · bad 17 · stripe 17. panel/panel2: trace 7.66/6.66. --bg stays the key wall (DS48(3) named list). DS48(2) --mut #3c5a30 7.79/6.77. */--kz-scrim:rgba(10,24,10,.42);--kz-shadow-1:0 1px 2px rgba(10,24,10,.14);--kz-shadow-2:0 10px 30px -10px rgba(10,24,10,.26);--kz-shadow-pop:0 24px 56px -16px rgba(10,24,10,.34);}
[data-theme="brawler"]{--bg:#2b2f3a;--panel:#363b48;--panel2:#424857;--line:#525a6b;--ink:#eef1f6;--mut:#b0b8c8;--dim:#7b8496;--stripe:#e8403a;--on-accent:#0a0b0e;/* 4.90:1 (white was 4.02 — fails) */--trace:#86c8ff;--ok:#74d09c;--warn:#ffb02e;--bad:#ffa7a1;/* L160 AA: was ok 3.95 on --panel2 · bad 4.01 on --panel / 3.28 on --panel2 — below 4.5. Dark theme, so the shade LIGHTENS (hues 146/4 held). NOW panel/bg/panel2: ok 5.99/7.16/4.90 · bad 6.02/7.19/4.92 · warn 6.13/7.32/5.01 (already passing) *//* L162/DS48(1): --trace WAS #ffb02e — the same pixel as --warn, so an info flash read as a warning. Now an electric sky blue (hue 207 — the arcade neon this theme runs on), distinct from ok 146 · warn 37 · bad 4 · stripe 2. panel/bg/panel2: trace 6.25/7.46/5.10. DS48(2): --mut was #aab2c2 = 4.29 on --panel2, UNDER the body-copy floor; lightened minimally to #b0b8c8 (hue 220 held) = 5.62/6.71/4.59. */--kz-scrim:rgba(10,12,18,.56);}
html[data-theme="plumber"]{background:#3cb043}
html[data-theme="dk"]{background:#5a3a1a}

/* ---------- base ---------- */
.kz-body, body.kz{margin:0;padding:0;background:var(--bg);color:var(--ink);
  font-family:"Montserrat","Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;}
.kz code,.kz .mono,.kz pre{font-family:"JetBrains Mono",ui-monospace,"SF Mono",Menlo,Consolas,monospace;}
.kz ::selection{background:var(--ink);color:var(--bg)}
.kz a{color:var(--ink)}
.kz-body.kz-scroll-lock,body.kz.kz-scroll-lock{overflow:hidden}

/* ---------- BRAND LOCKUP (one FIXED treatment — never recolors per theme) ----------
   Source of truth = real masters in Brand/real-logos/. "kaizen" BOLD, "Logic" THIN
   — Montserrat 800 / 300, NEVER bold on "Logic". Glyph sizes are a deliberate brand
   ramp (24 stacked · 20 horizontal/icon · 18 in-bar · 16 stacked-in-bar). */
.kz-header{border-bottom:1px solid var(--line);padding:22px 28px 0;position:relative;background:var(--bg)}
.kz-brand{display:flex;align-items:center;gap:14px;margin-bottom:16px;flex-wrap:wrap}

/* the coin: shared dark backing for EVERY lockup variant (fixed, theme-independent) */
.kz-coin{display:inline-flex;background:var(--kz-coin-bg);border:1px solid var(--kz-coin-edge);
  border-radius:var(--kz-coin-radius);box-shadow:var(--kz-shadow-1);text-decoration:none;
  transition:transform var(--kz-dur-fast) var(--kz-ease-standard),box-shadow var(--kz-dur) var(--kz-ease-decel)}
a.kz-coin:hover{box-shadow:var(--kz-shadow-2)}
a.kz-coin:active{transform:translateY(.5px)}
.kz-coin .logo{font-family:"Montserrat",system-ui,-apple-system,"Segoe UI",sans-serif;
  color:var(--kz-coin-ink);letter-spacing:-.02em;line-height:.98}
.kz-coin .logo.k{font-weight:800}   /* kaizen — BOLD  */
.kz-coin .logo.l{font-weight:300}   /* Logic  — THIN (acceptable floor 400; never 800) */

/* STACKED = the PRIMARY lockup (Bud 2026-07-06). kaizen over Logic. */
.kz-lockup{display:inline-flex;flex-direction:column;align-items:center;text-align:center;
  padding:8px 18px;text-decoration:none}
.kz-lockup .logo{display:block;font-size:24px}
.kz-lockup .logo.l{margin-top:-1px}

/* HORIZONTAL = the one-row lockup — kaizenLogic on one line (single capital L).
   The APP BAR + hub header default to this (a horizontal lockup sits in a single
   row far more cleanly than the taller stacked coin — DESIGN_SYSTEM §5.3). */
.kz-lockup-h{flex-direction:row;align-items:baseline;padding:8px 16px}
.kz-lockup-h .logo{display:inline;font-size:20px}

/* ICON = kL monogram for the tightest spots / favicon-scale (k bold, L thin) */
.kz-lockup-icon{padding:8px 12px}
.kz-lockup-icon .logo{display:inline;font-size:20px}
.kz-vdiv{width:1px;height:32px;background:var(--line)}
.kz-tagline{color:var(--mut);font-size:var(--kz-fs-hint);letter-spacing:.08em;text-transform:uppercase}
.kz-beta{color:var(--stripe);font-size:var(--kz-fs-micro);letter-spacing:.1em;text-transform:uppercase;
  border:1px solid var(--stripe);border-radius:999px;padding:2px 8px;vertical-align:middle;font-weight:600}
/* L3: --stripe as micro TEXT fails 4.5:1 on the 3 game themes (plumber 1.54-4.55 · dk 1.14-4.03 · brawler 2.28-3.33
   across panel/panel2/bg) — measured overrides below pass ALL three contexts; the other 4 themes' --stripe
   measures >=5.24 everywhere and keeps the accent (DS4 / DS20 pattern) */
[data-theme="plumber"] .kz-beta{color:#15233f;border-color:#15233f} /* 15.62 panel / 13.78 panel2 / 5.28 bg */
[data-theme="dk"] .kz-beta{color:#0d2012;border-color:#0d2012}      /* 17.04 panel / 14.81 panel2 / 4.83 bg */
[data-theme="brawler"] .kz-beta{color:var(--ink);border-color:var(--ink)} /* 9.89 panel / 8.08 panel2 / 11.81 bg */
/* brand tick — a STATIC accent (the auto-sweep was decorative motion; cut for restraint, DP-9) */
.kz-stripe{height:3px;background:var(--stripe);width:44px;margin:16px 0 0;border-radius:2px}

/* ---------- theme picker ---------- */
.kz-themewrap{position:relative;margin-left:auto}
.kz-themebtn{display:inline-flex;align-items:center;gap:8px;background:var(--panel2);color:var(--mut);
  border:1px solid var(--line);border-radius:var(--kz-radius-sm);font:inherit;font-size:var(--kz-fs-label);
  padding:8px 12px;cursor:pointer;
  transition:color var(--kz-dur-fast) var(--kz-ease-standard),border-color var(--kz-dur-fast) var(--kz-ease-standard),background var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-themebtn:hover{color:var(--ink);border-color:var(--dim)}
.kz-themebtn:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}
.kz-themebtn .tlbl{font-size:var(--kz-fs-micro);letter-spacing:.1em;text-transform:uppercase;color:inherit;margin-right:2px} /* L3: --dim failed 4.5:1 on every theme (2.3-2.7); inherit = the button's --mut (+ brawler's own override), measured >=4.97 all themes */
.kz-themebtn .tcar{font-size:var(--kz-fs-micro);opacity:.7;margin-left:1px}
.kz-thememenu{position:absolute;top:calc(100% + 8px);right:0;z-index:60;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--kz-radius);padding:8px;min-width:196px;box-shadow:var(--kz-shadow-pop);
  max-height:min(70vh,440px);overflow:auto;transform-origin:top right;
  animation:kz-menu-in var(--kz-dur) var(--kz-ease-decel)}
@keyframes kz-menu-in{from{opacity:0;transform:translateY(-6px) scale(.98)}to{opacity:1;transform:none}}
.kz-thememenu[hidden]{display:none}
.kz-thememenu button{display:flex;align-items:center;gap:8px;width:100%;background:none;border:0;color:var(--mut);
  font:inherit;font-size:var(--kz-fs-hint);text-align:left;padding:8px 12px;border-radius:var(--kz-radius-sm);cursor:pointer;
  transition:background var(--kz-dur-fast) var(--kz-ease-standard),color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-thememenu button:hover{background:var(--panel2);color:var(--ink)}
.kz-thememenu button[aria-checked="true"]{color:var(--ink)}
.kz-thememenu button[aria-checked="true"]::after{content:"\2713";margin-left:auto;color:var(--stripe);font-size:var(--kz-fs-label)}

/* ---------- footer (v3: the lockup stands ALONE on a centered brand row — Bud 2026-07-07;
   meta line + theme picker beneath it) ---------- */
/* L36 footer (2026-07-09, Claudio redesign — Bud: "fighting the footer the whole time"):
   ONE calm row. Lockup small + horizontal on the left, meta inline beside it,
   links + theme picker pushed right. Wraps gracefully narrow; never centered,
   never stacked. */
.kz-footer{position:relative;border-top:1px solid var(--line);color:var(--mut);font-size:var(--kz-fs-micro);
  padding:16px 32px;background:var(--bg)}
.kz-footer-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.kz-footer-meta{display:inline-flex;align-items:center;min-width:0}
.kz-footer-links{display:inline-flex;align-items:center;gap:14px;margin-left:auto}
@media(max-width:560px){ /* narrow: TWO tidy rows — brand + theme up top, meta beneath (never three staggered lines) */
  .kz-footer{padding:14px 20px}
  .kz-footer-meta{flex-basis:100%;order:3}
  .kz-footer-links a{display:none} /* the lockup IS the home link — the text link is redundant where width is scarce */
}
.kz-footer .kz-lockup-h{flex:0 0 auto}
.kz-footer .kz-lockup-h .logo{font-size:14px}
.kz-footer-theme{display:inline-flex}
.kz-footer-theme .kz-themebtn .tlbl{display:inline}
.kz-footer-theme .kz-thememenu{top:auto;bottom:calc(100% + 8px);transform-origin:bottom right;animation:kz-menu-up var(--kz-dur) var(--kz-ease-decel)}
@keyframes kz-menu-up{from{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:none}}
.kz-footer .cta{color:var(--mut)}
.kz-footer .cta a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--stripe)}
.kz-sigdots{display:inline-flex;gap:4px;margin-right:8px;vertical-align:middle}
.kz-sigdots i{width:6px;height:6px;border-radius:2px;display:inline-block}
.kz-sigdots i:nth-child(1){background:#4f8ef7}.kz-sigdots i:nth-child(2){background:#ef4444}.kz-sigdots i:nth-child(3){background:#cfcfd4}
/* footer brand lockup — small HORIZONTAL coin (L36 2026-07-09; the stacked footer coin is dead) */
.kz-footer .kz-lockup-h{padding:4px 10px}
[data-theme="plumber"] .kz-footer{background:#3cb043;border-top:4px solid #2a7d2f;color:#0c2a08}
[data-theme="plumber"] .kz-footer .cta,[data-theme="plumber"] .kz-footer .cta a{color:#0c2a08;border-bottom-color:#0c2a08}
[data-theme="dk"] .kz-footer{background:#5a3a1a;border-top:4px solid #3a2410;color:#f3e9d8}
[data-theme="dk"] .kz-footer .cta,[data-theme="dk"] .kz-footer .cta a{color:#f3e9d8;border-bottom-color:#f3e9d8}

/* on-colored-bg taglines: plumber/dk backgrounds need darker text than --mut (WCAG AA) */
[data-theme="plumber"] .kz-tagline{color:#15233f}
[data-theme="dk"] .kz-tagline{color:#0d2012}
[data-theme="brawler"] .kz-themebtn{color:#b8c0cf}

/* =====================================================================
   kz- component kit — tokens are the single source for type + spacing.
   ===================================================================== */

/* ---------- type scale ---------- */
:root{
  --kz-fs-display:clamp(32px,5.4vw,52px);  /* hero */
  --kz-fs-h1:28px;                          /* console page title (kz-canvas h1) — DS25 */
  --kz-fs-title:22px;                       /* section statement (= h2 step) */
  --kz-fs-h2:22px;
  --kz-fs-lead:18px;                        /* marketing lead: hero sub / section lead (the step above lg) */
  --kz-fs-h3:17px;
  --kz-fs-lg:16px;
  --kz-fs-body:16px;
  --kz-fs-sm:14px;
  --kz-fs-hint:13px;
  --kz-fs-label:12px;
  --kz-fs-micro:11px;                       /* uppercase micro-labels (beta / theme / appearance / footer) */
  --kz-fw-normal:400;
  --kz-fw-medium:500;
  --kz-fw-semibold:600;
  --kz-fw-bold:700;
  --kz-fw-black:800;
  --kz-lh-tight:1.06;      /* display */
  --kz-lh-snug:1.3;        /* headings */
  --kz-lh-body:1.6;        /* body */
  --kz-tr-tight:-.03em;    /* display/wordmark optical tightening */
  --kz-tr-label:.14em;
}

/* ---------- spacing scale (one rhythm everywhere) ---------- */
:root{
  --kz-space-1:4px;
  --kz-space-2:8px;
  --kz-space-3:12px;
  --kz-space-4:16px;
  --kz-space-5:24px;      /* layout ramp (DESIGN_SYSTEM §3) */
  --kz-space-6:32px;
  --kz-space-7:48px;
  --kz-space-8:64px;
  --kz-space-9:96px;
  --kz-touch:22px;        /* tap-ergonomic insertion (arm's-length reading) */
  --kz-tap:44px;          /* minimum tap target (Fisher-Price / Apple HIG) */
  --kz-gap:14px;          /* default grid/flex gap */
  --kz-pad:18px;          /* default card/panel padding */
  --kz-radius:10px;       /* default corner (cards/menus) — DECISIONS DS6 */
  --kz-radius-sm:8px;     /* buttons / inputs / chips */
  --kz-col:880px;         /* standard content column */
  --kz-ctrl:36px;         /* compact control height (nav link / .kz-btn-sm) */
  --kz-bar-h:60px;        /* app-bar height, used to offset the mobile drawer */
}

/* ---------- kz-appbar: the shared cross-nav bar (injected by kaizen.js) ----------
   Desktop: [ lockup -> home ] [ app switcher ] ····· [ (cta) ] [ theme toggle ]
   Mobile : [ lockup -> home ] ················· [ burger ]     — nothing else in the bar. */
.kz-appbar{display:flex;align-items:center;gap:var(--kz-space-4);
  padding:8px max(32px,calc(50% - 560px));min-height:var(--kz-bar-h);
  border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--bg) 86%,transparent);
  -webkit-backdrop-filter:saturate(1.4) blur(14px);backdrop-filter:saturate(1.4) blur(14px);
  position:sticky;top:0;z-index:50}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .kz-appbar{background:var(--bg)}
}
.kz-appbar .kz-appbrand{flex:0 0 auto;text-decoration:none;white-space:nowrap;padding:8px 12px}
.kz-appbar .kz-appbrand .logo{font-size:18px}
.kz-appbar .kz-appbrand.kz-lockup .logo{font-size:16px}   /* stacked, if a surface opts in */
/* app switcher: dead-simple horizontal-scroll link row */
.kz-appswitch{display:flex;align-items:center;gap:var(--kz-space-1);overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;flex:1 1 auto;min-width:0}
.kz-appswitch::-webkit-scrollbar{display:none}
.kz-appswitch a{display:inline-flex;align-items:center;min-height:var(--kz-ctrl);padding:8px 12px;border-radius:var(--kz-radius-sm);
  color:var(--mut);text-decoration:none;font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-medium);white-space:nowrap;
  transition:color var(--kz-dur-fast) var(--kz-ease-standard),background var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-appswitch a:hover{color:var(--ink);background:var(--panel2)}
.kz-appswitch a[aria-current="page"]{color:var(--ink);background:var(--panel2);font-weight:var(--kz-fw-semibold)}
.kz-appswitch a[aria-current="page"]::before{content:"";width:6px;height:6px;border-radius:2px;background:var(--stripe);margin-right:8px}
/* hamburger toggle — only shown on narrow viewports */
.kz-appmenu-btn{display:none;align-items:center;justify-content:center;width:var(--kz-tap);height:var(--kz-tap);
  background:transparent;border:1px solid var(--mut);border-radius:var(--kz-radius-sm);color:var(--ink); /* DS49: transparent fill = the border IS the control; --line measured 1.20-1.68, so it was invisible as an affordance. This button renders on the app bar, i.e. on --bg: --mut clears 3:1 there in six of eight palettes (5.48-6.71); the two key-wall themes are the named exception below. */
  cursor:pointer;flex:0 0 auto;padding:0;transition:background var(--kz-dur-fast) var(--kz-ease-standard),border-color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-appmenu-btn:hover{background:var(--panel2)}
.kz-appmenu-btn:active{background:var(--panel2)}
.kz-appmenu-btn:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}
/* crisp 2-line glyph (rotates/fades into an X when open) — no emoji hamburger */
.kz-burger-glyph{position:relative;width:18px;height:12px;display:block}
.kz-burger-glyph::before,.kz-burger-glyph::after{content:"";position:absolute;left:0;right:0;height:2px;border-radius:2px;
  background:currentColor;transition:transform var(--kz-dur) var(--kz-ease-decel),opacity var(--kz-dur-fast) linear}
.kz-burger-glyph::before{top:2px}.kz-burger-glyph::after{bottom:2px}

/* the drawer wrapper: on desktop it lays out inline (switcher + cta + theme pinned right).
   On mobile it becomes a real OVERLAY SHEET (scrim + slide), so the bar is ONLY lockup+burger. */
.kz-appnav{display:flex;align-items:center;gap:var(--kz-space-4);flex:1 1 auto;min-width:0}
.kz-appnav .kz-themewrap{margin-left:auto}
.kz-appbar .kz-beta{flex:0 0 auto;align-self:center;line-height:1}
/* optional primary CTA in the bar/drawer (data-kaizen-cta on <body>) — a compact bar button on
   desktop, a full-width primary in the mobile sheet. Only for surfaces WITHOUT the app switcher
   (the switcher's own Portal tab is the door on tool surfaces — don't double up). */
.kz-appcta{display:none}
.kz-appnav > .kz-appcta{display:inline-flex;flex:0 0 auto}

/* the scrim — one shared element, sits under the sheet on mobile */
.kz-scrim{position:fixed;inset:0;z-index:45;background:var(--kz-scrim);opacity:0;visibility:hidden;
  -webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);
  transition:opacity var(--kz-dur-lg) var(--kz-ease-decel),visibility var(--kz-dur-lg)}
.kz-scrim[data-show="1"]{opacity:1;visibility:visible}

@media(max-width:640px){
  .kz-appbar{padding:8px 24px}
  .kz-appmenu-btn{display:inline-flex;margin-left:auto}
  .kz-appbar[data-open="1"] .kz-burger-glyph::before{top:5px;transform:rotate(45deg)}
  .kz-appbar[data-open="1"] .kz-burger-glyph::after{bottom:5px;transform:rotate(-45deg)}
  /* the sheet: a fixed panel that slides down from under the bar, full width */
  .kz-appnav{position:fixed;left:0;right:0;top:var(--kz-bar-h);z-index:48;
    flex-direction:column;align-items:stretch;gap:0;
    padding:var(--kz-space-5) var(--kz-space-5) calc(env(safe-area-inset-bottom,0px) + var(--kz-space-5));
    background:var(--panel);border-bottom:1px solid var(--line);box-shadow:var(--kz-shadow-pop);
    max-height:calc(100dvh - var(--kz-bar-h));overflow:auto;
    transform:translateY(-16px);opacity:0;visibility:hidden;pointer-events:none;
    transition:transform var(--kz-dur-lg) var(--kz-ease-decel),opacity var(--kz-dur) var(--kz-ease-decel),visibility var(--kz-dur-lg)}
  .kz-appbar[data-open="1"] .kz-appnav{transform:none;opacity:1;visibility:visible;pointer-events:auto}
  /* nav rows: big, calm, tappable */
  .kz-appnav .kz-appswitch{flex-direction:column;align-items:stretch;overflow:visible;gap:var(--kz-space-1);width:100%}
  .kz-appnav .kz-appswitch a{min-height:48px;font-size:var(--kz-fs-lg);padding:0 16px;border-radius:var(--kz-radius)}
  /* primary CTA — one clear door, full width */
  .kz-appnav > .kz-appcta{display:flex;width:100%;min-height:var(--kz-tap);margin-top:var(--kz-space-4)}
  /* theme picker — a PREFERENCE, tucked at the very bottom under a divider */
  .kz-appnav .kz-themewrap{margin:var(--kz-space-5) 0 0;padding-top:var(--kz-space-4);
    border-top:1px solid var(--line);width:100%}
  .kz-appnav .kz-themewrap::before{content:"Appearance";display:block;font-size:var(--kz-fs-micro);letter-spacing:.1em;
    text-transform:uppercase;color:var(--mut);margin-bottom:var(--kz-space-2)} /* DS48(4): was --dim — a label is text */
  .kz-appnav .kz-themebtn{width:100%;justify-content:flex-start;min-height:var(--kz-tap);border-radius:var(--kz-radius)}
  .kz-appnav .kz-thememenu{position:static;box-shadow:none;border:0;padding:8px 0 0;min-width:0;animation:none;max-height:none}
  .kz-appnav .kz-beta{align-self:flex-start;margin-top:var(--kz-space-3)}
}
[data-theme="plumber"] .kz-appbar,[data-theme="dk"] .kz-appbar{border-bottom-width:2px}
/* DS49 KEY-WALL EXCEPTION — a NAMED list, DS48(3) style, not a wildcard.
   The burger does not paint on --panel: it sits in the app bar / shell bar, whose fill is --bg.
   Measured --mut vs --bg: plumber 2.74, dk 2.21 — both UNDER the 1.4.11 3:1 non-text floor
   (every other palette clears on --mut: 5.48-6.71). Repointed to --ink on exactly these two
   key-wall themes: plumber 5.28, dk 4.43. Named here, named in test/kit.test.js. */
[data-theme="plumber"] .kz-appmenu-btn,[data-theme="dk"] .kz-appmenu-btn{border-color:var(--ink)}

/* ---------- kz-card ---------- */
.kz-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--kz-radius);
  padding:var(--kz-pad);color:var(--ink);font-size:var(--kz-fs-body);line-height:var(--kz-lh-body)}
.kz-card > h3,.kz-card-title{margin:0 0 var(--kz-space-2);font-size:var(--kz-fs-h3);font-weight:var(--kz-fw-bold);
  line-height:var(--kz-lh-snug);color:var(--ink)}
.kz-card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--kz-space-4)}
/* interactive card affordance (opt-in via .kz-card-link) — calm lift, no bounce */
a.kz-card-link{display:block;text-decoration:none;color:var(--ink);
  transition:border-color var(--kz-dur) var(--kz-ease-decel),transform var(--kz-dur) var(--kz-ease-decel),box-shadow var(--kz-dur) var(--kz-ease-decel),background var(--kz-dur) var(--kz-ease-decel)}
a.kz-card-link:hover{border-color:color-mix(in srgb,var(--stripe) 60%,var(--line));transform:translateY(-2px);
  box-shadow:0 0 0 1px color-mix(in srgb,var(--stripe) 55%,transparent),0 10px 30px -6px color-mix(in srgb,var(--stripe) 55%,transparent)}
a.kz-card-link:active{transform:translateY(0);box-shadow:0 0 0 1px color-mix(in srgb,var(--stripe) 55%,transparent),inset 0 0 24px -12px color-mix(in srgb,var(--stripe) 45%,transparent)}
a.kz-card-link:active{transform:translateY(0)}
a.kz-card-link:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}

/* ---------- kz-btn — ONE scale: 44 default, 36 compact (.kz-btn-sm) ---------- */
.kz-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--kz-space-2);
  min-height:var(--kz-tap);padding:12px 24px;border-radius:var(--kz-radius-sm);border:1px solid var(--line);
  background:var(--panel2);color:var(--ink);font:inherit;font-size:var(--kz-fs-body);font-weight:var(--kz-fw-semibold);
  line-height:1;cursor:pointer;text-decoration:none;letter-spacing:.005em;
  transition:transform var(--kz-dur-fast) var(--kz-ease-standard),background var(--kz-dur-fast) var(--kz-ease-standard),border-color var(--kz-dur-fast) var(--kz-ease-standard),box-shadow var(--kz-dur) var(--kz-ease-decel)}
.kz-btn:hover{transform:translateY(-1px)}
.kz-btn:active{transform:translateY(0)}
.kz-btn:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}
.kz-btn:disabled,.kz-btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed;transform:none}
/* compact — bar/toolbar contexts. Same radius + weight, smaller box. */
.kz-btn-sm{min-height:var(--kz-ctrl);padding:8px 16px;font-size:var(--kz-fs-sm)}
.kz-btn-primary{background:var(--stripe);border-color:var(--stripe);color:var(--on-accent);box-shadow:var(--kz-shadow-1)}
.kz-btn-primary:hover{box-shadow:var(--kz-shadow-2)}
/* DS49 / DS48(4) second clause — A BOUNDARY THAT IS THE AFFORDANCE MAY NOT REST ON --dim.
   Secondary's fill is --panel2, i.e. the same tone as the inset surfaces it sits on, so its
   OUTLINE is the whole control: lose the edge and the button stops reading as a button. It
   shipped on --dim, the one token with no contrast floor, which measured 2.35-2.98 against
   its own --panel2 fill in six of the eight palettes (daylight 2.35 · brawler 2.43 · synthwave
   2.47 · nightman 2.64 · matrix 2.70 · branded 2.73) — under the WCAG 1.4.11 3:1 non-text bar
   everywhere the theme did not accidentally save it. Repointed to --mut KIT-WIDE (not scoped:
   the defect was never client-light-specific, L161 just found it there first). MEASURED --mut
   vs its own --panel2 fill: 4.59-7.15 across all 8 palettes, every one clear of 3:1, and
   plumber 7.15 / dk 6.77 run much higher because their --mut is a deep ink on white panels.
   --line is untouched and keeps every non-affordance divider (rails, rules, table edges). */
.kz-btn-secondary{background:var(--panel2);border-color:var(--mut);color:var(--ink)}
.kz-btn-secondary:hover{border-color:var(--ink)}
.kz-btn-ghost{background:transparent;border-color:transparent;color:var(--mut)}
.kz-btn-ghost:hover{background:var(--panel2);color:var(--ink)}
.kz a.kz-btn{color:var(--ink)}
.kz a.kz-btn-primary{color:var(--on-accent)}
.kz a.kz-btn-ghost{color:var(--mut)}

/* ---------- kz-toolbar ---------- */
.kz-toolbar{display:flex;align-items:center;gap:var(--kz-space-2);flex-wrap:wrap;
  padding:var(--kz-space-3) 0;border-bottom:1px solid var(--line)}
.kz-toolbar .kz-toolbar-spacer{flex:1 1 auto}
.kz-toolbar .kz-toolbar-group{display:flex;align-items:center;gap:var(--kz-space-2);flex-wrap:wrap}

/* ---------- kz-tabs ---------- */
.kz-tabs{display:flex;align-items:stretch;gap:var(--kz-space-1);overflow-x:auto;scrollbar-width:none;
  border-bottom:1px solid var(--line)}
.kz-tabs::-webkit-scrollbar{display:none}
.kz-tab{display:inline-flex;align-items:center;min-height:var(--kz-tap);padding:8px 16px;border:0;
  background:transparent;color:var(--mut);font:inherit;font-size:var(--kz-fs-body);font-weight:var(--kz-fw-medium);
  cursor:pointer;white-space:nowrap;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-tab:hover{color:var(--ink)}
.kz-tab[aria-selected="true"],.kz-tab.is-active{color:var(--ink);font-weight:var(--kz-fw-semibold);border-bottom-color:var(--stripe)}
.kz-tab:focus-visible{outline:2px solid var(--stripe);outline-offset:-2px}

/* ---------- kz-details (styled disclosure) ---------- */
.kz-details{border:1px solid var(--line);border-radius:var(--kz-radius);background:var(--panel);overflow:hidden}
.kz-details > summary{list-style:none;cursor:pointer;padding:var(--kz-space-3) var(--kz-pad);
  font-size:var(--kz-fs-body);font-weight:var(--kz-fw-semibold);color:var(--ink);
  display:flex;align-items:center;gap:var(--kz-space-2);min-height:var(--kz-tap)}
.kz-details > summary::-webkit-details-marker{display:none}
.kz-details > summary::after{content:"▾";margin-left:auto;color:var(--mut);font-size:var(--kz-fs-label);transition:transform var(--kz-dur) var(--kz-ease-decel)}
.kz-details[open] > summary::after{transform:rotate(180deg)}
.kz-details > summary:focus-visible{outline:2px solid var(--stripe);outline-offset:-2px}
.kz-details .kz-details-body{padding:0 var(--kz-pad) var(--kz-space-3);color:var(--ink);
  font-size:var(--kz-fs-body);line-height:var(--kz-lh-body)}

/* ---------- kz-icon (DS19: icon-forward, glanceable) ----------
   Sprite is injected by kaizen.js (id #kzIcons). Usage:
     <svg class="kz-icon" aria-hidden="true"><use href="#kz-i-layers"/></svg>
   Inherits currentColor -> themeable for free. Sizes: 20 default / 16 sm / 24 lg. */
.kz-icon{width:20px;height:20px;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;vertical-align:-4px}
.kz-icon-sm{width:16px;height:16px;stroke-width:2;vertical-align:-3px}
.kz-icon-lg{width:24px;height:24px;stroke-width:1.6}
.kz-icon-accent{color:var(--stripe)}
/* icon chip — the glanceable anchor for cards/tiles (a lit key, not a decoration) */
.kz-iconchip{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;
  border-radius:var(--kz-radius-sm);background:color-mix(in srgb,var(--stripe) 12%,var(--panel2));
  color:var(--stripe);border:1px solid color-mix(in srgb,var(--stripe) 22%,var(--line))}
.kz-iconchip .kz-icon{width:18px;height:18px}
/* icon list — icon + short label rows (replaces bullet walls; DS19) */
.kz-iconlist{list-style:none;margin:var(--kz-space-2) 0 2px;padding:0;display:grid;gap:var(--kz-space-3)}
.kz-iconlist li{display:flex;gap:var(--kz-space-3);align-items:flex-start;margin:0}
.kz-iconlist .kz-icon{margin-top:2px;color:var(--stripe)}
.kz-iconlist b{color:var(--ink);font-weight:var(--kz-fw-semibold)}

/* ---------- kz-hint ---------- */
/* .kz-meta is the SAME muted micro-line under another name — absorbed from
   kaizen-share's page shell (DS47) so a client page stops shipping its own copy.
   One rule, so the colored-bg overrides below can never drift apart from it. */
.kz-hint,.kz-meta{color:var(--mut);font-size:var(--kz-fs-hint);line-height:var(--kz-lh-snug)}
/* Flash messages — status lines that must be SEEN (VC9/DS21 era, 2026-07-08).
   Use .kz-flash for errors/blockers, .kz-flash-ok for confirmations; swap back
   to .kz-meta/.kz-hint for neutral status. Pair with aria-invalid on the field. */
.kz-flash{color:var(--bad);font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-bold)}
.kz-flash-ok{color:var(--ok);font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-bold)}
input[aria-invalid="true"],select[aria-invalid="true"],textarea[aria-invalid="true"]{outline:2px solid var(--bad);outline-offset:1px}
[data-theme="plumber"] .kz-hint,[data-theme="plumber"] .kz-meta{color:#15233f}
[data-theme="dk"] .kz-hint,[data-theme="dk"] .kz-meta{color:#0d2012}

/* ---------- AA on colored backgrounds (plumber/dk have colored --bg) ---------- */
[data-theme="plumber"] .kz-appswitch a{color:#15233f}
[data-theme="plumber"] .kz-tab{color:#15233f}
[data-theme="dk"] .kz-appswitch a{color:#0d2012}
[data-theme="dk"] .kz-tab{color:#0d2012}
[data-theme="plumber"] .kz-appswitch a[aria-current="page"],[data-theme="plumber"] .kz-tab[aria-selected="true"],[data-theme="plumber"] .kz-tab.is-active{color:var(--ink)}
[data-theme="dk"] .kz-appswitch a[aria-current="page"]{color:var(--ink)}
[data-theme="dk"] .kz-tab[aria-selected="true"],[data-theme="dk"] .kz-tab.is-active{color:#0d2012}
@media(max-width:640px){
  [data-theme="plumber"] .kz-appnav .kz-appswitch a,[data-theme="dk"] .kz-appnav .kz-appswitch a{color:var(--ink)}
}

/* =====================================================================
   kz v4 — ELEMENT GRAMMAR (UI lock-in pass 2026-07-09, model: claude-fable-5)
   DECISIONS DS23 · spec DESIGN_SYSTEM 7.1 · evidence DESIGN_RESEARCH Parts E/F.
   ALL additive. Each component ENCODES its user-consensus rules:
   chips = tint bg + ink text + dot, sentence-case adjectives, never clickable
   (GOV.UK); timeline = vertical/reverse-chron/plain-language (NN/g); tables =
   mono tabular numerals + row hover + load-more (NN/g/Baymard); empty state
   NEVER renders while loading (skeleton instead); labels above fields, 16px
   inputs, blur validation (Penzo/Baymard/GOV.UK). No toast component — ever:
   feedback is inline (kz-flash). Cmd-K rejected estate-wide (BM5).
   ===================================================================== */

/* ---------- kz-chip: THE status element ---------- */
.kz-chip{display:inline-flex;align-items:center;gap:7px;padding:3px 10px;border-radius:999px;
  background:var(--panel2);color:var(--ink);border:1px solid var(--line);
  font-size:var(--kz-fs-hint);font-weight:var(--kz-fw-semibold);line-height:1.4;white-space:nowrap}
.kz-chip::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--mut);flex:0 0 auto}
.kz-chip-ok{background:color-mix(in srgb,var(--ok) 13%,var(--panel2));border-color:color-mix(in srgb,var(--ok) 30%,var(--line))}
.kz-chip-ok::before{background:var(--ok)}
.kz-chip-warn{background:color-mix(in srgb,var(--warn) 14%,var(--panel2));border-color:color-mix(in srgb,var(--warn) 32%,var(--line))}
.kz-chip-warn::before{background:var(--warn)}
.kz-chip-bad{background:color-mix(in srgb,var(--bad) 13%,var(--panel2));border-color:color-mix(in srgb,var(--bad) 30%,var(--line))}
.kz-chip-bad::before{background:var(--bad)}
.kz-chip-info{background:color-mix(in srgb,var(--trace) 13%,var(--panel2));border-color:color-mix(in srgb,var(--trace) 30%,var(--line))}
.kz-chip-info::before{background:var(--trace)}
/* filter chip = the ONE interactive chip: carries a visible dismiss glyph */
button.kz-chip{cursor:pointer;font:inherit;font-size:var(--kz-fs-hint);font-weight:var(--kz-fw-semibold)}
button.kz-chip .x{color:var(--mut);font-weight:400;margin-left:2px}
button.kz-chip:hover .x{color:var(--ink)}
button.kz-chip:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}

/* ---------- kz-timeline: vertical event history ---------- */
.kz-timeline{list-style:none;margin:0;padding:0}
.kz-timeline > li{position:relative;padding:0 0 var(--kz-space-4) 26px}
.kz-timeline > li::before{content:"";position:absolute;left:0;top:5px;width:9px;height:9px;border-radius:50%;
  background:var(--mut);border:2px solid var(--panel);box-shadow:0 0 0 1px var(--line)}
.kz-timeline > li::after{content:"";position:absolute;left:4px;top:20px;bottom:2px;width:1px;background:var(--line)}
.kz-timeline > li:last-child{padding-bottom:0}
.kz-timeline > li:last-child::after{display:none}
.kz-timeline > li.ok::before{background:var(--ok)}
.kz-timeline > li.warn::before{background:var(--warn)}
.kz-timeline > li.bad::before{background:var(--bad)}
.kz-timeline > li.info::before{background:var(--trace)}
.kz-timeline .kz-tl-head{display:flex;align-items:baseline;gap:var(--kz-space-2);flex-wrap:wrap}
.kz-timeline .kz-tl-head b{font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-semibold);color:var(--ink)}
.kz-timeline .kz-tl-head time,.kz-timeline .kz-tl-head .t{color:var(--mut);font-size:var(--kz-fs-hint)}
.kz-timeline .kz-tl-detail{margin:2px 0 0;color:var(--mut);font-size:var(--kz-fs-hint);line-height:var(--kz-lh-snug)}

/* ---------- kz-list / kz-row: the list->detail row ---------- */
.kz-list{list-style:none;margin:0;padding:0;border:1px solid var(--line);border-radius:var(--kz-radius);
  background:var(--panel);overflow:hidden}
.kz-row{display:flex;align-items:center;gap:var(--kz-space-3);min-height:var(--kz-tap);
  padding:var(--kz-space-3) var(--kz-pad);border-bottom:1px solid var(--line);
  color:var(--ink);text-decoration:none;font-size:var(--kz-fs-body)}
.kz-list > li:last-child > .kz-row,.kz-list > .kz-row:last-child{border-bottom:0}
a.kz-row,button.kz-row{cursor:pointer;transition:background var(--kz-dur-fast) var(--kz-ease-standard);width:100%;
  background:transparent;border-left:0;border-right:0;border-top:0;font:inherit;text-align:left}
a.kz-row:hover,button.kz-row:hover{background:var(--panel2)}
a.kz-row:focus-visible,button.kz-row:focus-visible{outline:2px solid var(--stripe);outline-offset:-2px}
.kz-row-main{flex:1 1 auto;min-width:0}
.kz-row-title{display:block;font-weight:var(--kz-fw-semibold);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kz-row-sub{display:block;color:var(--mut);font-size:var(--kz-fs-hint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kz-row-meta{display:flex;align-items:center;gap:var(--kz-space-2);margin-left:auto;flex:0 0 auto;
  color:var(--mut);font-size:var(--kz-fs-hint);white-space:nowrap}

/* ---------- kz-table: data table + kz-num numerals ---------- */
.kz-tablewrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--kz-radius);background:var(--panel);
  background-image:linear-gradient(to right,var(--panel),var(--panel)),linear-gradient(to right,var(--panel),var(--panel)),
    linear-gradient(to right,var(--kz-scrim),transparent),linear-gradient(to left,var(--kz-scrim),transparent);
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;background-size:24px 100%,24px 100%,12px 100%,12px 100%;
  background-attachment:local,local,scroll,scroll}
.kz-table{width:100%;border-collapse:collapse;font-size:var(--kz-fs-sm)}
.kz-table th{text-align:left;padding:10px var(--kz-pad);border-bottom:1px solid var(--line);
  font-size:var(--kz-fs-micro);font-weight:var(--kz-fw-semibold);letter-spacing:.1em;text-transform:uppercase;color:var(--mut)}
.kz-table td{padding:10px var(--kz-pad);border-bottom:1px solid var(--line);color:var(--ink);vertical-align:middle}
.kz-table tbody tr:last-child td{border-bottom:0}
.kz-table tbody tr:hover td{background:var(--panel2)}
.kz-num{font-family:"JetBrains Mono",ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  font-variant-numeric:tabular-nums;text-align:right}
th.kz-num,td.kz-num{text-align:right}

/* ---------- kz-empty: teach-the-next-action (NEVER while loading) ---------- */
.kz-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
  gap:var(--kz-space-3);padding:var(--kz-space-8) var(--kz-space-5);color:var(--mut)}
.kz-empty .kz-icon{width:28px;height:28px;color:var(--dim);stroke-width:1.5} /* DS48(4)-OK: DECORATIVE — the glyph above an empty state, whose words are .kz-empty b/p in --ink/--mut; carries no meaning alone */
.kz-empty b{color:var(--ink);font-size:var(--kz-fs-h3);font-weight:var(--kz-fw-bold)}
.kz-empty p{margin:0;font-size:var(--kz-fs-sm);line-height:var(--kz-lh-snug);max-width:38ch}
.kz-empty .kz-btn{margin-top:var(--kz-space-2)}

/* ---------- kz-skeleton: content-load placeholder (replaces spinners) ---------- */
.kz-skeleton{position:relative;overflow:hidden;background:var(--panel2);border-radius:var(--kz-radius-sm)}
.kz-skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--ink) 7%,transparent),transparent);
  animation:kz-shimmer 1.4s var(--kz-ease-standard) infinite}
@keyframes kz-shimmer{to{transform:translateX(100%)}}
.kz-skeleton-line{height:14px;margin:10px 0}
.kz-skeleton-block{height:72px;margin:10px 0}

/* ---------- kz-field: form anatomy (label ABOVE, 16px inputs, blur validation) ---------- */
.kz-field{display:flex;flex-direction:column;gap:6px;margin:0 0 var(--kz-space-4)}
.kz-field > label{font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-medium);color:var(--ink)}
.kz-input,.kz-select,.kz-textarea{font:inherit;font-size:var(--kz-fs-body);width:100%;box-sizing:border-box;
  background:var(--panel2);color:var(--ink);border:1px solid var(--line);border-radius:var(--kz-radius-sm);
  padding:10px 14px;min-height:var(--kz-tap);
  transition:border-color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-input::placeholder,.kz-textarea::placeholder{color:var(--mut)} /* DS48(4): placeholder text is TEXT — was --dim (2.6:1 on daylight) */
.kz-input:hover,.kz-select:hover,.kz-textarea:hover{border-color:var(--dim)}
.kz-input:focus-visible,.kz-select:focus-visible,.kz-textarea:focus-visible{outline:2px solid var(--stripe);outline-offset:1px;border-color:var(--stripe)}
.kz-textarea{min-height:88px;resize:vertical;line-height:var(--kz-lh-body)}
.kz-field .kz-hint{margin:0}

/* ---------- kz-stat: KPI glance tile (ONE number; no gauges, ever) ---------- */
.kz-stat{display:flex;flex-direction:column;gap:4px;padding:var(--kz-pad);
  background:var(--panel);border:1px solid var(--line);border-radius:var(--kz-radius)}
.kz-stat .kz-stat-l{font-size:var(--kz-fs-micro);font-weight:var(--kz-fw-semibold);letter-spacing:.1em;
  text-transform:uppercase;color:var(--mut)}
.kz-stat .kz-stat-v{font-size:var(--kz-fs-title);font-weight:var(--kz-fw-black);color:var(--ink);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;line-height:1.1}
.kz-stat .kz-stat-d{font-size:var(--kz-fs-hint);color:var(--mut)}
.kz-stat .kz-stat-d.up{color:var(--ok)}
.kz-stat .kz-stat-d.down{color:var(--bad)}

/* ---------- kz-steps: wizard step indicator (setup flows ONLY) ---------- */
.kz-steps{list-style:none;display:flex;align-items:center;gap:var(--kz-space-4);flex-wrap:wrap;
  margin:0 0 var(--kz-space-5);padding:0;counter-reset:kzstep}
.kz-steps > li{counter-increment:kzstep;display:inline-flex;align-items:center;gap:8px;
  color:var(--mut);font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-medium)}
.kz-steps > li::before{content:counter(kzstep);width:24px;height:24px;border-radius:50%;flex:0 0 auto;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line);background:var(--panel2);color:var(--mut);
  font-size:var(--kz-fs-micro);font-weight:var(--kz-fw-semibold)}
.kz-steps > li[aria-current="step"]{color:var(--ink);font-weight:var(--kz-fw-semibold)}
.kz-steps > li[aria-current="step"]::before{background:var(--stripe);border-color:var(--stripe);color:var(--on-accent)}
.kz-steps > li.done{color:var(--mut)}
.kz-steps > li.done::before{content:"\2713";background:color-mix(in srgb,var(--ok) 15%,var(--panel2));
  border-color:color-mix(in srgb,var(--ok) 40%,var(--line));color:var(--ok)}

/* ---------- kz-envnote: environment honesty (theme-INDEPENDENT, like the coin) ----------
   Injected by kaizen.js from body[data-kaizen-env]; DS16 made visible. Fixed amber:
   #231603 on #f5b14c = 9.4:1. Never themed - it is a signal, not content. */
.kz-envnote{position:fixed;bottom:16px;left:50%;transform:translateX(-50%);z-index:70;
  background:#f5b14c;color:#231603;border:1px solid #b97f14;border-radius:999px;
  padding:7px 16px;font-size:var(--kz-fs-micro);font-weight:var(--kz-fw-bold);
  letter-spacing:.1em;text-transform:uppercase;box-shadow:var(--kz-shadow-2);pointer-events:none;white-space:nowrap}

/* ---------- kz-shell: the CONSOLE chrome (DS25 two-tier, 2026-07-09) ----------
   Ecosystem/product surfaces (hub, portal) = left rail + canvas; marketing keeps
   its top-nav page. Rail rows: icon+label, selection = bg pill + ink shift ONLY
   (never bolds -> zero text reflow). Page authors the markup; kaizen.js skips the
   injected appbar when body[data-kaizen-chrome="shell"]. Mobile: rail becomes a
   burger sheet (visible-nav is a desktop rule). */
.kz-shell{display:grid;grid-template-columns:236px minmax(0,1fr);min-height:100dvh;align-items:start}
.kz-rail{display:flex;flex-direction:column;gap:2px;padding:14px 12px calc(env(safe-area-inset-bottom,0px) + 14px);
  border-right:1px solid var(--line);position:sticky;top:0;height:100dvh;overflow:auto;box-sizing:border-box}
.kz-rail-brand{display:flex;align-items:center;padding:2px 4px 12px}
.kz-rail-brand .kz-coin{text-decoration:none}
.kz-rail-brand .logo{font-size:16px}
.kz-rail nav{display:flex;flex-direction:column;gap:2px}
/* row rules scoped to nav/foot so the BRAND anchor is never restyled (BL1: no gap in kaizenLogic) */
.kz-rail nav a,.kz-rail-foot a{display:flex;align-items:center;gap:10px;min-height:34px;padding:0 10px;border-radius:var(--kz-radius);
  color:var(--mut);text-decoration:none;font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-medium);
  transition:background var(--kz-dur-fast) var(--kz-ease-standard),color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-rail nav a:hover,.kz-rail-foot a:hover{background:var(--panel2);color:var(--ink)}
.kz-rail nav a[aria-current="page"]{background:var(--panel2);color:var(--ink)} /* NO weight change — reflow-free (DS25) */
.kz-rail nav a:focus-visible,.kz-rail-foot a:focus-visible{outline:2px solid var(--stripe);outline-offset:-2px}
.kz-rail nav .kz-icon,.kz-rail-foot .kz-icon{width:18px;height:18px;flex:0 0 auto}
/* DS26 THE ONE RAIL: the current app's row expands to expose its sections as
   indented sub-rows (kit-injected app group + page-registered sections).
   Sub-rows: hint-size, indented under the app label; active = pill + accent
   submark (inset bar) — still NO weight change (reflow-free law). When sections
   are present the app row drops its pill (ink only); the pill lives on the
   active sub-row. */
.kz-rail nav a[data-current="1"]{color:var(--ink)}
.kz-rail-sub{display:flex;flex-direction:column;gap:1px;margin:2px 0 6px 28px}
.kz-rail-sub[hidden]{display:none}
.kz-rail .kz-rail-sub a{min-height:30px;padding:0 8px;font-size:var(--kz-fs-hint);min-width:0}
.kz-rail .kz-rail-sub a > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0} /* long item names truncate, never wrap into blobs (full name = title tooltip) */
.kz-rail-sub-label{font-size:10px;font-weight:var(--kz-fw-semibold);letter-spacing:.09em;text-transform:uppercase;color:var(--mut);padding:6px 8px 2px} /* DS48(4): was --dim — group label — dummy-proof: says what the sub-rows ARE */
.kz-rail-sub-gap{border-top:1px solid var(--line);margin:6px 8px 2px} /* items ↑ / actions ↓ divider */
.kz-rail .kz-rail-sub a .kz-icon{width:15px;height:15px}
.kz-rail .kz-rail-sub a[aria-current="page"]{background:var(--panel2);color:var(--ink);box-shadow:inset 2px 0 0 0 var(--stripe)}
.kz-rail-foot{margin-top:auto;padding-top:12px;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:2px}
.kz-rail-foot:empty{display:none} /* no pill, no line — the separator only exists when something sits under it */
/* game-theme GROUND BAND (Bud 2026-07-09: consoles lost the marketing footer's themed
   ground when shells dropped the footer — the rail-foot IS the console's ground).
   Same pairs as the .kz-footer bands (WCAG-passed there); bleeds through the rail's
   own padding to its edges. The games launcher stands just ABOVE this band. */
[data-theme="plumber"] .kz-rail-foot,[data-theme="dk"] .kz-rail-foot{
  margin-left:-12px;margin-right:-12px;margin-bottom:calc(-1*(env(safe-area-inset-bottom,0px) + 14px));
  padding:12px 12px calc(env(safe-area-inset-bottom,0px) + 14px)}
[data-theme="plumber"] .kz-rail-foot{background:#3cb043;border-top:4px solid #2a7d2f}
[data-theme="dk"] .kz-rail-foot{background:#5a3a1a;border-top:4px solid #3a2410}
[data-theme="plumber"] .kz-rail-foot a{color:#0c2a08}
[data-theme="dk"] .kz-rail-foot a{color:#f3e9d8}
/* theme picker LIVES IN THE RAIL on consoles (Bud 2026-07-09: it was footer-buried;
   consoles no longer inject a footer at all). Menu opens UPWARD from the rail foot. */
.kz-rail-theme{margin-top:6px;order:99} /* DS28 (Bud 2026-07-10): theme = its own element BELOW the pills — rail-foot is flex-column, order pins it last regardless of injection timing */
.kz-rail-theme .kz-themewrap{margin-left:0;display:block}
.kz-rail-theme .kz-themebtn{width:100%;justify-content:flex-start;font-size:var(--kz-fs-hint)}
.kz-rail-theme .kz-themebtn .kz-tname{margin-left:auto}
.kz-rail-theme .kz-thememenu{top:auto;bottom:calc(100% + 8px);left:0;right:auto;transform-origin:bottom left;animation:kz-menu-up var(--kz-dur) var(--kz-ease-decel)}
/* kz-acct — the ACCOUNT PILL (FT21 round 3, promoted kit-level for continuity):
   one pill per identity/provider in any console rail-foot. States: .on = live
   (soft ok-glow, DS18) · .warn = remembered, needs a tap · .off = not connected.
   Body: dot + who (name over sub) + ONE action (button or a.kz-acct-act). */
.kz-acct{display:flex;align-items:center;gap:9px;border:1px solid var(--line);background:var(--panel);
  border-radius:10px;padding:8px 10px;margin:0 0 6px;min-width:0}
.kz-acct .kz-acct-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;background:var(--bad)}
.kz-acct.on{border-color:color-mix(in srgb,var(--ok) 45%,var(--line));box-shadow:0 0 14px -7px var(--ok)}
.kz-acct.on .kz-acct-dot{background:var(--ok);box-shadow:0 0 8px -2px var(--ok)}
.kz-acct.warn{border-color:color-mix(in srgb,var(--warn) 40%,var(--line))}
.kz-acct.warn .kz-acct-dot{background:var(--warn)}
.kz-acct-who{min-width:0;flex:1}
.kz-acct-who b{display:block;font-size:var(--kz-fs-hint);color:var(--ink);font-weight:var(--kz-fw-semibold)}
.kz-acct-who span{display:block;font-size:11px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kz-acct button,.kz-acct a.kz-acct-act{background:none;border:0;padding:2px 0;font:inherit;font-size:11px;
  color:var(--mut);cursor:pointer;flex:0 0 auto;text-decoration:none}
.kz-acct button:hover,.kz-acct a.kz-acct-act:hover{color:var(--ink)}
.kz-acct .kz-acct-connect{color:var(--stripe);font-weight:var(--kz-fw-semibold)}
@media(prefers-reduced-motion:reduce){.kz-acct.on{box-shadow:none}}
/* kz-acct POPOVER (L35, Bud 2026-07-09 — the Resend bottom-left account row):
   the rail-foot identity pill becomes ONE button; the popover (opens UPWARD)
   carries profile head + Appearance (the theme picker moves inside) + Sign out.
   Portal (true login) adopts first; tracker's per-provider pills stay pills. */
.kz-acct-popwrap{position:relative}
button.kz-acct{width:100%;cursor:pointer;text-align:left;font:inherit;box-sizing:border-box}
button.kz-acct:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}
.kz-acct .kz-acct-caret{margin-left:auto;color:var(--dim);font-size:var(--kz-fs-micro);flex:0 0 auto} /* DS48(4)-OK: DECORATIVE — affordance caret on a button whose label is already --ink; carries no meaning alone */
.kz-acct-pop{position:absolute;bottom:calc(100% + 8px);left:0;right:0;min-width:220px;z-index:60;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--kz-radius);
  box-shadow:var(--kz-shadow-pop);padding:8px;transform-origin:bottom left;
  animation:kz-menu-up var(--kz-dur) var(--kz-ease-decel)}
.kz-acct-pop[hidden]{display:none}
.kz-acct-pop-head{padding:8px 10px 10px;border-bottom:1px solid var(--line);margin:0 0 6px}
.kz-acct-pop-head b{display:block;font-size:var(--kz-fs-sm);font-weight:var(--kz-fw-semibold);color:var(--ink)}
.kz-acct-pop-head span{display:block;font-size:var(--kz-fs-micro);color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kz-acct-pop-item{display:flex;align-items:center;gap:10px;width:100%;min-height:34px;padding:0 10px;
  border:0;background:none;border-radius:var(--kz-radius-sm);color:var(--mut);font:inherit;font-size:var(--kz-fs-sm);
  text-decoration:none;cursor:pointer;box-sizing:border-box}
.kz-acct-pop-item:hover{background:var(--panel2);color:var(--ink)}
.kz-acct-pop-label{font-size:10px;font-weight:var(--kz-fw-semibold);letter-spacing:.09em;text-transform:uppercase;color:var(--mut);padding:8px 10px 2px} /* DS48(4): was --dim */
.kz-acct-pop .kz-rail-theme{margin:0;padding:0 4px}
.kz-acct-pop .kz-thememenu{bottom:calc(100% + 4px)}
.kz-rail-foot a{min-height:32px;font-size:var(--kz-fs-hint)}
.kz-canvas{padding:28px clamp(24px,4vw,48px) 64px;max-width:1100px;box-sizing:border-box;min-width:0}
/* ---------------------------------------------------------------------
   kz-aside — the RIGHT CONTEXT PANEL (L38, Bud 2026-07-09: "rail left,
   relevant shit right"). A kit primitive with STRICT rules — clutter is
   the named failure mode:
   1. GLANCEABLE CONTEXT ONLY — read-only summaries, upcoming items, meta.
      Never primary actions, never forms, never anything that exists ONLY here.
   2. Max ~3 modules per view; ONE aside per view; lives inside .kz-canvas.
   3. Desktop-only enhancement (>=1024px). Narrow: it drops UNDER the content —
      the page must be complete without it.
   Markup: .kz-withaside > (.kz-main + aside.kz-aside > .kz-aside-mod*)      */
.kz-withaside{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:var(--kz-space-6);align-items:start}
.kz-withaside > .kz-main{min-width:0}
.kz-aside{position:sticky;top:24px;display:flex;flex-direction:column;gap:var(--kz-space-4);min-width:0}
.kz-aside-mod{border:1px solid var(--line);background:var(--panel);border-radius:var(--kz-radius);padding:14px 16px}
.kz-aside-mod h3{margin:0 0 10px;font-size:var(--kz-fs-label);font-weight:var(--kz-fw-semibold);letter-spacing:var(--kz-tr-label);text-transform:uppercase;color:var(--mut)} /* DS48(4): was --dim — a module heading is text */
.kz-aside-mod p{margin:0;font-size:var(--kz-fs-sm);color:var(--mut);line-height:1.55}
.kz-aside-mod .kz-row{padding:6px 0}
@media(max-width:1023px){
  .kz-withaside{display:block}
  .kz-aside{position:static;margin-top:var(--kz-space-6);border-top:1px solid var(--line);padding-top:var(--kz-space-5)}
}
.kz-canvas-head{display:flex;align-items:center;gap:var(--kz-space-4);margin:0 0 var(--kz-space-5);flex-wrap:wrap}
.kz-canvas-head h1{margin:0;font-size:var(--kz-fs-h1);font-weight:var(--kz-fw-semibold);letter-spacing:-.02em;color:var(--ink)}
.kz-utility{display:flex;align-items:center;gap:var(--kz-space-2);margin-left:auto}
.kz-utility a,.kz-utility button{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:0 12px;
  border-radius:var(--kz-radius);background:transparent;border:0;color:var(--mut);font:inherit;font-size:var(--kz-fs-sm);
  text-decoration:none;cursor:pointer;transition:background var(--kz-dur-fast) var(--kz-ease-standard),color var(--kz-dur-fast) var(--kz-ease-standard)}
.kz-utility a:hover,.kz-utility button:hover{background:var(--panel2);color:var(--ink)}
/* mobile: rail off-canvas, toggled by a page-provided burger (same sheet feel as the kit drawer) */
.kz-shell-bar{display:none}
@media(max-width:767px){
  .kz-shell{display:block}
  .kz-shell-bar{display:flex;align-items:center;gap:12px;min-height:var(--kz-bar-h);padding:8px 20px;
    border-bottom:1px solid var(--line);position:sticky;top:0;z-index:50;
    background:color-mix(in srgb,var(--bg) 86%,transparent);
    -webkit-backdrop-filter:saturate(1.4) blur(14px);backdrop-filter:saturate(1.4) blur(14px)}
  .kz-shell-bar .kz-appmenu-btn{display:inline-flex;margin-left:auto}
  .kz-rail{position:fixed;left:0;right:0;top:var(--kz-bar-h);height:auto;max-height:calc(100dvh - var(--kz-bar-h));
    z-index:48;border-right:0;border-bottom:1px solid var(--line);background:var(--panel);box-shadow:var(--kz-shadow-pop);
    transform:translateY(-16px);opacity:0;visibility:hidden;pointer-events:none;
    transition:transform var(--kz-dur-lg) var(--kz-ease-decel),opacity var(--kz-dur) var(--kz-ease-decel),visibility var(--kz-dur-lg)}
  .kz-shell[data-open="1"] .kz-rail{transform:none;opacity:1;visibility:visible;pointer-events:auto}
  .kz-rail nav a{min-height:48px;font-size:var(--kz-fs-lg)}
  .kz-rail .kz-rail-sub a{min-height:44px;font-size:var(--kz-fs-sm)} /* DS26 sub-rows: 44px tap targets in the sheet */
  .kz-rail-brand{display:none} /* brand lives in the mobile bar */
  .kz-canvas{padding:24px 20px 48px}
}

/* =====================================================================
   reduced motion (DESIGN_SYSTEM.md 6.2 / DP-9 / WCAG 2.3.3)
   ===================================================================== */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
}

/* =====================================================================
   v6 — DS29 FOLD / SORT / HINT + kz-i-dog (L60, 2026-07-11)
   All ADDITIVE. The collapse grammar below is the portal cockpit's kl-exp,
   promoted verbatim (one grammar estate-wide — the DS2-drift lesson).
   ===================================================================== */

/* ---------- kz-fold: THE collapse primitive ----------
   LAW (DS29): secondary/long content rests folded; PRIMARY glance content
   never hides behind a tap. Summary = caret + icon + title + honest count. */
details.kz-fold{border:1px solid var(--line);border-radius:16px;background:var(--panel);overflow:hidden}
details.kz-fold > summary{cursor:pointer;list-style:none;padding:var(--kz-space-4,18px) var(--kz-space-5,22px);
  display:flex;align-items:center;gap:12px}
details.kz-fold > summary::-webkit-details-marker{display:none}
details.kz-fold > summary::before{content:"\25B8";color:var(--mut);transition:transform .15s ease;flex:0 0 auto}
details.kz-fold[open] > summary::before{transform:rotate(90deg)}
details.kz-fold[open] > summary{border-bottom:1px solid var(--line)}
.kz-fold-body{padding:var(--kz-space-4,18px) var(--kz-space-5,22px)}
.kz-fold-title{font-weight:var(--kz-fw-semibold,600);color:var(--ink)}
.kz-fold-note{margin-left:auto;color:var(--mut);font-size:var(--kz-fs-sm,13px);text-align:right}

/* ---------- sortable kz-table headers (DS29) ----------
   Any table with more than one meaningful order gets them (joins the v4
   "filter what you display" law). Markup: <th data-kz-sort>Header</th> —
   kaizen.js wires the click; state rides aria-sort (screen readers included). */
.kz-table th[data-kz-sort]{cursor:pointer;user-select:none;-webkit-user-select:none;white-space:nowrap}
.kz-table th[data-kz-sort]::after{content:"\2195";margin-left:6px;color:var(--mut);opacity:.55;font-size:.85em}
.kz-table th[aria-sort="ascending"]::after{content:"\2191";color:var(--ink);opacity:1}
.kz-table th[aria-sort="descending"]::after{content:"\2193";color:var(--ink);opacity:1}

/* ---------- kz-hint-i: the tap-info micro-affordance (DS29) ----------
   LAW: a hint bubble is the LAST resort — if a control needs one, rename the
   control first (head-scratching is a naming bug before it is a help gap).
   Reads as INFO per the coaching standard: muted i, panel2 bubble, never a
   warn color, never blocking, dismisses on outside tap / Esc.
   Markup: <button class="kz-hint-i" type="button" data-kz-hint="one plain line"
   aria-label="What is this?"></button> — the bubble is built on first tap. */
.kz-hint-i{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:50%;border:1px solid var(--mut);background:transparent; /* DS49: transparent fill, so the ring IS the affordance — was --line (1.20-1.68 on the panels, below the 3:1 non-text bar). */
  color:var(--mut);cursor:pointer;padding:0;vertical-align:middle;flex:0 0 auto}
.kz-hint-i::before{content:"i";font:italic 600 11px/1 Georgia,'Times New Roman',serif}
.kz-hint-i:hover,.kz-hint-i[aria-expanded="true"]{color:var(--ink);border-color:var(--ink)} /* DS49: rest is now --mut, so hover moves UP to --ink or the state change would be a no-op on the ring. */
.kz-hint-pop{position:absolute;z-index:40;top:calc(100% + 7px);left:50%;transform:translateX(-50%);
  width:max-content;min-width:140px;max-width:240px;background:var(--panel2);border:1px solid var(--line);
  border-radius:10px;padding:8px 12px;color:var(--ink);font:400 var(--kz-fs-sm,13px)/1.45 inherit;
  font-family:inherit;font-style:normal;text-align:left;box-shadow:0 6px 20px rgba(0,0,0,.18);cursor:default}

/* =====================================================================
   v7 — DS34-2 kz-live (L68, 2026-07-13): no-flicker live surfaces
   A heartbeat tick patches IN PLACE (kzLiveMorph in kaizen.js); the only
   visible change a tick may make is genuinely-new data. New keyed rows
   enter quietly; reduced-motion (above) kills the entrance like all motion.
   ===================================================================== */
.kz-live-new{animation:kz-live-in .32s ease}
@keyframes kz-live-in{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}

/* =====================================================================
   v8 — DS47 CLIENT PAGES (2026-08-13, model: claude-opus-5[1m])
   ONE kit, two client-facing modes, declared with ONE attribute on <body>:

     data-kaizen-client="branded"    WHITE LABEL. Zero Kaizen chrome (identical
       injection contract to data-kaizen-chrome="none", DS46) AND a FIXED
       neutral-light palette — see the token block below. The integrator's mark
       is the only brand on the page; the shared kaizen-theme cookie, our
       personality themes included, can never repaint it.
     data-kaizen-client="unbranded"  THE STOREFRONT. Full Kaizen skin (lockup +
       footer, exactly as data-kaizen-app) but NO theme picker, no easter-egg
       launcher and no shared theme cookie: fixed default Nightman. DS46's
       "no themes on a client-facing page" stands; DS47 only restored the skin.

   data-kaizen-client OVERRIDES data-kaizen-chrome and data-kaizen-nav
   (precedence lives in kaizen.js — see injectChrome()).
   ===================================================================== */

/* ---------- tokens: white-label neutral light (branded) ----------
   FIXED, cookie-proof, and de-hued on purpose. Derived from the daylight ramp
   (already WCAG-measured) but every surface is a TRUE NEUTRAL grey and the
   accent is charcoal, NOT Kaizen blue: an integrator's mark is drawn for white
   and nothing on their client's page may read as our brand colour. The only
   colour on the page should be the client's own logo.
   MEASURED (WCAG 2.1 contrast, computed — test/kit.test.js recomputes these
   from this very block and fails the build if any pair drops below the bar):
     --ink  on --bg 15.49 · --panel 16.91 · --panel2 14.54
     --mut  on --bg  6.08 · --panel  6.64 · --panel2  5.71   (bar 4.5)
     --on-accent on --stripe 13.13 · accent vs --bg 12.03 / --panel 13.13
     --ok   on --panel 5.81 · --bg 5.32 · --panel2 5.00
     --warn on --panel 5.92 · --bg 5.43 · --panel2 5.09
     --bad  on --panel 5.68 · --bg 5.20 · --panel2 4.88
     --trace on --panel 7.12 · --bg 6.52 · --panel2 6.12     (all bar 4.5)
     --dim 3.18 on --panel and --line 1.39 on --panel are FAINT/DIVIDER roles,
     never text — no AA text bar applies (same contract as every theme). */
body[data-kaizen-client="branded"]{
  --bg:#f4f5f6; --panel:#ffffff; --panel2:#eceef0; --line:#d8dbdf;
  --ink:#1a1d21; --mut:#585d64; --dim:#8b9198;
  --stripe:#2b3138; --on-accent:#ffffff;
  --ok:#0e7449; --warn:#8a5a06; --bad:#c22a3f; --trace:#455a70;
  --kz-scrim:rgba(20,22,26,.34);
  --kz-shadow-1:0 1px 2px rgba(20,22,26,.10);
  --kz-shadow-2:0 8px 28px -10px rgba(20,22,26,.22);
  --kz-shadow-pop:0 24px 56px -16px rgba(20,22,26,.30);
  /* painted here as well as in .kz so the page is already neutral-light on the
     FIRST paint — kaizen.js is deferred, and a dark flash on a client's page is
     the same leak as a lockup would be. Body background propagates to the
     canvas, so no html-level rule is needed. */
  background:var(--bg);color:var(--ink);
}

/* ---------- kz-clientpage: the client-delivery page grammar ----------
   Absorbed from kaizen-share's pageShell + gallery CSS so a client page assembles
   from the kit instead of shipping its own <style> block (the kit is the single
   source — a page hand-rolling its own layout is how DS2 drift started). Only the
   pieces the kit did not already carry live here: .kz-field / .kz-input / .kz-select
   / .kz-list / .kz-card / .kz-btn / .kz-chip were already kit-standard and are NOT
   duplicated. Tokens only — no colour literal outside the block above. */
.kz-wrap{max-width:var(--kz-col);margin:0 auto;padding:var(--kz-space-6) var(--kz-space-4)}
.kz-center{text-align:center}
.kz-big{font-size:var(--kz-fs-display);font-weight:var(--kz-fw-black);letter-spacing:var(--kz-tr-tight);
  line-height:var(--kz-lh-tight);margin:var(--kz-space-4) 0 var(--kz-space-2)}
/* a URL is a styled control or it is not on the page (DS46 §3): field + copy/open
   button on one row, never a bare pasted link. */
.kz-linkout{display:flex;gap:var(--kz-space-2);align-items:stretch;margin-top:var(--kz-space-3)}
.kz-linkout .kz-input,.kz-linkout input{flex:1 1 auto;min-width:0}

/* ---------- L161: the button tiers must READ as buttons on client-light ----------
   .kz-btn-ghost ships a TRANSPARENT border, which on our dark themes still reads as a
   control because the surrounding chrome frames it. On the neutral-light client palette
   there is no chrome — a ghost button next to body copy is indistinguishable from plain
   text, and a client cannot find the control. Fix is a VISIBLE BOUNDARY, not a new
   variant: same class, same tier order, same grammar everywhere else (this block is
   scoped to the branded client palette only).
   WCAG 1.4.11 non-text contrast bar = 3:1 for a control's visual boundary. MEASURED
   (recomputed in test/kit.test.js from these very declarations):
     ghost border     color-mix(--mut 70%, --line) = #7e8389 -> --panel 3.82 · --bg 3.50 · --panel2 3.29
     secondary border var(--mut)                   = #585d64 -> --panel 6.64 · --bg 6.08 · --panel2 5.71
   Fill stays as-is, so the tier order is unchanged: primary charcoal fill > secondary
   panel2 fill > ghost transparent, and the ghost edge stays LIGHTER than secondary's.
   DS49 UPDATE: secondary used to be deepened HERE, client-light only, because its stock
   --dim border measured 3.18/2.91/2.73 on this palette. That was the same defect in every
   palette (--dim is 2.35-2.98 against --panel2 in six of eight), so the repoint moved to
   the base rule and is now KIT-WIDE; the branded-only secondary override was deleted as
   redundant rather than left to shadow a rule identical to itself. Ghost stays scoped —
   a transparent border reads fine inside our own chrome and only fails on the unchromed
   client page, so that one really is palette-specific. */
body[data-kaizen-client="branded"] .kz-btn-ghost{border-color:color-mix(in srgb,var(--mut) 70%,var(--line))}
body[data-kaizen-client="branded"] .kz-btn-ghost:hover{border-color:var(--mut)}
body[data-kaizen-client="branded"] .kz-btn-secondary:hover{border-color:var(--ink)}

/* ONE brand slot per page (DS46), left-aligned at the top of the page header.
   EXPLICIT HEIGHT is the load-bearing part: an integrator's master is usually an
   SVG carrying a viewBox and NO width/height, which has no intrinsic size — left
   to itself it renders at the 300x150 replaced-element default or collapses to
   nothing. height + width:auto sizes it from the viewBox ratio; max-width caps a
   very wide wordmark; object-fit keeps a raster mark undistorted. 44px = the tap
   ramp, so a linked mark is also a legal target. */
.kz-clientbrand{display:block;margin:0 0 var(--kz-space-4)}
.kz-clientbrand a{display:inline-block;text-decoration:none;color:inherit}
.kz-clientbrand a:focus-visible{outline:2px solid var(--stripe);outline-offset:3px}
.kz-clientbrand img,.kz-clientbrand svg{display:block;height:44px;max-height:44px;max-width:220px;
  width:auto;object-fit:contain}
.kz-clientbrand-name{font-size:var(--kz-fs-h3);font-weight:var(--kz-fw-bold);color:var(--ink)}
.kz-clienthead{margin:0 0 var(--kz-space-5)}
.kz-clienthead .kz-big{margin-top:0}

/* gallery: the review grid. Cards are kz-card + kz-tile (one card grammar). */
.kz-gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:var(--kz-space-4);margin-top:var(--kz-space-5)}
.kz-tile{padding:0;overflow:hidden;margin:0;display:flex;flex-direction:column}
/* containment: a grid/flex item may never force the tile's content column wider
   than the tile — min-width:0 kills the min-content blowout that clipped buttons
   and titles below 351px. */
.kz-tile > *{min-width:0}
@supports (grid-template-rows:subgrid){
  .kz-tile{display:grid;grid-row:span 4;grid-template-rows:subgrid;row-gap:0}
  .kz-gallery{grid-auto-rows:auto}
}
.kz-tile-shot{display:block;width:100%;padding:0;border:0;background:var(--panel2);
  border-bottom:1px solid var(--line);cursor:zoom-in;font:inherit}
.kz-tile-shot img{display:block;width:100%;aspect-ratio:16/10;object-fit:contain}
.kz-tile-shot:focus-visible{outline:2px solid var(--stripe);outline-offset:2px}
.kz-tile-cap{padding:var(--kz-space-3) var(--kz-space-4) 0}
.kz-tile-cap h3{margin:0;font-size:var(--kz-fs-body);font-weight:var(--kz-fw-bold);overflow-wrap:anywhere}
.kz-tile-cap p{margin:var(--kz-space-2) 0 0;color:var(--mut);font-size:var(--kz-fs-sm)}
.kz-tile-foot{padding:var(--kz-space-3) var(--kz-space-4) var(--kz-space-4);align-self:end;
  width:100%;box-sizing:border-box}
.kz-tile-actions{display:flex;gap:var(--kz-space-2);flex-wrap:wrap}
.kz-tile-actions .kz-btn{flex:1 1 auto;min-height:var(--kz-tap)}
@media(max-width:480px){ .kz-gallery{grid-template-columns:1fr} }
