/* ============================================================
   kunlun · world.css — token layer, phase 0 starter
   ------------------------------------------------------------
   Copied verbatim from the design system's tokens/ on 2026-08-19:
     D:/DESIGN/KUNLUN Design System/tokens/
   Files, in the DS's own import order: fonts.css, colors.css, typography.css, spacing.css, effects.css, motion.css
   Source of truth is still D:/DESIGN — re-read SKILL.md / readme /
   tokens there before building. Resets, base layouts and component
   CSS were deliberately NOT copied: layout is this world's own job.

   Google Fonts @import lines were removed (rule: fonts are self-hosted,
   see docs/04-world-contract.md §fonts). The families each token file
   expected are listed right where the import used to be.
   ============================================================ */

/* ---------- tokens/fonts.css ---------- */
/* Webfonts loaded via Google Fonts CDN. We re-declare the @font-face rules
 * locally so the compiler can index them and so consumers don't need a
 * second <link>. Pull subsets generously — UI is heavy on numerals,
 * brackets, and CJK labels.
 */

/* [phase-0] Google Fonts import removed. Self-host these families
   (see docs/04-world-contract.md §fonts): JetBrains Mono, Space Mono, VT323, Orbitron, Noto Sans SC */

/* ---------- tokens/colors.css ---------- */
/* COLORS — cyan/blue dominant. Amber + red are sparing accents.
 * Base scale uses dark void backgrounds, cyan as primary "signal",
 * full neutral grayscale for type, plus warm warning hues.
 */

:root {
  /* ─── Base background scale (void → elevated) ─── */
  --bg-void:        #04060a;   /* deepest layer, between panels */
  --bg-base:        #06080d;   /* default page background */
  --bg-surface:     #0a0f17;   /* primary surface */
  --bg-panel:       #0d1320;   /* panel / card background */
  --bg-elevated:    #131a2a;   /* modals, dropdowns */
  --bg-inset:       #02040a;   /* recessed, e.g. input wells */

  /* ─── Neutral / grayscale (text + chrome) ─── */
  --neutral-50:     #f5f9ff;
  --neutral-100:    #e3eaf3;
  --neutral-200:    #c2cdda;
  --neutral-300:    #95a3b4;
  --neutral-400:    #6c7a8c;
  --neutral-500:    #4d5a6e;
  --neutral-600:    #364154;
  --neutral-700:    #232c3d;
  --neutral-800:    #161e2c;
  --neutral-900:    #0a0f17;

  /* ─── Primary — Cyan (signal / glow) ─── */
  --cyan-100:       #d5f6ff;
  --cyan-200:       #a7ecff;
  --cyan-300:       #5fd9ff;
  --cyan-400:       #1fc8ff;
  --cyan-500:       #00b8ff;   /* PRIMARY */
  --cyan-600:       #0098d4;
  --cyan-700:       #0075a8;
  --cyan-800:       #00547a;
  --cyan-900:       #00374e;

  /* ─── Secondary — Steel Blue (structure / depth) ─── */
  --blue-100:       #dbe6ff;
  --blue-200:       #a7c1ff;
  --blue-300:       #6c95ff;
  --blue-400:       #3d72f0;
  --blue-500:       #2a5fd6;
  --blue-600:       #1f47a8;
  --blue-700:       #18377f;
  --blue-800:       #112759;
  --blue-900:       #0a1738;

  /* ─── Accent — Amber (highlight, used sparingly) ─── */
  --amber-100:      #fff1c2;
  --amber-300:      #ffd166;
  --amber-500:      #ffb000;   /* HIGHLIGHT */
  --amber-700:      #b07300;

  /* ─── Danger — Red (warning state, sparing) ─── */
  --red-100:        #ffd9d9;
  --red-300:        #ff7878;
  --red-500:        #ff3a3a;   /* DANGER */
  --red-700:        #b22020;

  /* ─── Success — Cyan-green ─── */
  --green-300:      #5cffb8;
  --green-500:      #00e891;
  --green-700:      #008f55;

  /* ─── Semantic aliases (use these in components) ─── */
  --bg:                  var(--bg-base);
  --bg-card:             var(--bg-panel);
  --bg-input:            var(--bg-inset);
  --bg-hover:            #131c2e;
  --bg-active:           #1a2540;

  --text-primary:        var(--neutral-100);
  --text-secondary:      var(--neutral-300);
  --text-tertiary:       var(--neutral-400);
  --text-disabled:       var(--neutral-500);
  --text-inverse:        var(--bg-void);
  --text-link:           var(--cyan-400);
  --text-signal:         var(--cyan-300);
  --text-warn:           var(--amber-500);
  --text-danger:         var(--red-500);
  --text-success:        var(--green-500);

  --border:              #1a2540;
  --border-strong:       #2a3a5c;
  --border-signal:       var(--cyan-700);
  --border-warn:         var(--amber-700);
  --border-danger:       var(--red-700);

  --accent:              var(--cyan-500);
  --accent-hover:        var(--cyan-400);
  --accent-press:        var(--cyan-600);
  --accent-dim:          var(--cyan-700);
  --accent-glow:         rgba(0, 184, 255, 0.45);
  --accent-glow-soft:    rgba(0, 184, 255, 0.18);

  --warn:                var(--amber-500);
  --warn-glow:           rgba(255, 176, 0, 0.4);

  --danger:              var(--red-500);
  --danger-glow:         rgba(255, 58, 58, 0.4);

  --success:             var(--green-500);
  --success-glow:        rgba(0, 232, 145, 0.4);

  /* HUD ornament ink — used for corner marks, coordinates, ASCII frames */
  --hud-ink:             var(--cyan-700);
  --hud-ink-bright:      var(--cyan-400);

  /* Grid / scanline overlay color */
  --scanline:            rgba(0, 184, 255, 0.04);
  --grid-line:           rgba(120, 180, 255, 0.06);
  --grain:               rgba(255, 255, 255, 0.025);
}

/* ---------- tokens/typography.css ---------- */
/* TYPOGRAPHY — all monospace, with display & CRT variants.
 * Body / UI uses JetBrains Mono. Display / headings use Orbitron for
 * its geometric, mil-spec character. VT323 is reserved for "screen
 * readout" moments (terminal, status displays). CJK falls through to
 * Noto Sans SC.
 */

:root {
  /* Font families */
  --font-mono:      "JetBrains Mono", "Noto Sans SC", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-display:   "Orbitron", "JetBrains Mono", "Noto Sans SC", monospace;
  --font-crt:       "VT323", "JetBrains Mono", "Noto Sans SC", monospace;
  --font-body:      "JetBrains Mono", "Noto Sans SC", ui-monospace, monospace;
  --font-code:      "JetBrains Mono", "Space Mono", ui-monospace, monospace;

  /* Type scale — modular, terminal-friendly */
  --text-3xs:       10px;     /* coordinate labels, HUD micro */
  --text-2xs:       11px;     /* tags, status pills */
  --text-xs:        12px;     /* captions, table cells */
  --text-sm:        13px;     /* secondary UI */
  --text-base:      14px;     /* body */
  --text-md:        15px;     /* body emphasized */
  --text-lg:        18px;     /* card title */
  --text-xl:        22px;     /* section title */
  --text-2xl:       28px;     /* page title */
  --text-3xl:       36px;     /* feature title */
  --text-4xl:       48px;     /* hero */
  --text-5xl:       64px;     /* hero display */
  --text-6xl:       96px;     /* big numerals / countdown */

  /* Line heights — tight for terminal feel */
  --leading-tight:  1.1;
  --leading-snug:   1.25;
  --leading-normal: 1.45;
  --leading-loose:  1.7;

  /* Letter-spacing — wide for display, monospaced for body */
  --tracking-tight:  -0.01em;
  --tracking-normal: 0;
  --tracking-wide:   0.06em;
  --tracking-wider:  0.12em;
  --tracking-widest: 0.22em;  /* for HUD labels: [ N O D E - 0 7 ] */

  /* Weights */
  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;
  --weight-display:  900;

  /* Semantic type roles */
  --type-display:   var(--weight-display) var(--text-4xl)/var(--leading-tight) var(--font-display);
  --type-h1:        var(--weight-bold) var(--text-3xl)/var(--leading-tight) var(--font-display);
  --type-h2:        var(--weight-bold) var(--text-2xl)/var(--leading-snug) var(--font-display);
  --type-h3:        var(--weight-semibold) var(--text-xl)/var(--leading-snug) var(--font-mono);
  --type-h4:        var(--weight-semibold) var(--text-lg)/var(--leading-snug) var(--font-mono);
  --type-body:      var(--weight-regular) var(--text-base)/var(--leading-normal) var(--font-body);
  --type-caption:   var(--weight-regular) var(--text-xs)/var(--leading-normal) var(--font-mono);
  --type-label:     var(--weight-semibold) var(--text-2xs)/var(--leading-tight) var(--font-mono);
  --type-code:      var(--weight-regular) var(--text-sm)/var(--leading-normal) var(--font-code);
  --type-crt:       var(--weight-regular) var(--text-xl)/var(--leading-normal) var(--font-crt);
}

/* ---------- tokens/spacing.css ---------- */
/* SPACING — 4px base grid. Terminal layouts are dense; bias to smaller steps. */

:root {
  --space-0:    0;
  --space-px:   1px;
  --space-0_5:  2px;
  --space-1:    4px;
  --space-1_5:  6px;
  --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;
  --space-32:   128px;

  /* Component / layout */
  --pad-tight:    var(--space-2);
  --pad-snug:     var(--space-3);
  --pad-default:  var(--space-4);
  --pad-comfy:    var(--space-6);

  --gap-tight:    var(--space-2);
  --gap-default:  var(--space-3);
  --gap-comfy:    var(--space-6);

  /* Container widths */
  --w-narrow:     720px;
  --w-content:    960px;
  --w-wide:       1200px;
  --w-max:        1440px;

  /* Borders */
  --border-w:        1px;
  --border-w-bold:   2px;
  --border-w-thick:  3px;

  /* Corners — all chamfered. Radius is reserved for tiny inner pieces only. */
  --radius-none:    0;
  --radius-xs:      2px;
  --radius-sm:      3px;
  --radius-circle:  9999px;   /* avatars only */

  /* Chamfer cut size, used in clip-path expressions */
  --chamfer-xs:   6px;
  --chamfer-sm:   8px;
  --chamfer-md:   12px;
  --chamfer-lg:   18px;
  --chamfer-xl:   24px;
}

/* ---------- tokens/effects.css ---------- */
/* EFFECTS — shadows, glows, HUD chamfers, grain, scanlines.
 * Everything sci-fi-tech lives here as reusable utilities.
 */

:root {
  /* ─── Shadows (subtle in dark UI; rely on glow for emphasis) ─── */
  --shadow-sm:     0 1px 0 rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.3);
  --shadow-md:     0 4px 12px rgba(0, 0, 0, 0.6), 0 1px 0 rgba(0, 0, 0, 0.4);
  --shadow-lg:     0 16px 40px rgba(0, 0, 0, 0.75), 0 1px 0 rgba(0, 0, 0, 0.5);

  /* Inset for input wells */
  --shadow-inset:  inset 0 0 0 1px var(--border), inset 0 2px 6px rgba(0, 0, 0, 0.6);

  /* ─── Neon glows (the signature look) ─── */
  --glow-cyan-sm:   0 0 8px var(--accent-glow-soft);
  --glow-cyan-md:   0 0 16px var(--accent-glow), 0 0 2px var(--cyan-400);
  --glow-cyan-lg:   0 0 32px var(--accent-glow), 0 0 8px var(--cyan-300);

  --glow-amber-md:  0 0 16px var(--warn-glow), 0 0 2px var(--amber-300);
  --glow-red-md:    0 0 16px var(--danger-glow), 0 0 2px var(--red-300);
  --glow-green-md:  0 0 16px var(--success-glow), 0 0 2px var(--green-300);

  /* Text glows */
  --text-glow-cyan:  0 0 6px rgba(0, 184, 255, 0.7);
  --text-glow-amber: 0 0 6px rgba(255, 176, 0, 0.7);
  --text-glow-red:   0 0 6px rgba(255, 58, 58, 0.7);

  /* ─── Chamfer clip-paths (top-left + bottom-right cut) ─── */
  --clip-chamfer-sm: polygon(
      6px 0,
      100% 0,
      100% calc(100% - 6px),
      calc(100% - 6px) 100%,
      0 100%,
      0 6px
  ); /* @kind other */
  --clip-chamfer-md: polygon(
      12px 0,
      100% 0,
      100% calc(100% - 12px),
      calc(100% - 12px) 100%,
      0 100%,
      0 12px
  ); /* @kind other */
  --clip-chamfer-lg: polygon(
      18px 0,
      100% 0,
      100% calc(100% - 18px),
      calc(100% - 18px) 100%,
      0 100%,
      0 18px
  ); /* @kind other */

  /* All-four-corners chamfer (octagonal) */
  --clip-chamfer-all-md: polygon(
      10px 0, calc(100% - 10px) 0,
      100% 10px, 100% calc(100% - 10px),
      calc(100% - 10px) 100%, 10px 100%,
      0 calc(100% - 10px), 0 10px
  ); /* @kind other */

  /* ─── Backdrop / textures (use as ::before / ::after overlays) ─── */

  /* Scanlines — horizontal CRT lines */
  --bg-scanlines: repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 2px,
      var(--scanline) 2px,
      var(--scanline) 3px
  );

  /* Grid */
  --bg-grid: linear-gradient(var(--grid-line) 1px, transparent 1px) 0 0/40px 40px,
             linear-gradient(90deg, var(--grid-line) 1px, transparent 1px) 0 0/40px 40px;

  --bg-grid-fine: linear-gradient(var(--grid-line) 1px, transparent 1px) 0 0/12px 12px,
                  linear-gradient(90deg, var(--grid-line) 1px, transparent 1px) 0 0/12px 12px;

  /* Grain (use as inline SVG noise overlay) */
  --bg-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.15 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");

  /* Vignette */
  --bg-vignette: radial-gradient(ellipse at center, transparent 50%, rgba(0, 0, 0, 0.6) 100%);

  /* Glow gradient halo (for hero blocks) */
  --bg-halo-cyan: radial-gradient(circle at 50% 0%, var(--accent-glow-soft), transparent 60%);
}

/* ─── Reusable effect utility classes ─── */

.kl-grain::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--bg-grain);
  opacity: 0.5;
  mix-blend-mode: overlay;
  z-index: 1;
}

.kl-scanlines::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--bg-scanlines);
  z-index: 2;
}

.kl-chamfer {
  clip-path: var(--clip-chamfer-md);
}

.kl-chamfer-sm { clip-path: var(--clip-chamfer-sm); }
.kl-chamfer-lg { clip-path: var(--clip-chamfer-lg); }

/* Corner ticks — apply to a relatively-positioned container.
   Place four <span class="kl-corner kl-corner-tl"></span> etc inside. */
.kl-corner {
  position: absolute;
  width: 10px;
  height: 10px;
  border: 1px solid var(--hud-ink-bright);
  pointer-events: none;
}
.kl-corner-tl { top: 4px; left: 4px; border-right: none; border-bottom: none; }
.kl-corner-tr { top: 4px; right: 4px; border-left: none; border-bottom: none; }
.kl-corner-bl { bottom: 4px; left: 4px; border-right: none; border-top: none; }
.kl-corner-br { bottom: 4px; right: 4px; border-left: none; border-top: none; }

/* ---------- tokens/motion.css ---------- */
/* MOTION — terminal motion is sharp and stepped, not bouncy.
 * Keep durations short. Avoid ease-out-back / spring physics.
 */

:root {
  --dur-instant:  60ms;   /* @kind other */
  --dur-fast:     120ms;  /* @kind other */
  --dur-normal:   180ms;  /* @kind other */
  --dur-slow:     320ms;  /* @kind other */
  --dur-glacial:  640ms;  /* @kind other */

  /* Easing — favor linear and step-based curves. */
  --ease-linear:  linear;                            /* @kind other */
  --ease-out:     cubic-bezier(0.2, 0.7, 0.2, 1);    /* @kind other */
  --ease-in:      cubic-bezier(0.7, 0, 0.84, 0);     /* @kind other */
  --ease-step:    steps(8, end);     /* @kind other */
  --ease-step-2:  steps(2, end);     /* @kind other */

  --transition-base:  background var(--dur-fast) var(--ease-out),
                      color      var(--dur-fast) var(--ease-out),
                      border     var(--dur-fast) var(--ease-out),
                      box-shadow var(--dur-fast) var(--ease-out),
                      opacity    var(--dur-fast) var(--ease-out); /* @kind other */
}

/* ─── Keyframes (consumers reference these animations directly) ─── */

@keyframes kl-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

@keyframes kl-pulse-glow {
  0%, 100% { box-shadow: var(--glow-cyan-sm); }
  50%      { box-shadow: var(--glow-cyan-lg); }
}

@keyframes kl-flicker {
  0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
    opacity: 1;
    text-shadow: var(--text-glow-cyan);
  }
  20%, 24%, 55% {
    opacity: 0.6;
    text-shadow: none;
  }
}

@keyframes kl-scanline-sweep {
  0%   { transform: translateY(-100%); }
  100% { transform: translateY(100vh); }
}

@keyframes kl-typewriter {
  from { width: 0; }
  to   { width: 100%; }
}

@keyframes kl-caret {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

@keyframes kl-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@keyframes kl-spin {
  to { transform: rotate(360deg); }
}

@keyframes kl-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}


/* ============================================================
   phase-1 · KUNLUN world — layout & world-specific chrome
   ------------------------------------------------------------
   Overrides the phase-0 stage stub above. Follows the KUNLUN DS
   DON'Ts: no purple, no emoji, chamfered clip-paths (no rounded
   rectangles), all monospace with Orbitron only for display /
   HUD labels, UPPERCASE for labels + status, sharp / stepped
   motion (no bounces / springs), decorative looping only on
   status lights + HUD ornaments, imagery is incidental.
   ============================================================ */

/* --- self-hosted font subsets (see worlds/kunlun/fonts/) --- */
@font-face {
  font-family: "Orbitron";
  src: url("fonts/orbitron-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF;
}
@font-face {
  font-family: "Orbitron";
  src: url("fonts/orbitron-900.woff2") format("woff2");
  font-weight: 900; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jbmono-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+2010-25FF;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jbmono-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+2010-25FF;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jbmono-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+2010-25FF;
}
@font-face {
  font-family: "VT323";
  src: url("fonts/vt323-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+2010-25FF;
}
@font-face {
  font-family: "Noto Sans SC";
  src: url("fonts/notoscsc-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans SC";
  src: url("fonts/notoscsc-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* --- stage: void black + hard chrome --- */
html { background: var(--bg-void); color-scheme: dark; }
body { color: var(--text-primary); }

.yo-stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--bg-void);
  color: var(--text-primary);
  font-family: var(--font-body);
  contain: layout paint;
  isolation: isolate;
}

/* --- return key: HUD corner-bracket material carrying the moon + arrow svg
     (site-wide return glyph switched from 「夜」 to a waning moon paired with
     a back arrow; svg travels via currentColor so the HUD tint drives it) --- */
.yo-return {
  color: var(--cyan-300);
  background: rgba(4, 6, 10, 0.85);
  border: 1px solid var(--cyan-700);
  clip-path: var(--clip-chamfer-sm);
  top: 18px;
  left: 18px;
  width: 44px;
  height: 44px;
  z-index: 60;
  transition: box-shadow 120ms linear, background 120ms linear;
  overflow: visible;
}
/* shell will ship this rule on merge; declare an equivalent locally so this
   worktree renders the new return glyph at the intended size */
.yo-return svg {
  display: block;
  width: 26px;
  height: 24px;
  filter: drop-shadow(0 0 6px rgba(0, 184, 255, 0.55));
  transition: filter 140ms linear;
}
.yo-return::before, .yo-return::after {
  content: "";
  position: absolute;
  width: 8px;
  height: 8px;
  border: 1px solid var(--cyan-400);
  pointer-events: none;
}
.yo-return::before {
  top: 3px; left: 3px;
  border-right: none; border-bottom: none;
}
.yo-return::after {
  bottom: 3px; right: 3px;
  border-left: none; border-top: none;
}
.yo-return:hover, .yo-return:focus-visible {
  background: var(--bg-elevated);
  box-shadow: var(--glow-cyan-sm);
  outline: none;
}
.yo-return:hover svg,
.yo-return:focus-visible svg {
  filter: drop-shadow(0 0 10px rgba(31, 200, 255, 0.85));
}

/* --- HUD frame --- */
.kl-hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 40;
}
.kl-hud__corner {
  position: absolute;
  width: 22px;
  height: 22px;
  border: 1px solid var(--cyan-400);
}
.kl-hud__corner--tl { top: 14px; left: 74px;  border-right: none; border-bottom: none; }
.kl-hud__corner--tr { top: 14px; right: 14px; border-left: none;  border-bottom: none; }
.kl-hud__corner--bl { bottom: 14px; left: 14px;  border-right: none; border-top: none; }
.kl-hud__corner--br { bottom: 14px; right: 14px; border-left: none;  border-top: none; }

.kl-hud__label {
  position: absolute;
  top: 30px;
  left: 118px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: var(--tracking-widest);
  color: var(--cyan-200);
  text-shadow: var(--text-glow-cyan);
  text-transform: uppercase;
  display: flex; align-items: center; gap: 14px;
}
.kl-hud__label::before {
  content: "▸";
  color: var(--cyan-500);
  animation: kl-blink 1.6s steps(2, end) infinite;
}
.kl-hud__label__sub {
  font-family: var(--font-code), monospace;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.55em;
  color: var(--cyan-500);
  padding-left: 12px;
  border-left: 1px solid var(--cyan-800);
}

.kl-hud__coord {
  position: absolute;
  top: 30px;
  right: 46px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: var(--tracking-wider);
  color: var(--text-tertiary);
  text-transform: uppercase;
}
.kl-hud__scale {
  position: absolute;
  bottom: 148px;
  right: 46px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: var(--tracking-wider);
  color: var(--text-tertiary);
  text-transform: uppercase;
}

/* --- layered backdrops --- */
.kl-bg { position: absolute; inset: 0; pointer-events: none; }
.kl-map__grid {
  background-image: var(--bg-grid);
  mask-image: radial-gradient(ellipse at 50% 45%, black 20%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse at 50% 45%, black 20%, transparent 80%);
  opacity: 0.55;
  z-index: 4;
}
.kl-bg--scanlines {
  background-image: var(--bg-scanlines);
  opacity: 0.5;
  z-index: 6;
}
.kl-bg--grain {
  background-image: var(--bg-grain);
  opacity: 0.4;
  mix-blend-mode: overlay;
  z-index: 7;
}

/* --- pan/zoom viewport --- */
.kl-map {
  position: absolute;
  inset: 0;
  overflow: hidden;
  cursor: crosshair;
  z-index: 3;
  touch-action: none;
}
.kl-map__canvas {
  position: absolute;
  top: 0; left: 0;
  width: 2400px;
  /* Round 3.5 grew VIRTUAL_H 1200 → 1440 to fit the tailscale ring cluster
     at the bottom (tokyo + three device islands + operit + ring border).
     Kept in lockstep with world.js VIRTUAL_H. */
  height: 1440px;
  transform-origin: 0 0;
  will-change: transform;
  /* first frame = closed state: the circuit is hidden entirely until the
     boot ritual completes AND the log has fully left the DOM. Boot log
     types on a void so nothing shows through it. The map fades in when
     the stage opens (data-open="true") — which the boot ritual now only
     sets AFTER the log is removed, so there is no frame with both. */
  opacity: 0;
  transition: opacity 420ms linear;
}
.yo-stage[data-open="true"] .kl-map__canvas {
  opacity: 1;
}
.kl-map__canvas svg {
  display: block;
  width: 2400px;
  height: 1440px;
}

/* --- edges & signal --- */
.kl-edge {
  fill: none;
  stroke: var(--cyan-800);
  stroke-width: 1;
  opacity: 0.75;
}
.kl-edge--dense { stroke: var(--cyan-900); opacity: 0.55; }
.kl-edge--planned {
  stroke: var(--neutral-700);
  stroke-dasharray: 4 6;
  opacity: 0.4;
}
.kl-edge--active {
  stroke: var(--cyan-400);
  stroke-width: 1.5;
  opacity: 1;
  filter: drop-shadow(0 0 4px var(--accent-glow));
}
.kl-signal {
  fill: none;
  stroke: var(--cyan-200);
  stroke-width: 3;
  stroke-linecap: square;
  filter: drop-shadow(0 0 6px var(--accent-glow));
  opacity: 0;
}
.kl-signal--on { opacity: 1; }

/* --- nodes --- */
.kl-node { cursor: pointer; }
.kl-node rect,
.kl-node polygon {
  fill: var(--bg-panel);
  stroke: var(--border-strong);
  stroke-width: 1;
  transition: stroke 120ms linear, fill 120ms linear, filter 120ms linear;
}
.kl-node text.kl-label {
  fill: var(--neutral-200);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  pointer-events: none;
}
.kl-node text.kl-kind {
  fill: var(--cyan-400);
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  pointer-events: none;
}
.kl-node--big polygon {
  fill: var(--bg-elevated);
  stroke: var(--cyan-500);
  stroke-width: 1.5;
  filter: drop-shadow(0 0 8px var(--accent-glow-soft));
}
.kl-node--big text.kl-label {
  fill: var(--cyan-100);
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 18px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.kl-node--big text.kl-kind {
  fill: var(--cyan-300);
  font-size: 10px;
}
.kl-node--group rect {
  fill: transparent;
  stroke: var(--cyan-800);
  stroke-width: 1;
  stroke-dasharray: 3 5;
}
.kl-node--group text.kl-label {
  font-family: var(--font-mono);
  fill: var(--cyan-400);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
}
.kl-node--planned rect,
.kl-node--planned polygon {
  fill: transparent;
  stroke: var(--neutral-600);
  stroke-dasharray: 4 4;
}
.kl-node--planned text.kl-label { fill: var(--neutral-500); }
.kl-node--planned text.kl-kind { fill: var(--neutral-500); }
.kl-node--dot rect { fill: var(--bg-inset); stroke: var(--cyan-800); }
.kl-node--active rect,
.kl-node--active polygon {
  stroke: var(--cyan-300);
  stroke-width: 1.5;
  fill: var(--bg-elevated);
  filter: drop-shadow(0 0 8px var(--accent-glow));
}
.kl-node--active text.kl-label { fill: var(--cyan-100); text-shadow: var(--text-glow-cyan); }

/* --- kind-based tint (DS amber + blue tokens; NEVER purple) ---
   skills stay cyan (the primary signal); mcp tiles carry the amber accent
   (the DS's own "HIGHLIGHT" hue); servers wear the steel-blue "STRUCTURE"
   role. Legend at bottom-right of HUD names the mapping. */
[data-kind="mcp"] polygon,
[data-kind="mcp"] rect {
  stroke: var(--amber-500);
}
[data-kind="mcp"] text.kl-kind { fill: var(--amber-300); }
[data-kind="mcp"] text.kl-label { fill: var(--amber-100); }
[data-kind="mcp"]:hover polygon,
[data-kind="mcp"]:hover rect {
  filter: drop-shadow(0 0 6px var(--warn-glow));
}
[data-kind="server"] polygon,
[data-kind="server"] rect {
  stroke: var(--blue-400);
}
[data-kind="server"] text.kl-kind { fill: var(--blue-300); }
[data-kind="server"] text.kl-label { fill: var(--blue-200); }
[data-kind="server"]:hover polygon,
[data-kind="server"]:hover rect {
  filter: drop-shadow(0 0 6px rgba(61, 114, 240, 0.5));
}
[data-kind="edge"] polygon,
[data-kind="edge"] rect { stroke: var(--amber-700); }
[data-kind="edge"] text.kl-kind { fill: var(--amber-500); }
[data-kind="edge"] text.kl-label { fill: var(--amber-300); }
/* active outranks the tint but keeps the tint's glow */
[data-kind="mcp"].kl-node--active polygon,
[data-kind="mcp"].kl-node--active rect {
  stroke: var(--amber-300);
  filter: drop-shadow(0 0 8px var(--warn-glow));
}
[data-kind="server"].kl-node--active polygon,
[data-kind="server"].kl-node--active rect {
  stroke: var(--blue-300);
  filter: drop-shadow(0 0 8px rgba(61, 114, 240, 0.6));
}

/* --- shared MCP marker (both trunks reach in) ---
   `data-shared="true"` is set by draw() for MCPs whose parent set contains
   both claude-code and codex. The convergence of two elbows on the tile
   already tells the story visually; this extra glow + the " · shared"
   suffix in the kind label make it obvious at first glance. */
[data-kind="mcp"][data-shared="true"] polygon,
[data-kind="mcp"][data-shared="true"] rect {
  stroke-width: 1.5;
  filter: drop-shadow(0 0 10px var(--warn-glow));
}
[data-kind="mcp"][data-shared="true"] text.kl-kind {
  fill: var(--amber-300);
  text-shadow: 0 0 6px rgba(255, 176, 0, 0.4);
}

/* --- bus (~/.agents shared skill bus) ---
   Amber says "connector" (DS's own highlight role); dashed stroke says
   "rail — traffic runs along it". Distinct from a solid amber MCP tile:
   the dash is the visual claim of "shared line, not a leaf". */
[data-kind="bus"] polygon,
[data-kind="bus"] rect {
  fill: var(--bg-panel);
  stroke: var(--amber-500);
  stroke-width: 1.5;
  stroke-dasharray: 8 4;
  filter: drop-shadow(0 0 6px var(--warn-glow));
}
[data-kind="bus"] text.kl-kind {
  fill: var(--amber-300);
  text-shadow: 0 0 6px rgba(255, 176, 0, 0.35);
}
[data-kind="bus"] text.kl-label {
  fill: var(--amber-100);
  font-weight: 700;
  letter-spacing: 0.06em;
}
[data-kind="bus"]:hover polygon,
[data-kind="bus"]:hover rect {
  filter: drop-shadow(0 0 10px var(--warn-glow));
}
[data-kind="bus"].kl-node--active polygon,
[data-kind="bus"].kl-node--active rect {
  stroke: var(--amber-300);
  filter: drop-shadow(0 0 12px var(--warn-glow));
}

/* --- secondary trunk (any trunk that isn't the primary hub) ---
   Selector: an agent tile that renders "--big" AND sits at x ≥ 1000 (the
   right column of the layout). The primary trunk always anchors LEFT, so
   the right-side ".kl-node--big" is always the second seat. Same big
   material as the primary but tinted a touch cooler and less glow so the
   eye reads "home / other seat" instead of two identical hubs shouting.

   Implementation: primary trunk keeps the default .kl-node--big cyan-500
   stroke. The secondary + any extra trunks use kl-node--big-secondary,
   set in JS. That keeps the CSS id-free. */
.kl-node--big-secondary polygon {
  stroke: var(--cyan-400);
  filter: drop-shadow(0 0 6px rgba(31, 200, 255, 0.28));
}
.kl-node--big-secondary text.kl-label { fill: var(--cyan-200); }
.kl-node--big-secondary text.kl-kind  { fill: var(--cyan-300); }

/* --- rim agent seats (chatgpt, reasonix, any future rim seat) ---
   Every non-trunk agent gets this treatment. Cyan-family so it still reads
   as an operator seat, but dimmer, smaller, less glow than a trunk. */
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary) polygon,
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary) rect {
  fill: var(--bg-panel);
  stroke: var(--cyan-700);
  stroke-width: 1;
  filter: none;
}
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary) text.kl-label { fill: var(--cyan-200); }
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary) text.kl-kind  { fill: var(--cyan-400); }
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary):hover polygon,
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary):hover rect {
  filter: drop-shadow(0 0 6px var(--accent-glow-soft));
}
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary).kl-node--active polygon,
[data-kind="agent"]:not(.kl-node--big):not(.kl-node--big-secondary).kl-node--active rect {
  stroke: var(--cyan-300);
  filter: drop-shadow(0 0 8px var(--accent-glow));
}

/* --- device seat (an agent whose edges all point to `device` kind) ---
   Selector: an agent tile that carries data-device-seat="true" (set in JS via
   the predicate "all outgoing edges go to device-kind nodes"). Sits on top
   of its device inside the tailnet ring — a slim horizontal band. Cyan-family
   like other agent seats, but a touch dimmer so the eye reads "on-device"
   instead of "another trunk hub". No id in CSS; the predicate is data-driven. */
[data-kind="agent"][data-device-seat="true"] polygon,
[data-kind="agent"][data-device-seat="true"] rect {
  fill: rgba(13, 19, 32, 0.85);
  stroke: var(--cyan-500);
  stroke-width: 1;
  filter: drop-shadow(0 0 5px var(--accent-glow-soft));
}
[data-kind="agent"][data-device-seat="true"] text.kl-label { fill: var(--cyan-100); }
[data-kind="agent"][data-device-seat="true"] text.kl-kind  { fill: var(--cyan-300); }

/* --- device tile (physical machine — phone, tablet, laptop) ---
   Double-outline vocabulary within the DS: an outer chassis polygon in
   neutral steel + an inner "screen" polygon in cyan. Port notches on the
   top edge (small ticks) read as I/O. Client labels sit inside the screen
   in dim amber mono. Devices are structural (like servers) but personal —
   not blue like a rack server, not amber like a service. */
[data-kind="device"] .kl-device__chassis {
  fill: rgba(10, 15, 23, 0.9);
  stroke: var(--neutral-400);
  stroke-width: 1.5;
}
[data-kind="device"] .kl-device__screen {
  fill: rgba(0, 55, 78, 0.28);
  stroke: var(--cyan-500);
  stroke-width: 1;
}
[data-kind="device"] .kl-device__port {
  stroke: var(--cyan-400);
  stroke-width: 1.2;
  stroke-linecap: square;
}
[data-kind="device"] text.kl-kind  { fill: var(--cyan-300); }
[data-kind="device"] text.kl-label { fill: var(--cyan-100); text-shadow: 0 0 6px rgba(31, 200, 255, 0.45); }
[data-kind="device"] text.kl-device__client {
  fill: var(--amber-300);
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  opacity: 0.86;
}
[data-kind="device"]:hover .kl-device__chassis {
  stroke: var(--neutral-200);
  filter: drop-shadow(0 0 4px rgba(148, 158, 174, 0.5));
}
[data-kind="device"]:hover .kl-device__screen {
  filter: drop-shadow(0 0 6px var(--accent-glow-soft));
}
[data-kind="device"].kl-node--active .kl-device__chassis {
  stroke: var(--cyan-200);
  filter: drop-shadow(0 0 10px var(--accent-glow));
}
[data-kind="device"].kl-node--active .kl-device__screen {
  stroke: var(--cyan-300);
}

/* --- net:tailscale ring (private mesh boundary) ---
   The ring IS the node. Bounding-box chamfered polygon around every node
   with tailnet:true. Stroke reads as a network boundary via dot-dash. Fill
   transparent, pointer-events on stroke only so members inside stay clickable.
   Label sits just above the top edge of the ring. Steel-blue pairs with the
   server family without collision — bus is amber-dashed, net is blue-dashed. */
[data-kind="net"] .kl-net-ring__border {
  fill: transparent;
  stroke: var(--blue-300);
  stroke-width: 1.5;
  stroke-dasharray: 10 3 2 3;
  filter: drop-shadow(0 0 6px rgba(61, 114, 240, 0.32));
  pointer-events: stroke;
  transition: stroke 140ms linear, filter 140ms linear;
}
[data-kind="net"]:hover .kl-net-ring__border {
  stroke: var(--blue-200);
  filter: drop-shadow(0 0 10px rgba(61, 114, 240, 0.5));
}
[data-kind="net"].kl-node--active .kl-net-ring__border {
  stroke: var(--blue-100);
  filter: drop-shadow(0 0 12px rgba(61, 114, 240, 0.6));
}
[data-kind="net"] text.kl-net-ring__label {
  fill: var(--blue-200);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  pointer-events: none;
}

/* --- .kl-edge--akari · the ONE cross-net line (mcp lives on device) ---
   Predicate on `via === "lives on"`. Drawn slightly thinner than the
   default edges but with a soft cyan glow so it reads as intentional —
   the one wire that leaves the map body and threads into the ring.
   The rule is data-driven; the current data has exactly one such edge. */
.kl-edge--akari {
  stroke: var(--cyan-300);
  stroke-width: 1.1;
  opacity: 0.95;
  filter: drop-shadow(0 0 4px var(--accent-glow-soft));
}

/* --- chassis etching in HUD (bottom-left) ---
   The whole map runs INSIDE this machine — the viewer is already inside.
   Rendered like an instrument nameplate: chamfered outline block, CJK
   label rhymes with the top-left 昆 仑 mark, English gloss below, and a
   short tick stub in the tailnet ring's own stroke style so the eye reads
   "chassis is another member of the same mesh, folded into the frame." */
.kl-chassis {
  position: absolute;
  bottom: 180px;
  left: 28px;
  z-index: 41;
  padding: 8px 12px 8px 14px;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 12px;
  align-items: center;
  border: 1px solid var(--cyan-800);
  clip-path: var(--clip-chamfer-sm);
  background: rgba(4, 6, 10, 0.72);
  pointer-events: none;
  font-family: var(--font-mono);
  color: var(--text-tertiary);
}
.kl-chassis__model {
  font-family: var(--font-code), monospace;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.3em;
  color: var(--cyan-500);
  padding-right: 10px;
  border-right: 1px solid var(--cyan-800);
  line-height: 1;
}
.kl-chassis__body {
  display: flex; flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.kl-chassis__kind {
  color: var(--blue-300);
  font-size: 9px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
}
.kl-chassis__note {
  color: var(--text-tertiary);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* stub: a short dot-dash tick in the same style as the net:tailscale ring —
   reads as "the chassis is folded into the same mesh; the ring's stroke
   just enters the HUD frame here." Uses an SVG background so the dash
   pattern is byte-identical to the ring's stroke-dasharray "10 3 2 3". */
.kl-chassis::after {
  content: "";
  position: absolute;
  right: -60px;
  top: 50%;
  width: 60px;
  height: 4px;
  transform: translateY(-2px);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='60' height='4' viewBox='0 0 60 4'><line x1='0' y1='2' x2='60' y2='2' stroke='%236c95ff' stroke-width='1.6' stroke-dasharray='10 3 2 3' /></svg>");
  background-repeat: no-repeat;
  background-position: left center;
  filter: drop-shadow(0 0 4px rgba(61, 114, 240, 0.55));
}

/* --- unwired agent ("零边节点漂在图缘") ---
   Data-driven from nodes.json edge topology, not from a specific id. Any
   agent with zero edges gets this drift look — dashed neutral border,
   transparent fill, no glow. Nodes.json's $comment names this contract:
   "Nodes without edges are real but unwired: let them drift detached at
   the rim." Currently no node hits this case; the rule stays for future. */
[data-kind="agent"][data-unwired="true"] polygon,
[data-kind="agent"][data-unwired="true"] rect {
  fill: transparent;
  stroke: var(--neutral-500);
  stroke-width: 1;
  stroke-dasharray: 3 4;
  filter: none;
}
[data-kind="agent"][data-unwired="true"] text.kl-label { fill: var(--neutral-400); }
[data-kind="agent"][data-unwired="true"] text.kl-kind  { fill: var(--neutral-500); }
[data-kind="agent"][data-unwired="true"]:hover polygon,
[data-kind="agent"][data-unwired="true"]:hover rect {
  stroke: var(--neutral-400);
  filter: drop-shadow(0 0 4px rgba(148, 158, 174, 0.4));
}
[data-kind="agent"][data-unwired="true"].kl-node--active polygon,
[data-kind="agent"][data-unwired="true"].kl-node--active rect {
  stroke: var(--neutral-300);
  filter: drop-shadow(0 0 6px rgba(148, 158, 174, 0.5));
}


/* pop-in for the map's own nodes during onEnter — stepped, sharp */
.kl-node {
  opacity: 0;
  transform-origin: center;
}
.yo-stage[data-open="true"] .kl-node {
  opacity: 1;
  transition: opacity 60ms steps(1, end);
}

/* signature panels that pop next to a node during trace / click */
.kl-panel-pop-group { opacity: 0; }
.kl-panel-pop-group.kl-pop--on { opacity: 1; transition: opacity 120ms steps(2, end); }
.kl-panel-pop {
  fill: var(--bg-elevated);
  stroke: var(--cyan-500);
  stroke-width: 1;
  filter: drop-shadow(0 0 8px var(--accent-glow-soft));
}
.kl-panel-pop-title {
  fill: var(--cyan-200);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.kl-panel-pop-kind {
  fill: var(--cyan-400);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
}
.kl-panel-pop-body {
  fill: var(--neutral-200);
  font-family: var(--font-mono);
  font-size: 10px;
}
.kl-panel-pop-corner {
  fill: none;
  stroke: var(--cyan-400);
  stroke-width: 1;
}

/* --- boot log overlay --- */
.kl-boot {
  position: absolute;
  left: 118px;
  top: 74px;
  right: 24px;
  z-index: 45;
  color: var(--cyan-200);
  font-family: var(--font-code);
  font-size: 13px;
  line-height: 1.7;
  pointer-events: none;
  max-width: 640px;
}
.kl-boot__line {
  color: var(--cyan-200);
  white-space: pre;
  overflow: hidden;
  animation: kl-typewriter 320ms steps(24, end) forwards;
  width: 0;
}
.kl-boot__line--muted { color: var(--neutral-400); }
.kl-boot__line--em { color: var(--cyan-200); text-shadow: var(--text-glow-cyan); }
.kl-boot__line--sep { color: var(--cyan-800); }
.kl-boot__line::before { content: ""; }
.kl-boot--done { opacity: 0; transition: opacity 240ms linear; }

/* --- terminal at bottom ---
   the input box is the least self-explanatory thing on this screen, so give
   it a clearer edge, a heavier header bar, a placeholder in the input, and a
   one-shot glow pulse when boot finishes to draw the eye down. */
.kl-term {
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  background: rgba(4, 6, 10, 0.92);
  border: 1.5px solid var(--cyan-600);
  clip-path: var(--clip-chamfer-md);
  color: var(--cyan-200);
  font-family: var(--font-code);
  font-size: 13px;
  display: grid;
  grid-template-rows: auto 1fr;
  min-height: 118px;
  box-shadow:
    0 0 12px rgba(0, 184, 255, 0.22),
    0 0 0 1px rgba(0, 0, 0, 0.6) inset;
  transition: box-shadow 180ms linear, border-color 180ms linear;
  cursor: text;
}
.kl-term:focus-within {
  border-color: var(--cyan-400);
  box-shadow:
    0 0 20px rgba(0, 184, 255, 0.4),
    0 0 0 1px rgba(0, 0, 0, 0.6) inset;
}
/* the four HUD-style corner ticks on the terminal frame — help it read as an
   instrument, not a chat input */
.kl-term::before, .kl-term::after {
  content: "";
  position: absolute;
  width: 12px; height: 12px;
  border: 1px solid var(--cyan-400);
  pointer-events: none;
}
.kl-term::before { top: 4px; left: 4px;  border-right: none; border-bottom: none; }
.kl-term::after  { bottom: 4px; right: 4px; border-left: none;  border-top: none; }

/* one-shot glow the moment boot finishes — pulls the eye down to the input */
.kl-term.kl-term--nudge {
  animation: kl-term-nudge 1500ms ease-out 1;
}
@keyframes kl-term-nudge {
  0%   { box-shadow: 0 0 12px rgba(0, 184, 255, 0.22), 0 0 0 1px rgba(0, 0, 0, 0.6) inset; border-color: var(--cyan-600); }
  35%  { box-shadow: 0 0 42px rgba(0, 184, 255, 0.85), 0 0 0 1px rgba(0, 0, 0, 0.6) inset; border-color: var(--cyan-300); }
  100% { box-shadow: 0 0 12px rgba(0, 184, 255, 0.22), 0 0 0 1px rgba(0, 0, 0, 0.6) inset; border-color: var(--cyan-600); }
}

.kl-term__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 16px;
  border-bottom: 1px solid var(--cyan-800);
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
  background: linear-gradient(180deg, var(--bg-elevated), var(--bg-panel));
}
.kl-term__head-title {
  color: var(--cyan-200);
  text-shadow: var(--text-glow-cyan);
  font-weight: 700;
}
.kl-term__head-title::before { content: "▸ "; color: var(--cyan-400); }
.kl-term__head-hint {
  color: var(--cyan-400);
  opacity: 0.7;
  font-weight: 500;
  letter-spacing: var(--tracking-wide);
}
.kl-term__body {
  padding: 10px 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.kl-term__log {
  color: var(--neutral-300);
  font-family: var(--font-code);
  font-size: 12px;
  max-height: 44px;
  overflow: hidden;
  line-height: 1.4;
}
.kl-term__log-line--user { color: var(--cyan-200); }
.kl-term__log-line--user::before { content: "kunlun> "; color: var(--cyan-500); }
.kl-term__log-line--err { color: var(--red-300); }
.kl-term__log-line--muted { color: var(--text-tertiary); }
.kl-term__prompt-row {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: nowrap;
}
.kl-term__prompt {
  color: var(--cyan-300);
  text-shadow: 0 0 8px rgba(0, 184, 255, 0.9), 0 0 2px var(--cyan-200);
  user-select: none;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.03em;
}
.kl-term__input {
  flex: 1;
  background: transparent;
  color: var(--neutral-100);
  border: none; outline: none;
  font-family: inherit; font-size: 14px;
  caret-color: var(--cyan-400);
  min-width: 60px;
  letter-spacing: 0.02em;
}
.kl-term__input::placeholder {
  color: var(--cyan-800);
  opacity: 0.95;
  letter-spacing: 0.03em;
  font-style: normal;
  font-family: var(--font-code), "Noto Sans SC", monospace;
}
/* the ghost hides once the user has typed anything, so we key it off :not(:placeholder-shown) */
.kl-term__input:focus::placeholder { color: var(--cyan-900); opacity: 0.7; }
.kl-term__caret {
  display: inline-block;
  width: 9px; height: 15px;
  background: var(--cyan-400);
  box-shadow: var(--glow-cyan-sm);
  vertical-align: middle;
  margin-left: 6px;
  animation: kl-blink 1s steps(2, end) infinite;
}
/* once the input has focus the native caret takes over — never two cursors */
.kl-term__prompt-row:focus-within .kl-term__caret { display: none; }

.kl-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.kl-chip {
  position: relative;
  background: rgba(13, 19, 32, 0.75);
  border: 1px solid var(--cyan-700);
  color: var(--cyan-200);
  clip-path: var(--clip-chamfer-sm);
  font-family: inherit;
  font-size: 11.5px;
  letter-spacing: 0.05em;
  padding: 5px 12px 5px 16px;
  cursor: pointer;
  transition: border-color 120ms linear, box-shadow 120ms linear,
              color 120ms linear, background 120ms linear;
  white-space: nowrap;
  font-weight: 500;
}
.kl-chip::before {
  content: "▸";
  position: absolute;
  left: 6px; top: 50%;
  transform: translateY(-52%);
  color: var(--cyan-500);
  font-size: 10px;
  transition: transform 120ms linear, color 120ms linear;
}
.kl-chip:hover, .kl-chip:focus-visible {
  border-color: var(--cyan-300);
  color: var(--cyan-100);
  background: rgba(0, 117, 168, 0.32);
  box-shadow: 0 0 14px rgba(0, 184, 255, 0.5);
  outline: none;
}
.kl-chip:hover::before, .kl-chip:focus-visible::before {
  color: var(--cyan-200);
  transform: translateY(-52%) translateX(2px);
}
.kl-chip:active {
  background: rgba(0, 152, 212, 0.5);
  border-color: var(--cyan-200);
}
.kl-chip[data-cmd="exit"] {
  border-color: var(--red-700);
  color: var(--red-300);
  background: rgba(38, 6, 6, 0.55);
}
.kl-chip[data-cmd="exit"]::before { color: var(--red-500); }
.kl-chip[data-cmd="exit"]:hover {
  border-color: var(--red-500);
  color: var(--red-100);
  box-shadow: var(--glow-red-md);
  background: rgba(76, 12, 12, 0.6);
}
.kl-chip[data-cmd="exit"]:hover::before { color: var(--red-300); }

/* --- man-page overlay (cat / ls / node inspect) --- */
.kl-doc {
  position: absolute;
  top: 74px;
  bottom: 148px;
  right: 24px;
  width: min(720px, 46vw);
  background: rgba(4, 6, 10, 0.94);
  border: 1px solid var(--cyan-700);
  clip-path: var(--clip-chamfer-md);
  color: var(--neutral-100);
  font-family: var(--font-code);
  font-size: 12px;
  line-height: 1.55;
  padding: 20px 22px;
  overflow: auto;
  /* above the HUD (40) and the hover tooltip (45): a doc window is the
     topmost reading surface short of the terminal itself */
  z-index: 48;
  box-shadow: 0 0 32px rgba(0, 0, 0, 0.6);
  display: none;
}
.kl-doc[data-open="true"] { display: block; }
/* while a doc is up, the chrome living under its slot yields instantly —
   the legend and scale hint are map furniture, not doc furniture */
#stage[data-doc-open] .kl-hud__legend,
#stage[data-doc-open] .kl-hud__scale { opacity: 0; pointer-events: none; }
.kl-doc__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
  margin-bottom: 12px;
  cursor: grab;
  user-select: none;
  touch-action: none;
}
.kl-doc--dragging .kl-doc__head { cursor: grabbing; }
.kl-doc__head-title {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 11px;
  color: var(--cyan-300);
  text-shadow: var(--text-glow-cyan);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
}
.kl-doc__close {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-tertiary);
  font-family: inherit;
  font-size: 10px;
  padding: 3px 8px;
  clip-path: var(--clip-chamfer-sm);
  cursor: pointer;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.kl-doc__close:hover { color: var(--cyan-300); border-color: var(--cyan-700); }
.kl-doc__body { color: var(--neutral-100); }
.kl-doc__body pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: var(--font-code);
  font-size: 11.5px;
  color: var(--neutral-200);
  line-height: 1.5;
}
.kl-doc__body pre .h1 {
  color: var(--cyan-200);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  text-shadow: var(--text-glow-cyan);
}
.kl-doc__body pre .h2 {
  color: var(--cyan-300);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.kl-doc__body pre .h3 { color: var(--cyan-400); font-weight: 700; text-transform: uppercase; letter-spacing: var(--tracking-wide); }
.kl-doc__body pre .code { color: var(--cyan-300); }
.kl-doc__body pre .em { color: var(--neutral-50); }
.kl-doc__body pre .muted { color: var(--text-tertiary); }
.kl-doc__body pre .frame { color: var(--cyan-800); }
.kl-doc__body pre .link { color: var(--cyan-300); }

/* ls output: grid of 48 skills */
.kl-doc__ls {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 2px 24px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.4;
}
.kl-doc__ls-item {
  color: var(--neutral-200);
  padding: 1px 0;
  opacity: 0;
  animation: kl-ls-in 60ms steps(1, end) forwards;
}
.kl-doc__ls-item::before {
  content: "▸ ";
  color: var(--cyan-500);
}
.kl-doc__ls-item--muted { color: var(--text-tertiary); }
@keyframes kl-ls-in { to { opacity: 1; } }

/* node inspect body */
.kl-doc__node {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 4px 18px;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
}
.kl-doc__node dt {
  color: var(--cyan-400);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  font-size: 10px;
  padding-top: 3px;
}
.kl-doc__node dd { margin: 0; color: var(--neutral-100); }
.kl-doc__node dd .edge {
  display: block;
  color: var(--neutral-300);
  font-size: 11px;
}
.kl-doc__node dd .edge::before {
  content: "├── ";
  color: var(--cyan-800);
}

/* --- boot log — extra "tip for operator" bilingual line -----
   printed by the boot sequence after the mechanical lines. The whole point
   of this line is to onboard a first-time visitor who doesn't yet know the
   terminal is interactive. */
.kl-boot__line--tip {
  color: var(--amber-300);
  text-shadow: 0 0 6px rgba(255, 176, 0, 0.35);
  letter-spacing: 0.02em;
}
.kl-boot__line--tip-cn {
  color: var(--amber-100);
  opacity: 0.8;
  font-family: "Noto Sans SC", var(--font-code), monospace;
  font-size: 12px;
  letter-spacing: 0.05em;
}

/* --- HUD legend ---
   bottom-right of the frame, above the scale hint. Reads as diegetic HUD
   chrome (uppercase mono, small swatches), not as a UI overlay. */
.kl-hud__legend {
  position: absolute;
  bottom: 172px;
  right: 46px;
  display: grid;
  grid-template-columns: auto auto auto;
  gap: 4px 14px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  text-align: right;
  pointer-events: none;
  z-index: 41;
}
.kl-hud__legend-head {
  grid-column: 1 / -1;
  color: var(--cyan-500);
  letter-spacing: var(--tracking-widest);
  margin-bottom: 2px;
}
.kl-hud__legend-swatch {
  width: 10px; height: 10px;
  display: inline-block;
  border: 1px solid currentColor;
  vertical-align: middle;
  margin-right: 4px;
  clip-path: polygon(2px 0, 100% 0, 100% calc(100% - 2px), calc(100% - 2px) 100%, 0 100%, 0 2px);
}
.kl-hud__legend-row { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }
.kl-hud__legend-row--skill  { color: var(--cyan-300); }
.kl-hud__legend-row--mcp    { color: var(--amber-300); }
.kl-hud__legend-row--server { color: var(--blue-300); }
.kl-hud__legend-row--agent  { color: var(--cyan-200); }
.kl-hud__legend-row--bus    { color: var(--amber-300); }
.kl-hud__legend-row--bus .kl-hud__legend-swatch {
  border-style: dashed;
}
.kl-hud__legend-row--device { color: var(--cyan-100); }
.kl-hud__legend-row--device .kl-hud__legend-swatch {
  border-color: var(--neutral-400);
  border-width: 1px;
}
.kl-hud__legend-row--net    { color: var(--blue-200); }
.kl-hud__legend-row--net .kl-hud__legend-swatch {
  border-style: dashed;
  border-color: var(--blue-300);
}
.kl-hud__legend-cn {
  grid-column: 1 / -1;
  color: var(--text-tertiary);
  opacity: 0.7;
  font-family: "Noto Sans SC", var(--font-mono), monospace;
  font-size: 9.5px;
  letter-spacing: 0.16em;
  margin-top: 2px;
}

/* --- hover tooltip (bilingual: english label + chinese one-liner) ---
   a chamfered floating panel that follows the cursor. On touch there is no
   hover; the node-inspect overlay carries the chinese line instead. */
.kl-tip {
  position: fixed;
  z-index: 70;
  pointer-events: none;
  min-width: 220px;
  max-width: 300px;
  background: rgba(4, 6, 10, 0.95);
  border: 1px solid var(--cyan-500);
  clip-path: var(--clip-chamfer-sm);
  color: var(--neutral-100);
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 10px 12px 12px;
  box-shadow: 0 0 20px rgba(0, 184, 255, 0.35), 0 4px 16px rgba(0, 0, 0, 0.7);
  opacity: 0;
  transform: translate3d(0, 4px, 0);
  transition: opacity 120ms linear, transform 120ms linear;
}
.kl-tip[data-show="true"] { opacity: 1; transform: translate3d(0, 0, 0); }
.kl-tip[data-kind="mcp"]    { border-color: var(--amber-500); box-shadow: 0 0 20px rgba(255, 176, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.7); }
.kl-tip[data-kind="server"] { border-color: var(--blue-400);  box-shadow: 0 0 20px rgba(61, 114, 240, 0.35), 0 4px 16px rgba(0, 0, 0, 0.7); }
.kl-tip[data-kind="edge"]   { border-color: var(--amber-700); }
.kl-tip[data-kind="bus"]    { border-color: var(--amber-500); border-style: dashed; box-shadow: 0 0 20px rgba(255, 176, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.7); }
.kl-tip[data-kind="agent"]  { border-color: var(--cyan-500); }
.kl-tip[data-kind="device"] { border-color: var(--neutral-300); box-shadow: 0 0 20px rgba(148, 158, 174, 0.28), 0 4px 16px rgba(0, 0, 0, 0.7); }
.kl-tip[data-kind="net"]    { border-color: var(--blue-300); border-style: dashed; box-shadow: 0 0 20px rgba(61, 114, 240, 0.35), 0 4px 16px rgba(0, 0, 0, 0.7); }
.kl-tip__kind {
  color: var(--cyan-400);
  font-size: 9px;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  margin-bottom: 4px;
}
.kl-tip[data-kind="mcp"] .kl-tip__kind    { color: var(--amber-300); }
.kl-tip[data-kind="server"] .kl-tip__kind { color: var(--blue-300); }
.kl-tip[data-kind="edge"] .kl-tip__kind   { color: var(--amber-500); }
.kl-tip[data-kind="bus"] .kl-tip__kind    { color: var(--amber-300); }
.kl-tip[data-kind="agent"] .kl-tip__kind  { color: var(--cyan-300); }
.kl-tip[data-kind="device"] .kl-tip__kind { color: var(--neutral-200); }
.kl-tip[data-kind="net"] .kl-tip__kind    { color: var(--blue-200); }
.kl-tip__label {
  color: var(--cyan-100);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  margin-bottom: 6px;
  text-shadow: var(--text-glow-cyan);
}
.kl-tip[data-kind="mcp"] .kl-tip__label    { color: var(--amber-100); text-shadow: var(--text-glow-amber); }
.kl-tip[data-kind="server"] .kl-tip__label { color: var(--blue-200);  text-shadow: 0 0 6px rgba(61, 114, 240, 0.7); }
.kl-tip[data-kind="edge"] .kl-tip__label   { color: var(--amber-100); text-shadow: var(--text-glow-amber); }
.kl-tip[data-kind="bus"] .kl-tip__label    { color: var(--amber-100); text-shadow: var(--text-glow-amber); }
.kl-tip[data-kind="agent"] .kl-tip__label  { color: var(--cyan-100);  text-shadow: var(--text-glow-cyan); }
.kl-tip[data-kind="device"] .kl-tip__label { color: var(--cyan-100);  text-shadow: 0 0 6px rgba(31, 200, 255, 0.55); }
.kl-tip[data-kind="net"] .kl-tip__label    { color: var(--blue-100);  text-shadow: 0 0 6px rgba(61, 114, 240, 0.7); }
.kl-tip__zh {
  color: var(--neutral-100);
  font-family: "Noto Sans SC", var(--font-mono), monospace;
  font-size: 11.5px;
  line-height: 1.5;
  letter-spacing: 0.02em;
}
.kl-tip__zh::before {
  content: "▮ ";
  color: var(--cyan-500);
  letter-spacing: 0;
}
.kl-tip[data-kind="mcp"] .kl-tip__zh::before    { color: var(--amber-500); }
.kl-tip[data-kind="server"] .kl-tip__zh::before { color: var(--blue-400); }
.kl-tip[data-kind="bus"] .kl-tip__zh::before    { color: var(--amber-500); }
.kl-tip[data-kind="agent"] .kl-tip__zh::before  { color: var(--cyan-400); }
.kl-tip[data-kind="device"] .kl-tip__zh::before { color: var(--neutral-300); }
.kl-tip[data-kind="net"] .kl-tip__zh::before    { color: var(--blue-300); }

/* --- doc-body: chinese line for node inspect overlay --- */
.kl-doc__node dd.kl-doc__zh {
  font-family: "Noto Sans SC", var(--font-mono), monospace;
  color: var(--cyan-200);
  font-size: 12px;
  line-height: 1.55;
  letter-spacing: 0.02em;
}

/* --- marching ants on edges (only when the map is quiet — hover neighbourhood
   of a node highlights its adjacent edges). Uses steps() to keep with the DS
   motion vocabulary. */
.kl-edge--flow {
  stroke: var(--cyan-300);
  stroke-width: 1.5;
  stroke-dasharray: 6 6;
  opacity: 1;
  animation: kl-flow 800ms steps(12, end) infinite;
  filter: drop-shadow(0 0 4px var(--accent-glow-soft));
}
@keyframes kl-flow {
  from { stroke-dashoffset: 0;   }
  to   { stroke-dashoffset: -24; }
}

/* trace signal head — amber, walks the path with the node highlights */
.kl-signal-head {
  fill: none;
  stroke: var(--amber-300);
  stroke-width: 3;
  stroke-linecap: square;
  filter: drop-shadow(0 0 6px var(--warn-glow));
  opacity: 0.98;
}
.kl-edge--trace {
  stroke: var(--amber-500);
  stroke-width: 2;
  opacity: 1;
  filter: drop-shadow(0 0 6px var(--warn-glow));
}

/* --- easter-egg output block for the terminal (moon phase, whoami …) --- */
.kl-doc__body pre.kl-egg {
  color: var(--cyan-200);
  font-family: var(--font-code);
  font-size: 12px;
  line-height: 1.3;
  /* ASCII art must never fold (base doc pre is pre-wrap); scroll instead */
  white-space: pre;
  word-break: normal;
  overflow-x: auto;
}
.kl-doc__body pre.kl-egg .accent { color: var(--amber-300); text-shadow: var(--text-glow-amber); }
.kl-doc__body pre.kl-egg .dim    { color: var(--text-tertiary); }
.kl-doc__body pre.kl-egg .zh {
  color: var(--cyan-100);
  font-family: "Noto Sans SC", var(--font-code), monospace;
  font-size: 12px;
}

/* --- 390 mobile: term is a bit bigger, doc goes fullscreen --- */
@media (max-width: 640px) {
  .kl-hud__corner--tl { top: 12px; left: 66px; width: 16px; height: 16px; }
  .kl-hud__corner--tr { top: 12px; right: 12px; width: 16px; height: 16px; }
  .kl-hud__corner--bl { bottom: 12px; left: 12px; width: 16px; height: 16px; }
  .kl-hud__corner--br { bottom: 12px; right: 12px; width: 16px; height: 16px; }
  .kl-hud__label { top: 22px; left: 92px; font-size: 10px; gap: 8px; }
  .kl-hud__label__sub { font-size: 10px; letter-spacing: 0.4em; padding-left: 8px; }
  .kl-hud__coord { display: none; }
  .kl-hud__scale { display: none; }
  .kl-boot { left: 12px; top: 56px; right: 12px; font-size: 12px; }
  .kl-term {
    left: 10px; right: 10px; bottom: 10px;
    min-height: 96px;
  }
  .kl-term__body { padding: 6px 12px 10px; }
  .kl-doc {
    top: 56px;
    bottom: 138px;
    left: 10px;
    right: 10px;
    width: auto;
    font-size: 11px;
    padding: 14px 14px;
  }
  /* 56-char moon art fits the ~342px doc at 10px mono; keeps the
     discs ~1:1 (11ch×6px ≈ 5rows×13px) so no horizontal scroll */
  .kl-doc__body pre.kl-egg { font-size: 10px; }
  .kl-chips { gap: 6px; }
  .kl-chip { font-size: 10.5px; padding: 4px 10px 4px 14px; }
  .kl-chip::before { left: 5px; font-size: 9px; }
  .yo-return { top: 12px; left: 12px; width: 40px; height: 40px; }
  .yo-return svg { width: 22px; height: 20px; }
  .kl-map { touch-action: none; }
  .kl-hud__legend { display: none; }
  .kl-tip { display: none; }   /* hover is nonexistent on touch */
  .kl-chassis { display: none; }  /* HUD chrome hidden on 390 */
  .kl-term__prompt { font-size: 13px; }
  .kl-term__input { font-size: 13px; }
  .kl-term__head-hint { display: none; }
}

/* --- reduced motion still version ---
   The morph becomes an instant swap; the world still needs to render as a
   finished frame instead of animating in from empty. The map is at scale 1,
   nodes visible, HUD present, boot log hidden, chips already shown, caret
   still. */
@media (prefers-reduced-motion: reduce) {
  .kl-map__canvas { opacity: 1; }
  .kl-node { opacity: 1; }
  .kl-boot { display: none; }
  .kl-term__caret,
  .kl-hud__label::before { animation: none; opacity: 1; }
  .kl-boot__line { animation: none; width: 100%; }
  .kl-doc__ls-item { animation: none; opacity: 1; }
  /* marching ants stop; still readable as a highlighted edge */
  .kl-edge--flow {
    animation: none;
    stroke-dasharray: none;
  }
  /* the terminal glow-pulse only runs once anyway, but suppress it too */
  .kl-term.kl-term--nudge { animation: none; }
  /* tooltip appears instantly; no rise transition */
  .kl-tip { transition: none; }
}

/* pointer states for the map when the user is dragging */
.kl-map--panning { cursor: grabbing; }
