@kgdiff/viewer 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/README.md +43 -16
  2. package/custom-elements.json +2347 -0
  3. package/dist/bind.d.ts +41 -0
  4. package/dist/bind.d.ts.map +1 -0
  5. package/dist/bind.js +47 -0
  6. package/dist/bind.js.map +1 -0
  7. package/dist/context.d.ts +42 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +87 -0
  10. package/dist/context.js.map +1 -0
  11. package/dist/controller.d.ts +26 -237
  12. package/dist/controller.d.ts.map +1 -1
  13. package/dist/controller.js +33 -1484
  14. package/dist/controller.js.map +1 -1
  15. package/dist/dom.d.ts +10 -0
  16. package/dist/dom.d.ts.map +1 -1
  17. package/dist/dom.js +29 -0
  18. package/dist/dom.js.map +1 -1
  19. package/dist/element.d.ts +51 -42
  20. package/dist/element.d.ts.map +1 -1
  21. package/dist/element.js +381 -602
  22. package/dist/element.js.map +1 -1
  23. package/dist/elements/actions.d.ts +27 -0
  24. package/dist/elements/actions.d.ts.map +1 -0
  25. package/dist/elements/actions.js +81 -0
  26. package/dist/elements/actions.js.map +1 -0
  27. package/dist/elements/bound.d.ts +63 -0
  28. package/dist/elements/bound.d.ts.map +1 -0
  29. package/dist/elements/bound.js +213 -0
  30. package/dist/elements/bound.js.map +1 -0
  31. package/dist/elements/change-detail.d.ts +25 -0
  32. package/dist/elements/change-detail.d.ts.map +1 -0
  33. package/dist/elements/change-detail.js +19 -0
  34. package/dist/elements/change-detail.js.map +1 -0
  35. package/dist/elements/change-list.d.ts +27 -0
  36. package/dist/elements/change-list.d.ts.map +1 -0
  37. package/dist/elements/change-list.js +29 -0
  38. package/dist/elements/change-list.js.map +1 -0
  39. package/dist/elements/filters.d.ts +25 -0
  40. package/dist/elements/filters.d.ts.map +1 -0
  41. package/dist/elements/filters.js +19 -0
  42. package/dist/elements/filters.js.map +1 -0
  43. package/dist/elements/legend.d.ts +40 -0
  44. package/dist/elements/legend.d.ts.map +1 -0
  45. package/dist/elements/legend.js +87 -0
  46. package/dist/elements/legend.js.map +1 -0
  47. package/dist/elements/minimap.d.ts +38 -0
  48. package/dist/elements/minimap.d.ts.map +1 -0
  49. package/dist/elements/minimap.js +59 -0
  50. package/dist/elements/minimap.js.map +1 -0
  51. package/dist/elements/root.d.ts +40 -0
  52. package/dist/elements/root.d.ts.map +1 -0
  53. package/dist/elements/root.js +67 -0
  54. package/dist/elements/root.js.map +1 -0
  55. package/dist/elements/search.d.ts +29 -0
  56. package/dist/elements/search.d.ts.map +1 -0
  57. package/dist/elements/search.js +23 -0
  58. package/dist/elements/search.js.map +1 -0
  59. package/dist/elements/stage-context.d.ts +31 -0
  60. package/dist/elements/stage-context.d.ts.map +1 -0
  61. package/dist/elements/stage-context.js +4 -0
  62. package/dist/elements/stage-context.js.map +1 -0
  63. package/dist/elements/stage.d.ts +65 -0
  64. package/dist/elements/stage.d.ts.map +1 -0
  65. package/dist/elements/stage.js +259 -0
  66. package/dist/elements/stage.js.map +1 -0
  67. package/dist/elements/title.d.ts +25 -0
  68. package/dist/elements/title.d.ts.map +1 -0
  69. package/dist/elements/title.js +19 -0
  70. package/dist/elements/title.js.map +1 -0
  71. package/dist/elements/transport.d.ts +29 -0
  72. package/dist/elements/transport.d.ts.map +1 -0
  73. package/dist/elements/transport.js +24 -0
  74. package/dist/elements/transport.js.map +1 -0
  75. package/dist/elements/view-bound.d.ts +39 -0
  76. package/dist/elements/view-bound.d.ts.map +1 -0
  77. package/dist/elements/view-bound.js +88 -0
  78. package/dist/elements/view-bound.js.map +1 -0
  79. package/dist/elements/view-dock.d.ts +28 -0
  80. package/dist/elements/view-dock.d.ts.map +1 -0
  81. package/dist/elements/view-dock.js +28 -0
  82. package/dist/elements/view-dock.js.map +1 -0
  83. package/dist/elements/zoom.d.ts +28 -0
  84. package/dist/elements/zoom.d.ts.map +1 -0
  85. package/dist/elements/zoom.js +22 -0
  86. package/dist/elements/zoom.js.map +1 -0
  87. package/dist/i18n.d.ts +10 -0
  88. package/dist/i18n.d.ts.map +1 -1
  89. package/dist/i18n.js +20 -0
  90. package/dist/i18n.js.map +1 -1
  91. package/dist/icons.d.ts +1 -0
  92. package/dist/icons.d.ts.map +1 -1
  93. package/dist/icons.js +1 -0
  94. package/dist/icons.js.map +1 -1
  95. package/dist/index.d.ts +331 -36
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +13 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/keyboard.d.ts +39 -2
  100. package/dist/keyboard.d.ts.map +1 -1
  101. package/dist/keyboard.js +145 -62
  102. package/dist/keyboard.js.map +1 -1
  103. package/dist/layout-store.d.ts +26 -0
  104. package/dist/layout-store.d.ts.map +1 -0
  105. package/dist/layout-store.js +46 -0
  106. package/dist/layout-store.js.map +1 -0
  107. package/dist/panel-levels.d.ts +18 -0
  108. package/dist/panel-levels.d.ts.map +1 -0
  109. package/dist/panel-levels.js +41 -0
  110. package/dist/panel-levels.js.map +1 -0
  111. package/dist/panels.d.ts +28 -0
  112. package/dist/panels.d.ts.map +1 -0
  113. package/dist/panels.js +73 -0
  114. package/dist/panels.js.map +1 -0
  115. package/dist/pipeline.d.ts.map +1 -1
  116. package/dist/pipeline.js +3 -2
  117. package/dist/pipeline.js.map +1 -1
  118. package/dist/prefs.d.ts +5 -2
  119. package/dist/prefs.d.ts.map +1 -1
  120. package/dist/prefs.js +12 -3
  121. package/dist/prefs.js.map +1 -1
  122. package/dist/react.d.ts +460 -0
  123. package/dist/react.d.ts.map +1 -0
  124. package/dist/react.js +2 -0
  125. package/dist/react.js.map +1 -0
  126. package/dist/root-element.d.ts +122 -0
  127. package/dist/root-element.d.ts.map +1 -0
  128. package/dist/root-element.js +541 -0
  129. package/dist/root-element.js.map +1 -0
  130. package/dist/root.d.ts +54 -0
  131. package/dist/root.d.ts.map +1 -0
  132. package/dist/root.js +288 -0
  133. package/dist/root.js.map +1 -0
  134. package/dist/schedule.d.ts +22 -0
  135. package/dist/schedule.d.ts.map +1 -0
  136. package/dist/schedule.js +41 -0
  137. package/dist/schedule.js.map +1 -0
  138. package/dist/session.d.ts +269 -0
  139. package/dist/session.d.ts.map +1 -0
  140. package/dist/session.js +1237 -0
  141. package/dist/session.js.map +1 -0
  142. package/dist/standalone/kgdiff-viewer-compute.js +1 -1
  143. package/dist/standalone/kgdiff-viewer.js +4 -4
  144. package/dist/styles.d.ts +17 -6
  145. package/dist/styles.d.ts.map +1 -1
  146. package/dist/styles.js +367 -258
  147. package/dist/styles.js.map +1 -1
  148. package/dist/theme.d.ts +32 -0
  149. package/dist/theme.d.ts.map +1 -0
  150. package/dist/theme.js +152 -0
  151. package/dist/theme.js.map +1 -0
  152. package/dist/topics.d.ts +8 -0
  153. package/dist/topics.d.ts.map +1 -0
  154. package/dist/topics.js +18 -0
  155. package/dist/topics.js.map +1 -0
  156. package/dist/ui/change-list.d.ts +18 -0
  157. package/dist/ui/change-list.d.ts.map +1 -0
  158. package/dist/ui/change-list.js +331 -0
  159. package/dist/ui/change-list.js.map +1 -0
  160. package/dist/ui/context.d.ts +34 -7
  161. package/dist/ui/context.d.ts.map +1 -1
  162. package/dist/ui/context.js.map +1 -1
  163. package/dist/ui/filters.d.ts +9 -0
  164. package/dist/ui/filters.d.ts.map +1 -0
  165. package/dist/ui/filters.js +68 -0
  166. package/dist/ui/filters.js.map +1 -0
  167. package/dist/ui/header.d.ts +41 -3
  168. package/dist/ui/header.d.ts.map +1 -1
  169. package/dist/ui/header.js +101 -36
  170. package/dist/ui/header.js.map +1 -1
  171. package/dist/ui/legend.d.ts +15 -0
  172. package/dist/ui/legend.d.ts.map +1 -0
  173. package/dist/ui/legend.js +68 -0
  174. package/dist/ui/legend.js.map +1 -0
  175. package/dist/ui/popovers.d.ts +3 -1
  176. package/dist/ui/popovers.d.ts.map +1 -1
  177. package/dist/ui/popovers.js +62 -23
  178. package/dist/ui/popovers.js.map +1 -1
  179. package/dist/ui/search.d.ts +13 -0
  180. package/dist/ui/search.d.ts.map +1 -0
  181. package/dist/ui/search.js +192 -0
  182. package/dist/ui/search.js.map +1 -0
  183. package/dist/ui/sidebar.d.ts +32 -17
  184. package/dist/ui/sidebar.d.ts.map +1 -1
  185. package/dist/ui/sidebar.js +327 -519
  186. package/dist/ui/sidebar.js.map +1 -1
  187. package/dist/ui/stage.d.ts +56 -1
  188. package/dist/ui/stage.d.ts.map +1 -1
  189. package/dist/ui/stage.js +244 -256
  190. package/dist/ui/stage.js.map +1 -1
  191. package/dist/ui/swatch.js +1 -1
  192. package/dist/ui/swatch.js.map +1 -1
  193. package/dist/ui/transport.d.ts +12 -0
  194. package/dist/ui/transport.d.ts.map +1 -0
  195. package/dist/ui/transport.js +198 -0
  196. package/dist/ui/transport.js.map +1 -0
  197. package/dist/ui/zoom.d.ts +15 -0
  198. package/dist/ui/zoom.d.ts.map +1 -0
  199. package/dist/ui/zoom.js +30 -0
  200. package/dist/ui/zoom.js.map +1 -0
  201. package/dist/view.d.ts +94 -0
  202. package/dist/view.d.ts.map +1 -0
  203. package/dist/view.js +441 -0
  204. package/dist/view.js.map +1 -0
  205. package/package.json +95 -12
  206. package/src/bind.ts +80 -0
  207. package/src/context.ts +106 -0
  208. package/src/controller.ts +52 -1630
  209. package/src/dom.ts +30 -0
  210. package/src/element.ts +386 -619
  211. package/src/elements/actions.ts +94 -0
  212. package/src/elements/bound.ts +247 -0
  213. package/src/elements/change-detail.ts +34 -0
  214. package/src/elements/change-list.ts +51 -0
  215. package/src/elements/filters.ts +34 -0
  216. package/src/elements/legend.ts +103 -0
  217. package/src/elements/minimap.ts +75 -0
  218. package/src/elements/root.ts +86 -0
  219. package/src/elements/search.ts +38 -0
  220. package/src/elements/stage-context.ts +34 -0
  221. package/src/elements/stage.ts +285 -0
  222. package/src/elements/title.ts +34 -0
  223. package/src/elements/transport.ts +39 -0
  224. package/src/elements/view-bound.ts +118 -0
  225. package/src/elements/view-dock.ts +43 -0
  226. package/src/elements/zoom.ts +38 -0
  227. package/src/i18n.ts +20 -0
  228. package/src/icons.ts +1 -0
  229. package/src/index.ts +350 -35
  230. package/src/keyboard.ts +188 -61
  231. package/src/layout-store.ts +71 -0
  232. package/src/panel-levels.ts +53 -0
  233. package/src/panels.ts +81 -0
  234. package/src/pipeline.ts +4 -1
  235. package/src/prefs.ts +12 -4
  236. package/src/react.ts +459 -0
  237. package/src/root-element.ts +624 -0
  238. package/src/root.ts +335 -0
  239. package/src/schedule.ts +43 -0
  240. package/src/session.ts +1375 -0
  241. package/src/styles.ts +396 -263
  242. package/src/theme.ts +170 -0
  243. package/src/topics.ts +37 -0
  244. package/src/ui/change-list.ts +405 -0
  245. package/src/ui/context.ts +36 -7
  246. package/src/ui/filters.ts +79 -0
  247. package/src/ui/header.ts +129 -58
  248. package/src/ui/legend.ts +112 -0
  249. package/src/ui/popovers.ts +68 -25
  250. package/src/ui/search.ts +225 -0
  251. package/src/ui/sidebar.ts +362 -639
  252. package/src/ui/stage.ts +267 -331
  253. package/src/ui/swatch.ts +1 -1
  254. package/src/ui/transport.ts +240 -0
  255. package/src/ui/zoom.ts +45 -0
  256. package/src/view.ts +516 -0
  257. package/vscode.html-custom-data.json +726 -0
package/src/styles.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Shadow-DOM styles. Colors, fonts and motion come from the scene `Theme` as CSS custom
3
- * properties (`themeVariables`), so the chrome always matches the canvas; the static sheet
4
- * only references `var(--kg-…)`. Spacing follows a 4 / 8 px grid; borders are hairlines.
2
+ * Shadow-DOM styles, one constructable stylesheet parsed once and shared by every shadow root
3
+ * that adopts it. Colors, fonts and motion are the `--kgdiff-*` tokens a root sets (`theme.ts`);
4
+ * the sheet only references `var(--kgdiff-…)`. Spacing follows a 4 / 8 px grid; borders are
5
+ * hairlines.
5
6
  */
6
- import type { ChangeFamily, Theme } from '@kgdiff/scene';
7
+ import type { ChangeFamily } from '@kgdiff/scene';
7
8
 
8
9
  export const FAMILIES: readonly ChangeFamily[] = [
9
10
  'added',
@@ -13,64 +14,11 @@ export const FAMILIES: readonly ChangeFamily[] = [
13
14
  'identity',
14
15
  ];
15
16
 
16
- /** CSS custom properties for `theme` (a `:host` rule body). */
17
- export function themeVariables(theme: Theme): string {
18
- const dark = theme.name === 'dark';
19
- const vars: Record<string, string> = {
20
- 'color-scheme': dark ? 'dark' : 'light',
21
- '--kg-bg': theme.canvas.background,
22
- '--kg-grid': theme.canvas.grid,
23
- '--kg-panel': theme.surface.panel,
24
- '--kg-raised': theme.surface.raised,
25
- '--kg-overlay': theme.surface.overlay,
26
- '--kg-border': theme.surface.border,
27
- '--kg-border-strong': theme.surface.borderStrong,
28
- '--kg-control-border': theme.surface.controlBorder,
29
- '--kg-shadow': theme.surface.shadow,
30
- '--kg-text': theme.text.primary,
31
- '--kg-text-2': theme.text.secondary,
32
- '--kg-text-3': theme.text.muted,
33
- '--kg-accent': theme.accent.base,
34
- '--kg-accent-strong': theme.accent.strong,
35
- '--kg-accent-soft': theme.accent.soft,
36
- '--kg-accent-text': theme.accent.text,
37
- '--kg-neutral': theme.neutral.node,
38
- '--kg-neutral-stroke': theme.neutral.nodeStroke,
39
- '--kg-neutral-edge': theme.neutral.edge,
40
- '--kg-font': theme.font.family,
41
- '--kg-mono': theme.font.mono,
42
- '--kg-fast': `${theme.motion.fast}ms`,
43
- '--kg-base': `${theme.motion.base}ms`,
44
- '--kg-slow': `${theme.motion.slow}ms`,
45
- '--kg-ease': `cubic-bezier(${theme.motion.easing.join(', ')})`,
46
- // Derived interaction tints (not color-coded: hover / press / inputs / scrims).
47
- '--kg-hover': dark ? 'rgba(255,255,255,0.045)' : 'rgba(15,23,42,0.04)',
48
- '--kg-press': dark ? 'rgba(255,255,255,0.08)' : 'rgba(15,23,42,0.07)',
49
- '--kg-input': dark ? theme.surface.raised : '#F3F5F8',
50
- '--kg-selected': dark ? theme.surface.raised : '#F5F7FB',
51
- '--kg-scrim': dark ? 'rgba(5,7,11,0.56)' : 'rgba(15,23,42,0.18)',
52
- '--kg-skeleton': dark ? 'rgba(255,255,255,0.05)' : 'rgba(15,23,42,0.05)',
53
- '--kg-skeleton-hi': dark ? 'rgba(255,255,255,0.09)' : 'rgba(15,23,42,0.09)',
54
- '--kg-track': dark ? 'rgba(255,255,255,0.08)' : 'rgba(15,23,42,0.08)',
55
- '--kg-kbd': dark ? 'rgba(255,255,255,0.06)' : 'rgba(15,23,42,0.05)',
56
- };
57
- for (const f of FAMILIES) {
58
- const c = theme.family[f];
59
- vars[`--kg-${f}`] = c.base;
60
- vars[`--kg-${f}-strong`] = c.strong;
61
- vars[`--kg-${f}-soft`] = c.soft;
62
- vars[`--kg-${f}-text`] = c.text;
63
- }
64
- return Object.entries(vars)
65
- .map(([k, v]) => `${k}: ${v};`)
66
- .join(' ');
67
- }
68
-
69
17
  const familyRules = [...FAMILIES, 'neutral' as const]
70
18
  .map((f) =>
71
19
  f === 'neutral'
72
- ? `[data-family="neutral"] { --fam-base: var(--kg-neutral); --fam-soft: var(--kg-hover); --fam-text: var(--kg-text-2); }`
73
- : `[data-family="${f}"] { --fam-base: var(--kg-${f}); --fam-soft: var(--kg-${f}-soft); --fam-text: var(--kg-${f}-text); }`,
20
+ ? `[data-family="neutral"] { --fam-base: var(--kgdiff-neutral); --fam-soft: var(--kgdiff-hover); --fam-text: var(--kgdiff-text-secondary); }`
21
+ : `[data-family="${f}"] { --fam-base: var(--kgdiff-${f}); --fam-soft: var(--kgdiff-${f}-soft); --fam-text: var(--kgdiff-${f}-text); }`,
74
22
  )
75
23
  .join('\n');
76
24
 
@@ -82,11 +30,11 @@ export const STYLES = /* css */ `
82
30
  height: 100%;
83
31
  min-height: 420px;
84
32
  contain: content;
85
- font-family: var(--kg-font);
33
+ font-family: var(--kgdiff-font);
86
34
  font-size: 13px;
87
35
  line-height: 1.45;
88
- color: var(--kg-text);
89
- background: var(--kg-bg);
36
+ color: var(--kgdiff-text);
37
+ background: var(--kgdiff-background);
90
38
  -webkit-font-smoothing: antialiased;
91
39
  }
92
40
  :host([hidden]) { display: none; }
@@ -101,7 +49,7 @@ button {
101
49
  }
102
50
  button:disabled { cursor: default; opacity: 0.4; }
103
51
  :focus { outline: none; }
104
- :focus-visible { outline: 2px solid var(--kg-accent); outline-offset: 2px; }
52
+ :focus-visible { outline: 2px solid var(--kgdiff-accent); outline-offset: 2px; }
105
53
  .icon { width: 16px; height: 16px; flex: none; display: block; }
106
54
  .sr-only {
107
55
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
@@ -110,42 +58,57 @@ button:disabled { cursor: default; opacity: 0.4; }
110
58
  kbd {
111
59
  display: inline-flex; align-items: center; justify-content: center;
112
60
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: 5px;
113
- font: 500 11px/1 var(--kg-mono); color: var(--kg-text-2);
114
- background: var(--kg-kbd); box-shadow: inset 0 0 0 1px var(--kg-border), inset 0 -1px 0 var(--kg-border);
61
+ font: 500 11px/1 var(--kgdiff-font-mono); color: var(--kgdiff-text-secondary);
62
+ background: var(--kgdiff-kbd); box-shadow: inset 0 0 0 1px var(--kgdiff-border), inset 0 -1px 0 var(--kgdiff-border);
115
63
  }
116
- .scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: var(--kg-border-strong) transparent; }
64
+ .scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: var(--kgdiff-border-strong) transparent; }
117
65
 
118
66
  /* ── Root layout ─────────────────────────────────────────────────────────── */
119
67
  .root {
120
68
  position: absolute; inset: 0; display: flex; flex-direction: column; overflow: hidden;
121
- background: var(--kg-bg);
69
+ background: var(--kgdiff-background);
122
70
  }
123
- .body { position: relative; flex: 1; min-height: 0; display: flex; }
124
- .stage-wrap { position: relative; flex: 1; min-width: 0; min-height: 0; overflow: hidden; background: var(--kg-bg); container-type: inline-size; }
71
+ .body { position: relative; flex: 1; min-height: 0; display: flex; container-type: inline-size; }
72
+ .stage-wrap { position: relative; flex: 1; min-width: 0; min-height: 0; overflow: hidden; background: var(--kgdiff-background); container-type: inline-size; }
125
73
  .stage { position: absolute; inset: 0; }
126
74
  .stage canvas { outline: none; }
127
- .stage canvas:focus-visible { outline: 2px solid var(--kg-accent); outline-offset: -2px; }
75
+ .stage canvas:focus-visible { outline: 2px solid var(--kgdiff-accent); outline-offset: -2px; }
128
76
  .chrome { position: absolute; inset: 0; pointer-events: none; }
129
77
  .chrome > * { pointer-events: auto; }
78
+ /* Until a diff is ready only the page's slotted content shows. */
79
+ .chrome[data-ready="false"] > :not(.corner, .dock), .chrome[data-ready="false"] .corner > :not(slot),
80
+ .chrome[data-ready="false"] .dock > :not(slot) { display: none !important; }
81
+ /* <kgdiff-stage>: the page's content in the corners, further from the corner than the chrome. */
82
+ .corner { position: absolute; z-index: 2; display: flex; flex-direction: column; gap: 8px; max-width: calc(50% - 24px); pointer-events: none; }
83
+ .corner > * { pointer-events: auto; }
84
+ .corner.top-start { top: 12px; left: 12px; align-items: flex-start; }
85
+ .corner.top-end { top: 12px; right: 12px; align-items: flex-end; }
86
+ .corner.bottom-start { bottom: 16px; left: 16px; align-items: flex-start; }
87
+ .corner.bottom-end { bottom: 16px; right: 16px; align-items: flex-end; }
88
+ .corner > .float { position: static; }
89
+ /* A minimap slotted in replaces the stage's own: hidden while everything is in view, as it is. */
90
+ ::slotted(kgdiff-minimap:state(overview)) { display: none; }
91
+ /* <kgdiff-viewer>: the top corners sit under the title and the actions it shows. */
92
+ .stage-wrap[data-hud~="title"] .corner.top-start, .stage-wrap[data-hud~="actions"] .corner.top-end { top: 64px; }
130
93
 
131
94
  /* ── Buttons ─────────────────────────────────────────────────────────────── */
132
95
  .icon-btn {
133
96
  display: inline-flex; align-items: center; justify-content: center; flex: none;
134
- width: 32px; height: 32px; border-radius: 8px; color: var(--kg-text-2);
135
- transition: background var(--kg-fast) var(--kg-ease), color var(--kg-fast) var(--kg-ease);
97
+ width: 32px; height: 32px; border-radius: 8px; color: var(--kgdiff-text-secondary);
98
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease);
136
99
  }
137
- .icon-btn:hover { background: var(--kg-hover); color: var(--kg-text); }
138
- .icon-btn:active { background: var(--kg-press); }
139
- .icon-btn[aria-pressed="true"], .icon-btn[aria-expanded="true"] { background: var(--kg-press); color: var(--kg-text); }
100
+ .icon-btn:hover { background: var(--kgdiff-hover); color: var(--kgdiff-text); }
101
+ .icon-btn:active { background: var(--kgdiff-press); }
102
+ .icon-btn[aria-pressed="true"], .icon-btn[aria-expanded="true"] { background: var(--kgdiff-press); color: var(--kgdiff-text); }
140
103
  .icon-btn.sm { width: 28px; height: 28px; border-radius: 7px; }
141
104
  .btn {
142
105
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
143
- border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--kg-text);
144
- background: var(--kg-hover); box-shadow: inset 0 0 0 1px var(--kg-border);
145
- transition: background var(--kg-fast) var(--kg-ease), box-shadow var(--kg-fast) var(--kg-ease);
106
+ border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--kgdiff-text);
107
+ background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
108
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
146
109
  }
147
- .btn:hover { background: var(--kg-press); box-shadow: inset 0 0 0 1px var(--kg-border-strong); }
148
- .link-btn { min-height: 28px; color: var(--kg-accent-text); font-weight: 500; font-size: 12px; border-radius: 4px; }
110
+ .btn:hover { background: var(--kgdiff-press); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
111
+ .link-btn { min-height: 28px; color: var(--kgdiff-accent-text); font-weight: 500; font-size: 12px; border-radius: 4px; }
149
112
  .link-btn:hover { text-decoration: underline; text-underline-offset: 3px; }
150
113
 
151
114
  /* ── HUD: title (top-left) and actions (top-right), over the canvas ──────── */
@@ -155,40 +118,39 @@ kbd {
155
118
  position: absolute; top: 12px; left: 12px; max-width: calc(100% - 184px); height: 40px;
156
119
  display: flex; align-items: center; gap: 10px; padding: 0 14px 0 10px;
157
120
  }
158
- .hud[data-titled="false"] .header-sep { display: none; }
159
- :host([header="compact"]) .hud-title { display: none; }
121
+ .hud-title[data-titled="false"] .header-sep { display: none; }
160
122
  .hud-end { position: absolute; top: 12px; right: 12px; }
161
123
  .hud-actions { display: flex; align-items: center; gap: 2px; padding: 4px; }
162
- .brand { display: flex; align-items: center; gap: 8px; flex: none; color: var(--kg-text); }
124
+ .brand { display: flex; align-items: center; gap: 8px; flex: none; color: var(--kgdiff-text); }
163
125
  .brand-mark { width: 20px; height: 20px; display: block; }
164
126
  .brand-name { font-size: 14px; font-weight: 650; letter-spacing: -0.01em; }
165
- .header-sep { width: 1px; height: 18px; background: var(--kg-border-strong); flex: none; }
127
+ .header-sep { width: 1px; height: 18px; background: var(--kgdiff-border-strong); flex: none; }
166
128
  .versions { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; font-size: 13px; overflow: hidden; }
167
- .versions .name { font-weight: 550; color: var(--kg-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
168
- .versions .arrow { color: var(--kg-text-3); flex: none; }
129
+ .versions .name { font-weight: 550; color: var(--kgdiff-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
130
+ .versions .arrow { color: var(--kgdiff-text-muted); flex: none; }
169
131
  .versions .side { display: flex; align-items: center; gap: 6px; min-width: 0; }
170
132
  .tag {
171
133
  display: inline-flex; align-items: center; height: 20px; padding: 0 6px; border-radius: 5px; flex: none;
172
- font: 500 11px/1 var(--kg-mono); color: var(--kg-text-2); background: var(--kg-hover);
173
- box-shadow: inset 0 0 0 1px var(--kg-border); white-space: nowrap; max-width: 160px; overflow: hidden; text-overflow: ellipsis;
134
+ font: 500 11px/1 var(--kgdiff-font-mono); color: var(--kgdiff-text-secondary); background: var(--kgdiff-hover);
135
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border); white-space: nowrap; max-width: 160px; overflow: hidden; text-overflow: ellipsis;
174
136
  }
175
137
 
176
138
  .segmented {
177
139
  position: relative; display: inline-flex; align-items: center; gap: 2px; height: 32px; padding: 2px; flex: none;
178
- border-radius: 9px; background: var(--kg-hover); box-shadow: inset 0 0 0 1px var(--kg-border);
140
+ border-radius: 9px; background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
179
141
  }
180
142
  .segmented .thumb {
181
143
  position: absolute; top: 2px; bottom: 2px; left: 0; width: 0; border-radius: 7px;
182
- background: var(--kg-raised); box-shadow: 0 1px 2px rgba(0,0,0,0.12), inset 0 0 0 1px var(--kg-border-strong);
183
- transition: transform var(--kg-base) var(--kg-ease), width var(--kg-base) var(--kg-ease);
144
+ background: var(--kgdiff-raised); box-shadow: 0 1px 2px rgba(0,0,0,0.12), inset 0 0 0 1px var(--kgdiff-border-strong);
145
+ transition: transform var(--kgdiff-base) var(--kgdiff-ease), width var(--kgdiff-base) var(--kgdiff-ease);
184
146
  }
185
147
  .seg {
186
148
  position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
187
- border-radius: 7px; font-size: 12.5px; font-weight: 500; color: var(--kg-text-2); white-space: nowrap;
188
- transition: color var(--kg-fast) var(--kg-ease);
149
+ border-radius: 7px; font-size: 12.5px; font-weight: 500; color: var(--kgdiff-text-secondary); white-space: nowrap;
150
+ transition: color var(--kgdiff-fast) var(--kgdiff-ease);
189
151
  }
190
- .seg:hover { color: var(--kg-text); }
191
- .seg[aria-checked="true"] { color: var(--kg-text); }
152
+ .seg:hover { color: var(--kgdiff-text); }
153
+ .seg[aria-checked="true"] { color: var(--kgdiff-text); }
192
154
  .seg .icon { opacity: 0.9; }
193
155
 
194
156
 
@@ -196,7 +158,7 @@ ${familyRules}
196
158
 
197
159
  /* ── Floating chrome ─────────────────────────────────────────────────────── */
198
160
  .card {
199
- background: var(--kg-overlay); border-radius: 12px; box-shadow: var(--kg-shadow), 0 0 0 1px var(--kg-border);
161
+ background: var(--kgdiff-overlay); border-radius: 12px; box-shadow: var(--kgdiff-shadow), 0 0 0 1px var(--kgdiff-border);
200
162
  backdrop-filter: blur(16px) saturate(140%);
201
163
  }
202
164
  .float { position: absolute; }
@@ -204,34 +166,34 @@ ${familyRules}
204
166
  .bottom-right { bottom: 16px; right: 16px; }
205
167
  .chip {
206
168
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border-radius: 8px;
207
- font-size: 12px; font-weight: 600; color: var(--kg-text-2); letter-spacing: 0.01em;
169
+ font-size: 12px; font-weight: 600; color: var(--kgdiff-text-secondary); letter-spacing: 0.01em;
208
170
  }
209
171
 
210
172
  .map-cluster { display: flex; align-items: flex-end; gap: 8px; }
211
173
  .zoom-bar { display: flex; flex-direction: column; padding: 2px; gap: 2px; }
212
174
  .zoom-bar .icon-btn { width: 30px; height: 30px; }
213
- .minimap-host { position: relative; width: 184px; height: 128px; border-radius: 12px; box-shadow: 0 0 0 1px var(--kg-border);
214
- animation: fade var(--kg-base) var(--kg-ease); }
175
+ .minimap-host { position: relative; width: 184px; height: 128px; border-radius: 12px; box-shadow: 0 0 0 1px var(--kgdiff-border);
176
+ animation: fade var(--kgdiff-base) var(--kgdiff-ease); }
215
177
  /* Everything is in view: the minimap has nothing to add. */
216
178
  .minimap-host[data-hidden="true"] { display: none; }
217
179
  .minimap-host canvas { border-radius: 12px; }
218
180
 
219
181
  .legend { padding: 6px; min-width: 0; }
220
182
  .legend-head { display: flex; align-items: center; gap: 6px; height: 28px; padding: 0 6px 0 8px; font-size: 11px; font-weight: 600;
221
- letter-spacing: 0.06em; text-transform: uppercase; color: var(--kg-text-3); border-radius: 7px; width: 100%; }
222
- .legend-head:hover { color: var(--kg-text-2); background: var(--kg-hover); }
223
- .legend-head .icon { width: 14px; height: 14px; margin-left: auto; transition: transform var(--kg-base) var(--kg-ease); }
183
+ letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted); border-radius: 7px; width: 100%; }
184
+ .legend-head:hover { color: var(--kgdiff-text-secondary); background: var(--kgdiff-hover); }
185
+ .legend-head .icon { width: 14px; height: 14px; margin-left: auto; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
224
186
  .legend[data-open="false"] .legend-head .icon { transform: rotate(180deg); }
225
187
  .legend[data-open="false"] .legend-body { display: none; }
226
188
  .legend[data-open="false"] { padding: 2px; }
227
189
  .legend-body { display: grid; gap: 2px; padding: 2px 4px 4px; }
228
190
  .legend-row { display: grid; grid-template-columns: 22px auto; align-items: center; column-gap: 8px; height: 26px; padding: 0 10px 0 4px; border-radius: 6px; cursor: default; }
229
- .legend-row .name { font-size: 12px; font-weight: 550; color: var(--kg-text); }
191
+ .legend-row .name { font-size: 12px; font-weight: 550; color: var(--kgdiff-text); }
230
192
  .swatch { width: 22px; height: 22px; display: block; }
231
193
  .mini-legend { display: none; }
232
194
  .stage-wrap[data-legend="mini"] .mini-legend {
233
195
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; bottom: 8px; left: 12px; max-width: calc(100% - 24px);
234
- font-size: 11px; line-height: 16px; color: var(--kg-text-2);
196
+ font-size: 11px; line-height: 16px; color: var(--kgdiff-text-secondary);
235
197
  }
236
198
  .mini-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
237
199
  .mini-item .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--fam-base); }
@@ -243,7 +205,7 @@ ${familyRules}
243
205
  font-size: 12.5px; font-weight: 550;
244
206
  }
245
207
  .caption .text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
246
- .caption .count { color: var(--kg-text-3); font-weight: 500; font-variant-numeric: tabular-nums; flex: none; }
208
+ .caption .count { color: var(--kgdiff-text-muted); font-weight: 500; font-variant-numeric: tabular-nums; flex: none; }
247
209
  .caption[data-empty="true"] { display: none; }
248
210
 
249
211
  .glyph-box {
@@ -258,24 +220,24 @@ ${familyRules}
258
220
  }
259
221
  .dock > * { pointer-events: auto; }
260
222
  .dock-bar { display: flex; align-items: center; gap: 4px; height: 44px; padding: 0 6px; max-width: 100%; }
261
- .dock-sep { width: 1px; height: 20px; margin: 0 2px; flex: none; background: var(--kg-border-strong); }
223
+ .dock-sep { width: 1px; height: 20px; margin: 0 2px; flex: none; background: var(--kgdiff-border-strong); }
262
224
  .scope-btn {
263
225
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px; border-radius: 9px; flex: none;
264
- font-size: 12.5px; font-weight: 500; color: var(--kg-text-2); white-space: nowrap;
265
- transition: background var(--kg-fast) var(--kg-ease), color var(--kg-fast) var(--kg-ease), box-shadow var(--kg-fast) var(--kg-ease);
226
+ font-size: 12.5px; font-weight: 500; color: var(--kgdiff-text-secondary); white-space: nowrap;
227
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
266
228
  }
267
- .scope-btn:hover { color: var(--kg-text); background: var(--kg-hover); }
268
- .scope-btn[aria-pressed="true"] { color: var(--kg-accent-text); background: var(--kg-accent-soft); box-shadow: inset 0 0 0 1px var(--kg-accent-soft); }
229
+ .scope-btn:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
230
+ .scope-btn[aria-pressed="true"] { color: var(--kgdiff-accent-text); background: var(--kgdiff-accent-soft); box-shadow: inset 0 0 0 1px var(--kgdiff-accent-soft); }
269
231
  .scope-btn[aria-disabled="true"] { cursor: default; }
270
- .context { display: inline-flex; align-items: center; gap: 8px; padding-left: 2px; flex: none; animation: reveal var(--kg-base) var(--kg-ease); }
232
+ .context { display: inline-flex; align-items: center; gap: 8px; padding-left: 2px; flex: none; animation: reveal var(--kgdiff-base) var(--kgdiff-ease); }
271
233
  @keyframes reveal { from { opacity: 0; transform: translateX(-6px); } }
272
- .context .label { font-size: 12px; font-weight: 550; color: var(--kg-text-3); white-space: nowrap; }
273
- .context .opts { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px; background: var(--kg-hover); }
274
- .context .opt { width: 28px; height: 28px; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--kg-text-2); font-variant-numeric: tabular-nums; }
275
- .context .opt:hover { color: var(--kg-text); }
276
- .context .opt[aria-checked="true"] { background: var(--kg-raised); color: var(--kg-text); box-shadow: inset 0 0 0 1px var(--kg-border-strong); }
277
- .context .expand { display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 10px 0 8px; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--kg-text-2); box-shadow: inset 0 0 0 1px var(--kg-border-strong); font-variant-numeric: tabular-nums; }
278
- .context .expand:hover:not(:disabled) { color: var(--kg-text); background: var(--kg-hover); }
234
+ .context .label { font-size: 12px; font-weight: 550; color: var(--kgdiff-text-muted); white-space: nowrap; }
235
+ .context .opts { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px; background: var(--kgdiff-hover); }
236
+ .context .opt { width: 28px; height: 28px; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--kgdiff-text-secondary); font-variant-numeric: tabular-nums; }
237
+ .context .opt:hover { color: var(--kgdiff-text); }
238
+ .context .opt[aria-checked="true"] { background: var(--kgdiff-raised); color: var(--kgdiff-text); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
239
+ .context .expand { display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 10px 0 8px; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--kgdiff-text-secondary); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); font-variant-numeric: tabular-nums; }
240
+ .context .expand:hover:not(:disabled) { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
279
241
  .context .expand .icon { width: 14px; height: 14px; }
280
242
  /* Narrower canvases keep the dock clear of the legend and the minimap: labels give way to icons
281
243
  (each control keeps its name as a tooltip). */
@@ -289,18 +251,18 @@ ${familyRules}
289
251
  position: absolute; top: 0; bottom: 0; left: 0; width: 24px; margin-left: -12px; cursor: ew-resize; pointer-events: auto;
290
252
  touch-action: none; display: flex; justify-content: center;
291
253
  }
292
- .swipe .divider::before { content: ''; width: 1.5px; height: 100%; background: var(--kg-accent); opacity: 0.85; box-shadow: 0 0 12px var(--kg-accent-soft); }
254
+ .swipe .divider::before { content: ''; width: 1.5px; height: 100%; background: var(--kgdiff-accent); opacity: 0.85; box-shadow: 0 0 12px var(--kgdiff-accent-soft); }
293
255
  .swipe .handle {
294
256
  position: absolute; top: 50%; left: 50%; width: 32px; height: 32px; margin: -16px 0 0 -16px; border-radius: 50%;
295
- display: flex; align-items: center; justify-content: center; gap: 0; color: var(--kg-accent-text);
296
- background: var(--kg-overlay); box-shadow: var(--kg-shadow), 0 0 0 1.5px var(--kg-accent);
257
+ display: flex; align-items: center; justify-content: center; gap: 0; color: var(--kgdiff-accent-text);
258
+ background: var(--kgdiff-overlay); box-shadow: var(--kgdiff-shadow), 0 0 0 1.5px var(--kgdiff-accent);
297
259
  backdrop-filter: blur(16px);
298
- transition: transform var(--kg-fast) var(--kg-ease);
260
+ transition: transform var(--kgdiff-fast) var(--kgdiff-ease);
299
261
  }
300
262
  .swipe .handle .icon { width: 12px; height: 12px; }
301
263
  .swipe .divider:hover .handle, .swipe .divider.dragging .handle { transform: scale(1.08); }
302
264
  .swipe .divider:focus-visible { outline: none; }
303
- .swipe .divider:focus-visible .handle { box-shadow: var(--kg-shadow), 0 0 0 2px var(--kg-accent), 0 0 0 5px var(--kg-accent-soft); }
265
+ .swipe .divider:focus-visible .handle { box-shadow: var(--kgdiff-shadow), 0 0 0 2px var(--kgdiff-accent), 0 0 0 5px var(--kgdiff-accent-soft); }
304
266
  /* Below the HUD's title and actions. */
305
267
  .swipe .side-chip { position: absolute; top: 64px; pointer-events: none; }
306
268
  .swipe .side-chip.before { left: 12px; }
@@ -311,18 +273,18 @@ ${familyRules}
311
273
  width: min(600px, calc(100% - 464px)); min-width: min(360px, 100%);
312
274
  display: flex; align-items: center; gap: 4px; height: 48px; padding: 0 8px;
313
275
  }
314
- .transport .play { width: 36px; height: 36px; border-radius: 10px; color: var(--kg-text); }
276
+ .transport .play { width: 36px; height: 36px; border-radius: 10px; color: var(--kgdiff-text); }
315
277
  .transport .play .icon { width: 18px; height: 18px; }
316
- .transport .pos { flex: none; min-width: 64px; text-align: center; font-size: 12px; font-weight: 550; color: var(--kg-text-2); font-variant-numeric: tabular-nums; }
317
- .transport .speed { flex: none; height: 28px; min-width: 44px; padding: 0 8px; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--kg-text-2); font-variant-numeric: tabular-nums; box-shadow: inset 0 0 0 1px var(--kg-border); }
318
- .transport .speed:hover { color: var(--kg-text); background: var(--kg-hover); }
278
+ .transport .pos { flex: none; min-width: 64px; text-align: center; font-size: 12px; font-weight: 550; color: var(--kgdiff-text-secondary); font-variant-numeric: tabular-nums; }
279
+ .transport .speed { flex: none; height: 28px; min-width: 44px; padding: 0 8px; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--kgdiff-text-secondary); font-variant-numeric: tabular-nums; box-shadow: inset 0 0 0 1px var(--kgdiff-border); }
280
+ .transport .speed:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
319
281
  .scrubber { position: relative; flex: 1; height: 36px; margin: 0 8px; cursor: pointer; touch-action: none; border-radius: 6px; }
320
- .scrubber .track { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 2px; background: var(--kg-track); overflow: hidden; }
321
- .scrubber .fill { position: absolute; left: 0; top: 0; bottom: 0; width: 100%; transform-origin: left; background: var(--kg-accent); opacity: 0.85; }
282
+ .scrubber .track { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 2px; background: var(--kgdiff-track); overflow: hidden; }
283
+ .scrubber .fill { position: absolute; left: 0; top: 0; bottom: 0; width: 100%; transform-origin: left; background: var(--kgdiff-accent); opacity: 0.85; }
322
284
  .scrubber .ticks { position: absolute; left: 0; right: 0; top: 50%; height: 14px; margin-top: -7px; width: 100%; pointer-events: none; overflow: visible; }
323
285
  .scrubber .knob {
324
286
  position: absolute; top: 50%; left: 0; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%;
325
- background: var(--kg-text); box-shadow: 0 1px 4px rgba(0,0,0,0.35), 0 0 0 3px var(--kg-accent-soft); pointer-events: none;
287
+ background: var(--kgdiff-text); box-shadow: 0 1px 4px rgba(0,0,0,0.35), 0 0 0 3px var(--kgdiff-accent-soft); pointer-events: none;
326
288
  }
327
289
  .scrubber:focus-visible { outline-offset: 0; }
328
290
  .scrub-tip {
@@ -332,25 +294,25 @@ ${familyRules}
332
294
  }
333
295
  .finished {
334
296
  display: flex; align-items: center; gap: 10px;
335
- height: 36px; padding: 0 6px 0 12px; font-size: 12.5px; font-weight: 550; color: var(--kg-text);
336
- animation: rise var(--kg-base) var(--kg-ease);
297
+ height: 36px; padding: 0 6px 0 12px; font-size: 12.5px; font-weight: 550; color: var(--kgdiff-text);
298
+ animation: rise var(--kgdiff-base) var(--kgdiff-ease);
337
299
  }
338
- .finished .icon { color: var(--kg-accent-text); }
300
+ .finished .icon { color: var(--kgdiff-accent-text); }
339
301
  @keyframes rise { from { opacity: 0; transform: translateY(6px); } }
340
302
 
341
303
  /* Tooltip */
342
304
  .tooltip {
343
305
  position: absolute; left: 0; top: 0; z-index: 4; width: max-content; max-width: 300px; padding: 10px 12px; pointer-events: none;
344
- border-radius: 10px; opacity: 0; transition: opacity var(--kg-fast) var(--kg-ease);
306
+ border-radius: 10px; opacity: 0; transition: opacity var(--kgdiff-fast) var(--kgdiff-ease);
345
307
  }
346
308
  .tooltip[data-open="true"] { opacity: 1; }
347
- .tooltip .t-label { font-size: 13px; font-weight: 600; color: var(--kg-text); overflow-wrap: anywhere; }
348
- .tooltip .t-old { font-size: 12px; color: var(--kg-text-3); text-decoration: line-through; overflow-wrap: anywhere; }
349
- .tooltip .t-id { margin-top: 2px; font: 11px/1.4 var(--kg-mono); color: var(--kg-text-3); overflow-wrap: anywhere; }
309
+ .tooltip .t-label { font-size: 13px; font-weight: 600; color: var(--kgdiff-text); overflow-wrap: anywhere; }
310
+ .tooltip .t-old { font-size: 12px; color: var(--kgdiff-text-muted); text-decoration: line-through; overflow-wrap: anywhere; }
311
+ .tooltip .t-id { margin-top: 2px; font: 11px/1.4 var(--kgdiff-font-mono); color: var(--kgdiff-text-muted); overflow-wrap: anywhere; }
350
312
  .tooltip .t-status { margin-top: 8px; display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 550; color: var(--fam-text); }
351
313
  .tooltip .t-status .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--fam-base); flex: none; }
352
- .tooltip .t-sum { margin-top: 4px; font-size: 12px; color: var(--kg-text-2); overflow-wrap: anywhere; }
353
- .tooltip .t-hint { margin-top: 6px; font-size: 11px; color: var(--kg-text-3); }
314
+ .tooltip .t-sum { margin-top: 4px; font-size: 12px; color: var(--kgdiff-text-secondary); overflow-wrap: anywhere; }
315
+ .tooltip .t-hint { margin-top: 6px; font-size: 11px; color: var(--kgdiff-text-muted); }
354
316
 
355
317
  /* States (loading / identical / error / empty) */
356
318
  .state {
@@ -358,36 +320,36 @@ ${familyRules}
358
320
  pointer-events: none;
359
321
  }
360
322
  .state > * { pointer-events: auto; }
361
- .state.blocking { background: var(--kg-bg); }
323
+ .state.blocking { background: var(--kgdiff-background); }
362
324
  .state-card { width: min(380px, 100%); padding: 24px; text-align: left; }
363
325
  .state-card .state-icon {
364
326
  width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center;
365
- color: var(--kg-accent-text); background: var(--kg-accent-soft); margin-bottom: 16px;
327
+ color: var(--kgdiff-accent-text); background: var(--kgdiff-accent-soft); margin-bottom: 16px;
366
328
  }
367
329
  .state-card .state-icon .icon { width: 20px; height: 20px; }
368
- .state-card.error .state-icon { color: var(--kg-removed-text); background: var(--kg-removed-soft); }
369
- .state-card.ok .state-icon { color: var(--kg-added-text); background: var(--kg-added-soft); }
330
+ .state-card.error .state-icon { color: var(--kgdiff-removed-text); background: var(--kgdiff-removed-soft); }
331
+ .state-card.ok .state-icon { color: var(--kgdiff-added-text); background: var(--kgdiff-added-soft); }
370
332
  .state-card h2 { margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
371
- .state-card p { margin: 6px 0 0; color: var(--kg-text-2); font-size: 13px; }
372
- .state-card details { margin-top: 16px; font-size: 12px; color: var(--kg-text-2); }
373
- .state-card summary { cursor: pointer; font-weight: 550; color: var(--kg-text-2); border-radius: 4px; width: max-content; }
333
+ .state-card p { margin: 6px 0 0; color: var(--kgdiff-text-secondary); font-size: 13px; }
334
+ .state-card details { margin-top: 16px; font-size: 12px; color: var(--kgdiff-text-secondary); }
335
+ .state-card summary { cursor: pointer; font-weight: 550; color: var(--kgdiff-text-secondary); border-radius: 4px; width: max-content; }
374
336
  .state-card pre {
375
- margin: 8px 0 0; max-height: 200px; padding: 10px 12px; border-radius: 8px; background: var(--kg-input);
376
- font: 11.5px/1.5 var(--kg-mono); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--kg-text-2);
377
- box-shadow: inset 0 0 0 1px var(--kg-border);
337
+ margin: 8px 0 0; max-height: 200px; padding: 10px 12px; border-radius: 8px; background: var(--kgdiff-input);
338
+ font: 11.5px/1.5 var(--kgdiff-font-mono); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--kgdiff-text-secondary);
339
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border);
378
340
  }
379
341
  .steps { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 10px; }
380
- .steps li { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--kg-text-3); transition: color var(--kg-base) var(--kg-ease); }
381
- .steps li[data-state="active"] { color: var(--kg-text); font-weight: 550; }
382
- .steps li[data-state="done"] { color: var(--kg-text-2); }
383
- .steps .mark { width: 18px; height: 18px; flex: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0 1.5px var(--kg-border-strong); }
384
- .steps li[data-state="done"] .mark { background: var(--kg-accent-soft); box-shadow: none; color: var(--kg-accent-text); }
342
+ .steps li { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--kgdiff-text-muted); transition: color var(--kgdiff-base) var(--kgdiff-ease); }
343
+ .steps li[data-state="active"] { color: var(--kgdiff-text); font-weight: 550; }
344
+ .steps li[data-state="done"] { color: var(--kgdiff-text-secondary); }
345
+ .steps .mark { width: 18px; height: 18px; flex: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0 1.5px var(--kgdiff-border-strong); }
346
+ .steps li[data-state="done"] .mark { background: var(--kgdiff-accent-soft); box-shadow: none; color: var(--kgdiff-accent-text); }
385
347
  .steps li[data-state="done"] .mark .icon { width: 12px; height: 12px; }
386
- .steps li[data-state="active"] .mark { box-shadow: inset 0 0 0 1.5px var(--kg-accent-soft); border-top: 1.5px solid var(--kg-accent); animation: spin 0.8s linear infinite; }
348
+ .steps li[data-state="active"] .mark { box-shadow: inset 0 0 0 1.5px var(--kgdiff-accent-soft); border-top: 1.5px solid var(--kgdiff-accent); animation: spin 0.8s linear infinite; }
387
349
  @keyframes spin { to { transform: rotate(360deg); } }
388
350
  .skeleton-grid {
389
351
  position: absolute; inset: 0;
390
- background-image: radial-gradient(var(--kg-skeleton-hi) 1.2px, transparent 1.4px); background-size: 24px 24px;
352
+ background-image: radial-gradient(var(--kgdiff-skeleton-hi) 1.2px, transparent 1.4px); background-size: 24px 24px;
391
353
  mask-image: radial-gradient(ellipse at center, #000 20%, transparent 75%);
392
354
  animation: breathe 2.4s ease-in-out infinite;
393
355
  }
@@ -395,47 +357,66 @@ ${familyRules}
395
357
 
396
358
  /* ── Sidebar ─────────────────────────────────────────────────────────────── */
397
359
  .sidebar {
398
- position: relative; z-index: 2; flex: none; width: 360px; display: flex; overflow: hidden;
399
- background: var(--kg-panel); border-left: 1px solid var(--kg-border);
400
- transition: width var(--kg-base) var(--kg-ease), border-color var(--kg-base) var(--kg-ease);
360
+ /* The canvas keeps at least 320 px next to it. */
361
+ --sb-w: min(var(--sidebar-w, 360px), max(280px, 100cqw - 320px));
362
+ position: relative; z-index: 2; flex: none; width: var(--sb-w); display: flex; overflow: hidden;
363
+ background: var(--kgdiff-panel); border-left: 1px solid var(--kgdiff-border);
364
+ transition: width var(--kgdiff-base) var(--kgdiff-ease), border-color var(--kgdiff-base) var(--kgdiff-ease);
401
365
  }
366
+ .sidebar.dragging { transition: none; }
402
367
  .sidebar[data-open="false"] { width: 0; border-left-color: transparent; }
403
- .sidebar-inner { width: 360px; flex: none; display: flex; flex-direction: column; min-height: 0; }
368
+ .sidebar-inner { width: var(--sb-w); flex: none; display: flex; flex-direction: column; min-height: 0; }
404
369
  .sheet-handle { display: none; }
405
- .sb-head { flex: none; padding: 12px 12px 8px; display: grid; gap: 10px; border-bottom: 1px solid var(--kg-border); }
370
+ /* Separators: the sidebar's width (its left edge) and the list / detail split. */
371
+ .sb-resize { position: absolute; z-index: 3; top: 0; bottom: 0; left: 0; width: 6px; cursor: ew-resize; touch-action: none; }
372
+ .sb-split { position: relative; z-index: 1; flex: none; height: 0; cursor: ns-resize; touch-action: none; }
373
+ .sb-split::before { content: ""; position: absolute; left: 0; right: 0; top: -4px; height: 8px; }
374
+ .sb-resize:hover, .sb-resize:focus-visible, .sidebar.dragging .sb-resize { box-shadow: inset 2px 0 0 var(--kgdiff-accent); }
375
+ .sb-split:hover::after, .sb-split:focus-visible::after {
376
+ content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--kgdiff-accent);
377
+ }
378
+ .sb-resize:focus-visible, .sb-split:focus-visible { outline: none; }
379
+ .sb-head { flex: none; padding: 12px 12px 8px; display: grid; gap: 10px; border-bottom: 1px solid var(--kgdiff-border); }
380
+ .sb-row { display: flex; align-items: center; gap: 2px; min-width: 0; }
381
+ .sb-row > .sb-title { flex: 1; min-width: 0; }
382
+ .sb-row > .icon-btn { width: 24px; height: 24px; border-radius: 6px; }
383
+ .list-toggle .icon { transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
384
+ .list-toggle[aria-expanded="false"] .icon { transform: rotate(180deg); }
385
+ .filters-btn[data-active="true"] { color: var(--kgdiff-accent-text); }
386
+ .filters-wrap:has(> .filters[hidden]) { display: none; }
406
387
  .sb-title { display: flex; align-items: center; gap: 8px; height: 24px; padding: 0 2px; }
407
388
  .sb-title h2 { margin: 0; font-size: 13px; font-weight: 650; }
408
389
  .count-badge {
409
390
  display: inline-flex; align-items: center; height: 18px; padding: 0 6px; border-radius: 9px; font-size: 11px; font-weight: 600;
410
- color: var(--kg-text-2); background: var(--kg-hover); box-shadow: inset 0 0 0 1px var(--kg-border); font-variant-numeric: tabular-nums;
391
+ color: var(--kgdiff-text-secondary); background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border); font-variant-numeric: tabular-nums;
411
392
  }
412
- .progress { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--kg-text-3); font-variant-numeric: tabular-nums; }
413
- .progress .bar { width: 64px; height: 4px; border-radius: 2px; background: var(--kg-track); overflow: hidden; }
414
- .progress .bar > div { height: 100%; width: 100%; transform-origin: left; background: var(--kg-accent); border-radius: 2px; transition: transform var(--kg-base) var(--kg-ease); }
415
- .progress[data-complete="true"] { color: var(--kg-accent-text); }
393
+ .progress { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--kgdiff-text-muted); font-variant-numeric: tabular-nums; }
394
+ .progress .bar { width: 64px; height: 4px; border-radius: 2px; background: var(--kgdiff-track); overflow: hidden; }
395
+ .progress .bar > div { height: 100%; width: 100%; transform-origin: left; background: var(--kgdiff-accent); border-radius: 2px; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
396
+ .progress[data-complete="true"] { color: var(--kgdiff-accent-text); }
416
397
 
417
- .search { position: relative; display: flex; align-items: center; height: 32px; border-radius: 8px; background: var(--kg-input);
418
- box-shadow: inset 0 0 0 1px var(--kg-border); transition: box-shadow var(--kg-fast) var(--kg-ease); }
419
- .search:focus-within { box-shadow: inset 0 0 0 1px var(--kg-accent), 0 0 0 3px var(--kg-accent-soft); }
420
- .search > .icon { position: absolute; left: 10px; color: var(--kg-text-3); pointer-events: none; }
398
+ .search { position: relative; display: flex; align-items: center; height: 32px; border-radius: 8px; background: var(--kgdiff-input);
399
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border); transition: box-shadow var(--kgdiff-fast) var(--kgdiff-ease); }
400
+ .search:focus-within { box-shadow: inset 0 0 0 1px var(--kgdiff-accent), 0 0 0 3px var(--kgdiff-accent-soft); }
401
+ .search > .icon { position: absolute; left: 10px; color: var(--kgdiff-text-muted); pointer-events: none; }
421
402
  .search input {
422
- flex: 1; min-width: 0; height: 100%; padding: 0 64px 0 32px; border: 0; background: transparent; font-size: 13px; color: var(--kg-text);
403
+ flex: 1; min-width: 0; height: 100%; padding: 0 64px 0 32px; border: 0; background: transparent; font-size: 13px; color: var(--kgdiff-text);
423
404
  }
424
- .search input::placeholder { color: var(--kg-text-3); }
405
+ .search input::placeholder { color: var(--kgdiff-text-muted); }
425
406
  .search input::-webkit-search-cancel-button { display: none; }
426
407
  .search kbd { position: absolute; right: 8px; }
427
- .search .clear { position: absolute; right: 2px; width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: var(--kg-text-3); }
428
- .search .clear:hover { color: var(--kg-text); background: var(--kg-hover); }
408
+ .search .clear { position: absolute; right: 2px; width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: var(--kgdiff-text-muted); }
409
+ .search .clear:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
429
410
 
430
411
  .filters { display: flex; flex-wrap: wrap; gap: 4px; }
431
412
  .fchip {
432
413
  display: inline-flex; align-items: center; gap: 5px; height: 28px; padding: 0 8px 0 7px; border-radius: 14px;
433
- font-size: 11.5px; font-weight: 550; color: var(--kg-text-2); box-shadow: inset 0 0 0 1px var(--kg-border);
434
- transition: background var(--kg-fast) var(--kg-ease), box-shadow var(--kg-fast) var(--kg-ease), color var(--kg-fast) var(--kg-ease);
414
+ font-size: 11.5px; font-weight: 550; color: var(--kgdiff-text-secondary); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
415
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease);
435
416
  }
436
- .fchip:hover { background: var(--kg-hover); color: var(--kg-text); }
417
+ .fchip:hover { background: var(--kgdiff-hover); color: var(--kgdiff-text); }
437
418
  .fchip .swatch { width: 16px; height: 16px; margin: 0 -1px; }
438
- .fchip .n { color: var(--kg-text-3); font-variant-numeric: tabular-nums; font-weight: 500; }
419
+ .fchip .n { color: var(--kgdiff-text-muted); font-variant-numeric: tabular-nums; font-weight: 500; }
439
420
  .fchip[aria-pressed="true"] { background: var(--fam-soft); color: var(--fam-text); box-shadow: inset 0 0 0 1px var(--fam-base); }
440
421
  .fchip[aria-pressed="true"] .n { color: var(--fam-text); opacity: 0.8; }
441
422
  .fchip:disabled { opacity: 0.35; }
@@ -443,73 +424,89 @@ ${familyRules}
443
424
  .list-wrap { position: relative; flex: 1 1 50%; min-height: 96px; display: flex; flex-direction: column; }
444
425
  .list { position: relative; flex: 1; min-height: 0; outline: none; }
445
426
  .list:focus-visible { outline: none; }
446
- .list:focus-visible:not(:has(.row[aria-selected="true"])) { box-shadow: inset 0 0 0 2px var(--kg-accent); }
447
- .list:focus-visible .row[aria-selected="true"] { box-shadow: inset 0 0 0 2px var(--kg-accent); }
427
+ .list:focus-visible:not(:has(.row[aria-selected="true"])) { box-shadow: inset 0 0 0 2px var(--kgdiff-accent); }
428
+ .list:focus-visible .row[aria-selected="true"] { box-shadow: inset 0 0 0 2px var(--kgdiff-accent); }
448
429
  .list-inner { position: relative; width: 100%; }
449
430
  .row {
450
431
  position: absolute; left: 8px; right: 8px; height: 52px; display: flex; align-items: center; gap: 10px; padding: 0 6px 0 14px;
451
432
  border-radius: 8px; cursor: pointer; user-select: none; -webkit-user-select: none;
452
- transition: background var(--kg-fast) var(--kg-ease), box-shadow var(--kg-fast) var(--kg-ease);
433
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
453
434
  }
454
435
  .row::before { content: ''; position: absolute; left: 4px; top: 10px; bottom: 10px; width: 3px; border-radius: 2px; background: var(--fam-base); }
455
- .row:hover { background: var(--kg-hover); }
456
- .row[aria-selected="true"] { background: var(--kg-selected); box-shadow: inset 0 0 0 1px var(--kg-accent), 0 1px 2px rgba(0,0,0,0.06); }
436
+ .row:hover { background: var(--kgdiff-hover); }
437
+ .row[aria-selected="true"] { background: var(--kgdiff-selected); box-shadow: inset 0 0 0 1px var(--kgdiff-accent), 0 1px 2px rgba(0,0,0,0.06); }
457
438
  .row .txt { flex: 1; min-width: 0; display: grid; gap: 1px; }
458
- .row .title { font-size: 13px; font-weight: 500; color: var(--kg-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
459
- .row .sub { font-size: 12px; color: var(--kg-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-height: 17px; }
460
- .row[data-reviewed="true"] .title { color: var(--kg-text-2); }
439
+ .row .title { font-size: 13px; font-weight: 500; color: var(--kgdiff-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
440
+ .row .sub { font-size: 12px; color: var(--kgdiff-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-height: 17px; }
441
+ .row[data-reviewed="true"] .title { color: var(--kgdiff-text-secondary); }
461
442
  .row[data-reviewed="true"] .glyph-box { opacity: 0.7; }
462
443
  .rv {
463
444
  flex: none; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer;
464
445
  }
465
446
  .rv .ring {
466
447
  width: 18px; height: 18px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
467
- box-shadow: inset 0 0 0 1.5px var(--kg-control-border); color: transparent;
468
- transition: background var(--kg-fast) var(--kg-ease), box-shadow var(--kg-fast) var(--kg-ease), color var(--kg-fast) var(--kg-ease), transform var(--kg-fast) var(--kg-ease);
448
+ box-shadow: inset 0 0 0 1.5px var(--kgdiff-control-border); color: transparent;
449
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease), transform var(--kgdiff-fast) var(--kgdiff-ease);
469
450
  }
470
451
  .rv .ring .icon { width: 12px; height: 12px; }
471
- .rv:hover .ring { box-shadow: inset 0 0 0 1.5px var(--kg-accent); color: var(--kg-text-3); }
472
- .rv[data-pressed="true"] .ring { background: var(--kg-accent); box-shadow: none; color: var(--kg-bg); }
452
+ .rv:hover .ring { box-shadow: inset 0 0 0 1.5px var(--kgdiff-accent); color: var(--kgdiff-text-muted); }
453
+ .rv[data-pressed="true"] .ring { background: var(--kgdiff-accent); box-shadow: none; color: var(--kgdiff-background); }
473
454
  .rv[data-pressed="true"]:active .ring { transform: scale(0.9); }
474
455
  .group {
475
456
  position: absolute; left: 0; right: 0; height: 32px; display: flex; align-items: center; gap: 8px; padding: 10px 16px 0 22px;
476
- font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kg-text-3);
457
+ font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted);
477
458
  }
478
459
  .group .n { font-weight: 500; letter-spacing: 0; font-variant-numeric: tabular-nums; }
479
460
  .sticky {
480
- position: absolute; top: 0; left: 0; right: 0; z-index: 1; height: 32px; background: var(--kg-panel);
481
- box-shadow: 0 1px 0 var(--kg-border); pointer-events: none;
461
+ position: absolute; top: 0; left: 0; right: 0; z-index: 1; height: 32px; background: var(--kgdiff-panel);
462
+ box-shadow: 0 1px 0 var(--kgdiff-border); pointer-events: none;
482
463
  }
483
464
  .sticky .group { position: static; height: 32px; padding-top: 0; }
484
465
  .sticky[data-shadow="false"] { box-shadow: none; }
485
- .list-empty { padding: 32px 24px; text-align: center; color: var(--kg-text-3); font-size: 12.5px; display: grid; gap: 10px; justify-items: center; }
466
+ .list-empty { padding: 32px 24px; text-align: center; color: var(--kgdiff-text-muted); font-size: 12.5px; display: grid; gap: 10px; justify-items: center; }
486
467
  .skeleton-row { height: 52px; margin: 0 8px; padding: 0 14px; display: flex; align-items: center; gap: 10px; }
487
- .skeleton-row .b { flex: 1; max-width: 70%; height: 10px; border-radius: 5px; background: var(--kg-skeleton); }
468
+ .skeleton-row .b { flex: 1; max-width: 70%; height: 10px; border-radius: 5px; background: var(--kgdiff-skeleton); }
488
469
  .skeleton-row:nth-child(even) .b { max-width: 52%; }
489
- .skeleton-row .g { width: 20px; height: 20px; border-radius: 6px; background: var(--kg-skeleton); flex: none; }
470
+ .skeleton-row .g { width: 20px; height: 20px; border-radius: 6px; background: var(--kgdiff-skeleton); flex: none; }
490
471
  .shimmer { animation: breathe 1.6s ease-in-out infinite; }
491
472
 
492
473
  .results { padding: 4px 0 8px; }
493
- .results-head { padding: 8px 16px 6px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kg-text-3); }
474
+ .results-head { padding: 8px 16px 6px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted); }
494
475
  .result {
495
476
  display: flex; align-items: center; gap: 10px; width: calc(100% - 16px); margin: 0 8px; height: 44px; padding: 0 10px; border-radius: 8px;
496
477
  text-align: left;
497
478
  }
498
- .result:hover, .result[aria-selected="true"] { background: var(--kg-hover); }
499
- .result[aria-selected="true"] { box-shadow: inset 0 0 0 1px var(--kg-border-strong); }
479
+ .result:hover, .result[aria-selected="true"] { background: var(--kgdiff-hover); }
480
+ .result[aria-selected="true"] { box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
500
481
  .result .sdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--fam-base); }
501
- .result[data-family="neutral"] .sdot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--kg-neutral-stroke); }
482
+ .result[data-family="neutral"] .sdot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--kgdiff-neutral-stroke); }
502
483
  .result .txt { flex: 1; min-width: 0; display: grid; }
503
484
  .result .title { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
504
- .result .sub { font: 11px/1.4 var(--kg-mono); color: var(--kg-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
505
- .result mark { background: var(--kg-accent-soft); color: inherit; border-radius: 3px; padding: 0 1px; }
485
+ .result .sub { font: 11px/1.4 var(--kgdiff-font-mono); color: var(--kgdiff-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
486
+ .result mark { background: var(--kgdiff-accent-soft); color: inherit; border-radius: 3px; padding: 0 1px; }
506
487
  .result .status { font-size: 11.5px; color: var(--fam-text); flex: none; }
507
488
 
508
489
  /* ── Detail panel ────────────────────────────────────────────────────────── */
509
- .detail { flex: 0 1 auto; max-height: 58%; min-height: 0; display: flex; flex-direction: column; border-top: 1px solid var(--kg-border); background: var(--kg-panel); }
490
+ .detail-wrap { flex: 0 1 auto; max-height: 58%; min-height: 0; display: flex; flex-direction: column; border-top: 1px solid var(--kgdiff-border); background: var(--kgdiff-panel); }
491
+ .detail-wrap:has(> .detail[hidden]) { display: none; }
492
+ .detail-wrap[data-state="collapsed"] { flex: none; }
493
+ .detail-wrap[data-state="collapsed"] > .detail { display: none; }
494
+ /* The list is folded: the detail panel takes the room. */
495
+ .sidebar[data-list="folded"] .detail-wrap { flex: 1 1 auto; max-height: none; }
496
+ /* The user dragged the split. */
497
+ .sidebar[data-split] .detail-wrap:not([data-state="collapsed"]) { flex: none; height: calc(var(--detail-share) * 100%); max-height: none; }
498
+ .detail-bar {
499
+ flex: none; display: flex; align-items: center; gap: 8px; width: 100%; height: 30px; padding: 0 10px 0 16px;
500
+ font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted);
501
+ }
502
+ .detail-bar:hover { color: var(--kgdiff-text-secondary); background: var(--kgdiff-hover); }
503
+ .detail-bar .icon { width: 14px; height: 14px; margin-left: auto; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
504
+ .detail-bar[aria-expanded="false"] .icon { transform: rotate(180deg); }
505
+ .detail { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; background: var(--kgdiff-panel); }
506
+ .detail-wrap > .detail:not([data-empty="true"]) { flex: 1 1 auto; }
510
507
  .detail[data-empty="true"] { flex: none; }
511
- .detail-empty { padding: 16px; display: flex; align-items: center; gap: 12px; color: var(--kg-text-3); font-size: 12.5px; }
512
- .detail-empty .icon { color: var(--kg-text-3); }
508
+ .detail-empty { padding: 16px; display: flex; align-items: center; gap: 12px; color: var(--kgdiff-text-muted); font-size: 12.5px; }
509
+ .detail-empty .icon { color: var(--kgdiff-text-muted); }
513
510
  .detail-empty .sub { font-size: 11.5px; margin-top: 2px; }
514
511
  .detail-scroll { flex: 1; min-height: 0; padding: 14px 16px 20px; }
515
512
  .d-top { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
@@ -517,136 +514,147 @@ ${familyRules}
517
514
  .fam-pill .g { font-weight: 700; }
518
515
  .d-nav { margin-left: auto; display: flex; align-items: center; gap: 2px; }
519
516
  .d-title { margin: 0; font-size: 14.5px; line-height: 1.4; font-weight: 600; letter-spacing: -0.005em; overflow-wrap: anywhere; }
520
- .d-sub { margin-top: 4px; font-size: 12.5px; color: var(--kg-text-2); overflow-wrap: anywhere; }
521
- .rv-btn { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 6px; border-radius: 7px; font-size: 12px; font-weight: 550; color: var(--kg-text-2); box-shadow: inset 0 0 0 1px var(--kg-border); margin-right: 4px; }
522
- .rv-btn:hover { color: var(--kg-text); background: var(--kg-hover); }
523
- .rv-btn .ring { width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0 1.5px var(--kg-control-border); color: transparent; }
517
+ .d-sub { margin-top: 4px; font-size: 12.5px; color: var(--kgdiff-text-secondary); overflow-wrap: anywhere; }
518
+ .rv-btn { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 6px; border-radius: 7px; font-size: 12px; font-weight: 550; color: var(--kgdiff-text-secondary); box-shadow: inset 0 0 0 1px var(--kgdiff-border); margin-right: 4px; }
519
+ .rv-btn:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
520
+ .rv-btn .ring { width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0 1.5px var(--kgdiff-control-border); color: transparent; }
524
521
  .rv-btn .ring .icon { width: 11px; height: 11px; }
525
- .rv-btn[aria-pressed="true"] { color: var(--kg-accent-text); box-shadow: inset 0 0 0 1px var(--kg-accent); background: var(--kg-accent-soft); }
526
- .rv-btn[aria-pressed="true"] .ring { background: var(--kg-accent); box-shadow: none; color: var(--kg-bg); }
522
+ .rv-btn[aria-pressed="true"] { color: var(--kgdiff-accent-text); box-shadow: inset 0 0 0 1px var(--kgdiff-accent); background: var(--kgdiff-accent-soft); }
523
+ .rv-btn[aria-pressed="true"] .ring { background: var(--kgdiff-accent); box-shadow: none; color: var(--kgdiff-background); }
527
524
  .section { margin-top: 18px; }
528
- .section > h3 { margin: 0 0 8px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kg-text-3); display: flex; align-items: center; gap: 6px; }
525
+ .section > h3 { margin: 0 0 8px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted); display: flex; align-items: center; gap: 6px; }
529
526
  .section > h3 .n { font-weight: 500; letter-spacing: 0; }
530
527
  .kv { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 6px 12px; font-size: 12.5px; align-items: baseline; }
531
- .kv dt { color: var(--kg-text-3); }
528
+ .kv dt { color: var(--kgdiff-text-muted); }
532
529
  .kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
533
- .id-text { font: 12px/1.5 var(--kg-mono); color: var(--kg-text); overflow-wrap: anywhere; }
530
+ .id-text { font: 12px/1.5 var(--kgdiff-font-mono); color: var(--kgdiff-text); overflow-wrap: anywhere; }
534
531
  .chips { display: flex; flex-wrap: wrap; gap: 4px; }
535
- .vchip { display: inline-flex; align-items: center; gap: 4px; min-height: 22px; padding: 2px 8px; border-radius: 6px; font-size: 12px; background: var(--kg-hover); box-shadow: inset 0 0 0 1px var(--kg-border); overflow-wrap: anywhere; }
536
- .vchip.add { color: var(--kg-added-text); background: var(--kg-added-soft); box-shadow: none; }
537
- .vchip.del { color: var(--kg-removed-text); background: var(--kg-removed-soft); box-shadow: none; text-decoration: line-through; text-decoration-thickness: 1px; }
532
+ .vchip { display: inline-flex; align-items: center; gap: 4px; min-height: 22px; padding: 2px 8px; border-radius: 6px; font-size: 12px; background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border); overflow-wrap: anywhere; }
533
+ .vchip.add { color: var(--kgdiff-added-text); background: var(--kgdiff-added-soft); box-shadow: none; }
534
+ .vchip.del { color: var(--kgdiff-removed-text); background: var(--kgdiff-removed-soft); box-shadow: none; text-decoration: line-through; text-decoration-thickness: 1px; }
538
535
  .vchip .sign { font-weight: 700; text-decoration: none; display: inline-block; }
539
536
  .pair { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 6px; }
540
- .pair .b, .line.old { color: var(--kg-text-2); }
541
- .pair .arr { color: var(--kg-text-3); }
537
+ .pair .b, .line.old { color: var(--kgdiff-text-secondary); }
538
+ .pair .arr { color: var(--kgdiff-text-muted); }
542
539
  .line { display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: 6px; align-items: baseline; }
543
- .line .sign { font: 600 12px/1 var(--kg-mono); text-align: center; }
544
- .line.old .sign { color: var(--kg-removed-text); }
545
- .line.new .sign { color: var(--kg-added-text); }
546
- del.hl { text-decoration: line-through; text-decoration-color: var(--kg-removed); background: var(--kg-removed-soft); color: var(--kg-removed-text); border-radius: 3px; padding: 0 1px; }
547
- ins.hl { text-decoration: none; background: var(--kg-added-soft); color: var(--kg-added-text); border-radius: 3px; padding: 0 1px; }
540
+ .line .sign { font: 600 12px/1 var(--kgdiff-font-mono); text-align: center; }
541
+ .line.old .sign { color: var(--kgdiff-removed-text); }
542
+ .line.new .sign { color: var(--kgdiff-added-text); }
543
+ del.hl { text-decoration: line-through; text-decoration-color: var(--kgdiff-removed); background: var(--kgdiff-removed-soft); color: var(--kgdiff-removed-text); border-radius: 3px; padding: 0 1px; }
544
+ ins.hl { text-decoration: none; background: var(--kgdiff-added-soft); color: var(--kgdiff-added-text); border-radius: 3px; padding: 0 1px; }
548
545
  .attrs { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12.5px; table-layout: fixed; }
549
- .attrs td { vertical-align: baseline; padding: 6px 8px 6px 0; border-top: 1px solid var(--kg-border); overflow-wrap: anywhere; }
546
+ .attrs td { vertical-align: baseline; padding: 6px 8px 6px 0; border-top: 1px solid var(--kgdiff-border); overflow-wrap: anywhere; }
550
547
  .attrs tr:first-child td { border-top: 0; padding-top: 0; }
551
548
  .attrs td > * + * { margin-top: 4px; }
552
- .attrs td.k { color: var(--kg-text-2); }
549
+ .attrs td.k { color: var(--kgdiff-text-secondary); }
553
550
  .attrs col.k { width: 28%; }
554
551
  .val { white-space: pre-wrap; }
555
552
  .val.clamped { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
556
553
  .more { margin-top: 2px; }
557
- .disclosure { display: inline-flex; align-items: center; gap: 4px; min-height: 28px; margin-top: 4px; font-size: 12px; color: var(--kg-text-2); border-radius: 4px; }
558
- .disclosure:hover { color: var(--kg-text); }
559
- .disclosure .icon { width: 12px; height: 12px; transition: transform var(--kg-base) var(--kg-ease); }
554
+ .disclosure { display: inline-flex; align-items: center; gap: 4px; min-height: 28px; margin-top: 4px; font-size: 12px; color: var(--kgdiff-text-secondary); border-radius: 4px; }
555
+ .disclosure:hover { color: var(--kgdiff-text); }
556
+ .disclosure .icon { width: 12px; height: 12px; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
560
557
  .disclosure[aria-expanded="true"] .icon { transform: rotate(180deg); }
561
558
  .rels { display: grid; gap: 2px; }
562
559
  .rel { display: flex; align-items: baseline; gap: 8px; width: 100%; padding: 6px 8px; margin: 0 -8px; border-radius: 7px; text-align: left; font-size: 12.5px; line-height: 1.5; width: calc(100% + 16px); }
563
- .rel:hover { background: var(--kg-hover); }
564
- .rel .sign { flex: none; width: 12px; font: 600 12px/1.5 var(--kg-mono); color: var(--fam-text); text-align: center; }
560
+ .rel:hover { background: var(--kgdiff-hover); }
561
+ .rel .sign { flex: none; width: 12px; font: 600 12px/1.5 var(--kgdiff-font-mono); color: var(--fam-text); text-align: center; }
565
562
  .rel .s { min-width: 0; overflow-wrap: anywhere; }
566
563
  .rel .node { font-weight: 550; }
567
- .rel .pred { color: var(--kg-text-2); white-space: nowrap; }
568
- .rel .was { color: var(--kg-text-3); }
569
- .rel.removed .s { text-decoration: line-through; text-decoration-color: var(--kg-text-3); color: var(--kg-text-2); }
564
+ .rel .pred { color: var(--kgdiff-text-secondary); white-space: nowrap; }
565
+ .rel .was { color: var(--kgdiff-text-muted); }
566
+ .rel.removed .s { text-decoration: line-through; text-decoration-color: var(--kgdiff-text-muted); color: var(--kgdiff-text-secondary); }
570
567
  .nodes { display: grid; gap: 2px; }
571
568
  .node-link { display: flex; align-items: center; gap: 8px; width: calc(100% + 16px); margin: 0 -8px; padding: 5px 8px; border-radius: 7px; text-align: left; font-size: 12.5px; }
572
- .node-link:hover { background: var(--kg-hover); }
573
- .node-link[aria-current="true"] { background: var(--kg-hover); box-shadow: inset 0 0 0 1px var(--kg-border-strong); }
569
+ .node-link:hover { background: var(--kgdiff-hover); }
570
+ .node-link[aria-current="true"] { background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
574
571
  .node-link .sdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--fam-base); }
575
572
  .node-link .lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
576
- .node-link .meta { flex: none; color: var(--kg-text-3); font-size: 11.5px; }
573
+ .node-link .meta { flex: none; color: var(--kgdiff-text-muted); font-size: 11.5px; }
577
574
  .comp { display: grid; gap: 6px; }
578
575
  .comp-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
579
- .comp-row .arrow { color: var(--kg-text-3); }
580
- .comp-row .note { color: var(--kg-text-3); font-size: 11.5px; }
576
+ .comp-row .arrow { color: var(--kgdiff-text-muted); }
577
+ .comp-row .note { color: var(--kgdiff-text-muted); font-size: 11.5px; }
581
578
  .bars { display: grid; gap: 6px; }
582
579
  .bar-row { display: grid; grid-template-columns: 84px minmax(0, 1fr) 40px; align-items: center; gap: 10px; font-size: 12px; }
583
- .bar-row .lbl { color: var(--kg-text-2); }
584
- .bar-row .track { height: 6px; border-radius: 3px; background: var(--kg-track); overflow: hidden; }
585
- .bar-row .fill { height: 100%; border-radius: 3px; background: var(--kg-identity); }
586
- .bar-row .v { text-align: right; color: var(--kg-text-2); font-variant-numeric: tabular-nums; }
587
- .note-line { font-size: 12px; color: var(--kg-text-3); margin-top: 6px; }
580
+ .bar-row .lbl { color: var(--kgdiff-text-secondary); }
581
+ .bar-row .track { height: 6px; border-radius: 3px; background: var(--kgdiff-track); overflow: hidden; }
582
+ .bar-row .fill { height: 100%; border-radius: 3px; background: var(--kgdiff-identity); }
583
+ .bar-row .v { text-align: right; color: var(--kgdiff-text-secondary); font-variant-numeric: tabular-nums; }
584
+ .note-line { font-size: 12px; color: var(--kgdiff-text-muted); margin-top: 6px; }
588
585
 
589
586
  /* ── Popovers ────────────────────────────────────────────────────────────── */
590
587
  .menu {
591
588
  position: absolute; z-index: 10; top: calc(100% + 8px); right: 0; width: 240px; padding: 6px; border-radius: 12px;
592
- animation: pop var(--kg-fast) var(--kg-ease);
589
+ animation: pop var(--kgdiff-fast) var(--kgdiff-ease);
593
590
  }
594
591
  @keyframes pop { from { opacity: 0; transform: translateY(-4px) scale(0.98); } }
595
- .menu-group + .menu-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--kg-border); }
596
- .menu-label { padding: 6px 8px 4px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kg-text-3); }
597
- .menu-item { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 8px; border-radius: 7px; font-size: 13px; color: var(--kg-text); text-align: left; }
598
- .menu-item:hover, .menu-item:focus-visible { background: var(--kg-hover); outline: none; }
599
- .menu-item .icon { color: var(--kg-text-2); }
600
- .menu-item .check { margin-left: auto; color: var(--kg-accent-text); opacity: 0; }
592
+ .menu-group + .menu-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--kgdiff-border); }
593
+ .menu-label { padding: 6px 8px 4px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-muted); }
594
+ .menu-item { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 8px; border-radius: 7px; font-size: 13px; color: var(--kgdiff-text); text-align: left; }
595
+ .menu-item:hover, .menu-item:focus-visible { background: var(--kgdiff-hover); outline: none; }
596
+ .menu-item .icon { color: var(--kgdiff-text-secondary); }
597
+ .menu-item .check { margin-left: auto; color: var(--kgdiff-accent-text); opacity: 0; }
601
598
  .menu-item[aria-checked="true"] .check { opacity: 1; }
602
599
  .menu-item .sw { display: inline-flex; gap: 2px; }
603
600
  .menu-item .sw i { width: 6px; height: 6px; border-radius: 50%; display: block; }
604
601
 
605
602
  .overlay-scrim {
606
603
  position: absolute; inset: 0; z-index: 20; display: flex; align-items: center; justify-content: center; padding: 24px;
607
- background: var(--kg-scrim); animation: fade var(--kg-fast) var(--kg-ease);
604
+ background: var(--kgdiff-scrim); animation: fade var(--kgdiff-fast) var(--kgdiff-ease);
608
605
  }
609
606
  @keyframes fade { from { opacity: 0; } }
610
- .dialog { width: min(640px, 100%); max-height: 100%; display: flex; flex-direction: column; border-radius: 14px; animation: pop var(--kg-base) var(--kg-ease); }
607
+ .dialog { width: min(640px, 100%); max-height: 100%; display: flex; flex-direction: column; border-radius: 14px; animation: pop var(--kgdiff-base) var(--kgdiff-ease); }
611
608
  .dialog-head { display: flex; align-items: center; gap: 10px; padding: 14px 12px 10px 20px; }
612
609
  .dialog-head h2 { margin: 0; font-size: 15px; font-weight: 650; }
613
610
  .dialog-head .icon-btn { margin-left: auto; }
614
611
  .dialog-body { padding: 0 20px 18px; }
615
612
  .keys { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; }
616
- .key-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 34px; border-bottom: 1px solid var(--kg-border); font-size: 12.5px; color: var(--kg-text-2); }
617
- .key-row .ks { display: inline-flex; align-items: center; gap: 3px; flex: none; color: var(--kg-text-3); font-size: 11px; }
613
+ .key-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 34px; border-bottom: 1px solid var(--kgdiff-border); font-size: 12.5px; color: var(--kgdiff-text-secondary); }
614
+ .key-row .ks { display: inline-flex; align-items: center; gap: 3px; flex: none; color: var(--kgdiff-text-muted); font-size: 11px; }
615
+
616
+ /* ── Panel states: a region not open is not shown (the legend folds to its head) ── */
617
+ [data-panel="hidden"], [data-panel="collapsed"]:not(.legend, .mini-legend) { display: none !important; }
618
618
 
619
619
  /* ── chrome="none": the graph, its hover tooltip and the legend line only ──── */
620
- .root[data-chrome="none"] .hud, .root[data-chrome="none"] .sidebar, .root[data-chrome="none"] .dock,
621
- .root[data-chrome="none"] .map-cluster, .root[data-chrome="none"] .side-chip { display: none !important; }
620
+ /* The regions are hidden by the preset's panel states; the step caption and the swipe chips
621
+ belong to the canvas. */
622
+ .root[data-chrome="none"] .caption, .root[data-chrome="none"] .side-chip { display: none !important; }
622
623
  .stage-wrap[data-selectable="false"] .tooltip .t-hint { display: none; }
623
624
 
624
625
  /* ── Compact (< 760 px): bottom sheet ────────────────────────────────────── */
625
626
  .root[data-compact="true"] .body { flex-direction: column; }
626
627
  .root[data-compact="true"] .sidebar {
627
- width: auto; height: var(--sheet-h, 40%); min-height: 0; border-left: 0; border-top: 1px solid var(--kg-border);
628
+ width: auto; height: var(--sheet-h, 40%); min-height: 0; border-left: 0; border-top: 1px solid var(--kgdiff-border);
628
629
  border-radius: 14px 14px 0 0; margin-top: -14px; box-shadow: 0 -8px 24px rgba(0,0,0,0.18);
629
- transition: height var(--kg-base) var(--kg-ease);
630
+ transition: height var(--kgdiff-base) var(--kgdiff-ease);
630
631
  }
631
632
  .root[data-compact="true"] .sidebar.dragging { transition: none; }
632
633
  .root[data-compact="true"] .sidebar[data-open="false"] { height: 0; border-top-color: transparent; box-shadow: none; }
633
634
  .root[data-compact="true"] .sidebar-inner { width: 100%; }
635
+ .root[data-compact="true"] .sb-resize { display: none; }
634
636
  .root[data-compact="true"] .sheet-handle {
635
637
  display: flex; flex: none; justify-content: center; align-items: center; height: 28px; cursor: ns-resize; touch-action: none; border-radius: 14px 14px 0 0;
636
638
  }
637
- .root[data-compact="true"] .sheet-handle::before { content: ""; width: 36px; height: 4px; border-radius: 2px; background: var(--kg-control-border); }
639
+ .root[data-compact="true"] .sheet-handle::before { content: ""; width: 36px; height: 4px; border-radius: 2px; background: var(--kgdiff-control-border); }
638
640
  .root[data-compact="true"] .sb-head { padding-top: 4px; }
639
641
  .root[data-compact="true"] .versions, .root[data-compact="true"] .header-sep,
640
642
  .root[data-compact="true"] .brand-name { display: none; }
641
643
  .root[data-compact="true"] .hud-title { padding: 0 10px; }
642
644
  .root[data-compact="true"] .minimap-host, .root[data-compact="true"] .legend { display: none; }
643
- /* Zoom controls move under the actions, clear of the full-width replay transport. */
644
- .root[data-compact="true"] .map-cluster { top: 64px; bottom: auto; right: 12px; }
645
+ /* Zoom controls move under the actions, clear of the full-width replay transport (fixed: the
646
+ stage, a size container, contains them), and the page's top-end content under them. The page's
647
+ bottom content rises over the full-width dock, and the transport above it in replay. */
648
+ .root[data-compact="true"] .map-cluster { position: fixed; top: 64px; bottom: auto; right: 12px; }
649
+ .root[data-compact="true"] .corner.top-end { top: 174px; }
650
+ .root[data-compact="true"] .corner.bottom-start, .root[data-compact="true"] .corner.bottom-end { bottom: 82px; }
651
+ .root[data-compact="true"][data-mode="replay"] .corner.bottom-start,
652
+ .root[data-compact="true"][data-mode="replay"] .corner.bottom-end { bottom: 134px; }
645
653
  /* The bottom sheet's rounded top overlaps the canvas. */
646
654
  .root[data-compact="true"] .chrome > .dock { left: 12px; right: 12px; bottom: 26px; }
647
655
  .root[data-compact="true"] .transport { width: 100%; min-width: 0; }
648
656
  .root[data-compact="true"] .transport .pos { min-width: 48px; }
649
- .root[data-compact="true"] .detail { max-height: 50%; }
657
+ .root[data-compact="true"] .detail-wrap { max-height: 50%; }
650
658
  .root[data-narrow="true"] .versions .tag, .root[data-narrow="true"] .versions .arrow { display: none; }
651
659
 
652
660
  @media (prefers-reduced-motion: reduce) {
@@ -654,3 +662,128 @@ ins.hl { text-decoration: none; background: var(--kg-added-soft); color: var(--k
654
662
  .skeleton-grid, .shimmer { animation: none; }
655
663
  }
656
664
  `;
665
+
666
+ /** What every element's host starts from (`all: initial` also resets the shared `:host` rule). */
667
+ const HOST =
668
+ 'all: initial; font-family: var(--kgdiff-font); font-size: 13px; line-height: 1.45; color: var(--kgdiff-text); -webkit-font-smoothing: antialiased;';
669
+
670
+ /** The elements of RFC 0001 §3, by the name after `kgdiff-`. */
671
+ export type ElementName =
672
+ | 'root'
673
+ | 'stage'
674
+ | 'change-list'
675
+ | 'change-detail'
676
+ | 'search'
677
+ | 'filters'
678
+ | 'view-dock'
679
+ | 'transport'
680
+ | 'legend'
681
+ | 'zoom'
682
+ | 'minimap'
683
+ | 'title'
684
+ | 'actions';
685
+
686
+ /** Each element's host rules, adopted after the shared sheet (`<kgdiff-root>`: instead of it). */
687
+ const ELEMENT_STYLES: Readonly<Record<ElementName, string>> = {
688
+ root: /* css */ `
689
+ :host { display: contents; }
690
+ [hidden] { display: none !important; }
691
+ .sr-only {
692
+ position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
693
+ overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
694
+ }`,
695
+ stage: /* css */ `
696
+ :host { ${HOST} display: block; position: relative; height: 100%; min-height: 240px; contain: content; background: var(--kgdiff-background); }
697
+ :host([hidden]) { display: none; }
698
+ .stage-wrap { position: absolute; inset: 0; }`,
699
+ 'change-list': /* css */ `
700
+ :host { ${HOST} display: flex; flex-direction: column; min-height: 0; background: var(--kgdiff-panel); }
701
+ :host([hidden]) { display: none; }
702
+ .change-list { flex: 1; min-height: 0; display: flex; flex-direction: column; }`,
703
+ 'change-detail': /* css */ `
704
+ :host { ${HOST} display: flex; flex-direction: column; min-height: 0; background: var(--kgdiff-panel); }
705
+ :host([hidden]) { display: none; }
706
+ .detail { flex: 1 1 auto; }`,
707
+ search: /* css */ `
708
+ :host { ${HOST} display: block; position: relative; }
709
+ :host([hidden]) { display: none; }
710
+ /* Standalone, the results open below the box. */
711
+ .results {
712
+ position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 10; max-height: min(60vh, 420px);
713
+ border-radius: 12px; background: var(--kgdiff-raised); box-shadow: var(--kgdiff-shadow), 0 0 0 1px var(--kgdiff-border);
714
+ }`,
715
+ filters: /* css */ `
716
+ :host { ${HOST} display: block; }
717
+ :host([hidden]) { display: none; }`,
718
+ 'view-dock': /* css */ `
719
+ :host { ${HOST} display: inline-block; max-width: 100%; container-type: inline-size; }
720
+ :host([hidden]) { display: none; }`,
721
+ transport: /* css */ `
722
+ :host { ${HOST} display: block; width: min(600px, 100%); }
723
+ :host([hidden]) { display: none; }
724
+ .stack { display: flex; flex-direction: column; align-items: center; gap: 8px; }
725
+ .transport { width: 100%; min-width: 0; }`,
726
+ legend: /* css */ `
727
+ :host { ${HOST} display: inline-block; }
728
+ :host([hidden]) { display: none; }
729
+ .legend.float, .mini-legend.float { position: static; }
730
+ .mini-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; font-size: 11px; line-height: 16px; color: var(--kgdiff-text-secondary); }`,
731
+ zoom: /* css */ `
732
+ :host { ${HOST} display: inline-block; }
733
+ :host([hidden]) { display: none; }`,
734
+ minimap: /* css */ `
735
+ :host { ${HOST} display: block; position: relative; width: 184px; height: 128px; }
736
+ :host([hidden]) { display: none; }
737
+ .minimap-host { width: 100%; height: 100%; }`,
738
+ title: /* css */ `
739
+ :host { ${HOST} display: inline-block; max-width: 100%; }
740
+ :host([hidden]) { display: none; }
741
+ .hud-title { position: static; max-width: none; }`,
742
+ actions: /* css */ `
743
+ :host { ${HOST} display: inline-block; position: relative; }
744
+ :host([hidden]) { display: none; }
745
+ .hud-end { position: relative; top: auto; right: auto; }
746
+ /* The dialog covers the page, not the element. */
747
+ .overlay-scrim { position: fixed; z-index: 1000; }`,
748
+ };
749
+
750
+ const elementSheets = new WeakMap<Document, Map<ElementName, CSSStyleSheet>>();
751
+
752
+ /** The host rules of the element `name` as a stylesheet of `doc`, parsed once per document. */
753
+ export function elementSheet(doc: Document, name: ElementName): CSSStyleSheet {
754
+ let byName = elementSheets.get(doc);
755
+ if (!byName) {
756
+ byName = new Map();
757
+ elementSheets.set(doc, byName);
758
+ }
759
+ let sheet = byName.get(name);
760
+ if (!sheet) {
761
+ sheet = sheetFor(doc, ELEMENT_STYLES[name]);
762
+ byName.set(name, sheet);
763
+ }
764
+ return sheet;
765
+ }
766
+
767
+ /**
768
+ * A stylesheet of `doc` holding `css`. A constructed sheet can only be adopted in the document
769
+ * it was made for, so it is made with that document's own `CSSStyleSheet` (a viewer moved into
770
+ * an iframe or a popup gets sheets of its new document).
771
+ */
772
+ export function sheetFor(doc: Document, css: string): CSSStyleSheet {
773
+ const Sheet = (doc.defaultView as (Window & typeof globalThis) | null)?.CSSStyleSheet;
774
+ const sheet = new (Sheet ?? CSSStyleSheet)();
775
+ sheet.replaceSync(css);
776
+ return sheet;
777
+ }
778
+
779
+ const shared = new WeakMap<Document, CSSStyleSheet>();
780
+
781
+ /** `STYLES` as a stylesheet of `doc`, parsed once per document. */
782
+ export function stylesheet(doc: Document): CSSStyleSheet {
783
+ let sheet = shared.get(doc);
784
+ if (!sheet) {
785
+ sheet = sheetFor(doc, STYLES);
786
+ shared.set(doc, sheet);
787
+ }
788
+ return sheet;
789
+ }