@kgdiff/viewer 0.4.0 → 0.5.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 (84) hide show
  1. package/dist/context.d.ts +42 -0
  2. package/dist/context.d.ts.map +1 -0
  3. package/dist/context.js +87 -0
  4. package/dist/context.js.map +1 -0
  5. package/dist/controller.d.ts +26 -185
  6. package/dist/controller.d.ts.map +1 -1
  7. package/dist/controller.js +45 -1155
  8. package/dist/controller.js.map +1 -1
  9. package/dist/element.d.ts +6 -3
  10. package/dist/element.d.ts.map +1 -1
  11. package/dist/element.js +49 -229
  12. package/dist/element.js.map +1 -1
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/pipeline.d.ts.map +1 -1
  17. package/dist/pipeline.js +3 -2
  18. package/dist/pipeline.js.map +1 -1
  19. package/dist/root.d.ts +54 -0
  20. package/dist/root.d.ts.map +1 -0
  21. package/dist/root.js +265 -0
  22. package/dist/root.js.map +1 -0
  23. package/dist/schedule.d.ts +22 -0
  24. package/dist/schedule.d.ts.map +1 -0
  25. package/dist/schedule.js +41 -0
  26. package/dist/schedule.js.map +1 -0
  27. package/dist/session.d.ts +253 -0
  28. package/dist/session.d.ts.map +1 -0
  29. package/dist/session.js +1239 -0
  30. package/dist/session.js.map +1 -0
  31. package/dist/standalone/kgdiff-viewer-compute.js +1 -1
  32. package/dist/standalone/kgdiff-viewer.js +4 -4
  33. package/dist/styles.d.ts +13 -6
  34. package/dist/styles.d.ts.map +1 -1
  35. package/dist/styles.js +217 -248
  36. package/dist/styles.js.map +1 -1
  37. package/dist/theme.d.ts +25 -0
  38. package/dist/theme.d.ts.map +1 -0
  39. package/dist/theme.js +149 -0
  40. package/dist/theme.js.map +1 -0
  41. package/dist/ui/change-list.d.ts +18 -0
  42. package/dist/ui/change-list.d.ts.map +1 -0
  43. package/dist/ui/change-list.js +331 -0
  44. package/dist/ui/change-list.js.map +1 -0
  45. package/dist/ui/context.d.ts +19 -5
  46. package/dist/ui/context.d.ts.map +1 -1
  47. package/dist/ui/context.js.map +1 -1
  48. package/dist/ui/filters.d.ts +9 -0
  49. package/dist/ui/filters.d.ts.map +1 -0
  50. package/dist/ui/filters.js +68 -0
  51. package/dist/ui/filters.js.map +1 -0
  52. package/dist/ui/header.js +2 -2
  53. package/dist/ui/header.js.map +1 -1
  54. package/dist/ui/search.d.ts +13 -0
  55. package/dist/ui/search.d.ts.map +1 -0
  56. package/dist/ui/search.js +191 -0
  57. package/dist/ui/search.js.map +1 -0
  58. package/dist/ui/sidebar.d.ts +8 -16
  59. package/dist/ui/sidebar.d.ts.map +1 -1
  60. package/dist/ui/sidebar.js +15 -539
  61. package/dist/ui/sidebar.js.map +1 -1
  62. package/dist/ui/stage.js +1 -1
  63. package/dist/ui/stage.js.map +1 -1
  64. package/dist/ui/swatch.js +1 -1
  65. package/dist/ui/swatch.js.map +1 -1
  66. package/package.json +6 -6
  67. package/src/context.ts +106 -0
  68. package/src/controller.ts +70 -1257
  69. package/src/element.ts +52 -243
  70. package/src/index.ts +3 -1
  71. package/src/pipeline.ts +4 -1
  72. package/src/root.ts +308 -0
  73. package/src/schedule.ts +43 -0
  74. package/src/session.ts +1386 -0
  75. package/src/styles.ts +225 -253
  76. package/src/theme.ts +167 -0
  77. package/src/ui/change-list.ts +401 -0
  78. package/src/ui/context.ts +21 -5
  79. package/src/ui/filters.ts +79 -0
  80. package/src/ui/header.ts +2 -2
  81. package/src/ui/search.ts +220 -0
  82. package/src/ui/sidebar.ts +28 -668
  83. package/src/ui/stage.ts +1 -1
  84. package/src/ui/swatch.ts +1 -1
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-2); }`
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-bg);
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,42 @@ 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-mono); color: var(--kgdiff-text-2);
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-bg);
122
70
  }
123
71
  .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; }
72
+ .stage-wrap { position: relative; flex: 1; min-width: 0; min-height: 0; overflow: hidden; background: var(--kgdiff-bg); 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; }
130
78
 
131
79
  /* ── Buttons ─────────────────────────────────────────────────────────────── */
132
80
  .icon-btn {
133
81
  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);
82
+ width: 32px; height: 32px; border-radius: 8px; color: var(--kgdiff-text-2);
83
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease);
136
84
  }
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); }
85
+ .icon-btn:hover { background: var(--kgdiff-hover); color: var(--kgdiff-text); }
86
+ .icon-btn:active { background: var(--kgdiff-press); }
87
+ .icon-btn[aria-pressed="true"], .icon-btn[aria-expanded="true"] { background: var(--kgdiff-press); color: var(--kgdiff-text); }
140
88
  .icon-btn.sm { width: 28px; height: 28px; border-radius: 7px; }
141
89
  .btn {
142
90
  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);
91
+ border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--kgdiff-text);
92
+ background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
93
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
146
94
  }
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; }
95
+ .btn:hover { background: var(--kgdiff-press); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
96
+ .link-btn { min-height: 28px; color: var(--kgdiff-accent-text); font-weight: 500; font-size: 12px; border-radius: 4px; }
149
97
  .link-btn:hover { text-decoration: underline; text-underline-offset: 3px; }
150
98
 
151
99
  /* ── HUD: title (top-left) and actions (top-right), over the canvas ──────── */
@@ -159,36 +107,36 @@ kbd {
159
107
  :host([header="compact"]) .hud-title { display: none; }
160
108
  .hud-end { position: absolute; top: 12px; right: 12px; }
161
109
  .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); }
110
+ .brand { display: flex; align-items: center; gap: 8px; flex: none; color: var(--kgdiff-text); }
163
111
  .brand-mark { width: 20px; height: 20px; display: block; }
164
112
  .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; }
113
+ .header-sep { width: 1px; height: 18px; background: var(--kgdiff-border-strong); flex: none; }
166
114
  .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; }
115
+ .versions .name { font-weight: 550; color: var(--kgdiff-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
116
+ .versions .arrow { color: var(--kgdiff-text-3); flex: none; }
169
117
  .versions .side { display: flex; align-items: center; gap: 6px; min-width: 0; }
170
118
  .tag {
171
119
  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;
120
+ font: 500 11px/1 var(--kgdiff-mono); color: var(--kgdiff-text-2); background: var(--kgdiff-hover);
121
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border); white-space: nowrap; max-width: 160px; overflow: hidden; text-overflow: ellipsis;
174
122
  }
175
123
 
176
124
  .segmented {
177
125
  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);
126
+ border-radius: 9px; background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
179
127
  }
180
128
  .segmented .thumb {
181
129
  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);
130
+ background: var(--kgdiff-raised); box-shadow: 0 1px 2px rgba(0,0,0,0.12), inset 0 0 0 1px var(--kgdiff-border-strong);
131
+ transition: transform var(--kgdiff-base) var(--kgdiff-ease), width var(--kgdiff-base) var(--kgdiff-ease);
184
132
  }
185
133
  .seg {
186
134
  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);
135
+ border-radius: 7px; font-size: 12.5px; font-weight: 500; color: var(--kgdiff-text-2); white-space: nowrap;
136
+ transition: color var(--kgdiff-fast) var(--kgdiff-ease);
189
137
  }
190
- .seg:hover { color: var(--kg-text); }
191
- .seg[aria-checked="true"] { color: var(--kg-text); }
138
+ .seg:hover { color: var(--kgdiff-text); }
139
+ .seg[aria-checked="true"] { color: var(--kgdiff-text); }
192
140
  .seg .icon { opacity: 0.9; }
193
141
 
194
142
 
@@ -196,7 +144,7 @@ ${familyRules}
196
144
 
197
145
  /* ── Floating chrome ─────────────────────────────────────────────────────── */
198
146
  .card {
199
- background: var(--kg-overlay); border-radius: 12px; box-shadow: var(--kg-shadow), 0 0 0 1px var(--kg-border);
147
+ background: var(--kgdiff-overlay); border-radius: 12px; box-shadow: var(--kgdiff-shadow), 0 0 0 1px var(--kgdiff-border);
200
148
  backdrop-filter: blur(16px) saturate(140%);
201
149
  }
202
150
  .float { position: absolute; }
@@ -204,34 +152,34 @@ ${familyRules}
204
152
  .bottom-right { bottom: 16px; right: 16px; }
205
153
  .chip {
206
154
  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;
155
+ font-size: 12px; font-weight: 600; color: var(--kgdiff-text-2); letter-spacing: 0.01em;
208
156
  }
209
157
 
210
158
  .map-cluster { display: flex; align-items: flex-end; gap: 8px; }
211
159
  .zoom-bar { display: flex; flex-direction: column; padding: 2px; gap: 2px; }
212
160
  .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); }
161
+ .minimap-host { position: relative; width: 184px; height: 128px; border-radius: 12px; box-shadow: 0 0 0 1px var(--kgdiff-border);
162
+ animation: fade var(--kgdiff-base) var(--kgdiff-ease); }
215
163
  /* Everything is in view: the minimap has nothing to add. */
216
164
  .minimap-host[data-hidden="true"] { display: none; }
217
165
  .minimap-host canvas { border-radius: 12px; }
218
166
 
219
167
  .legend { padding: 6px; min-width: 0; }
220
168
  .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); }
169
+ letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-3); border-radius: 7px; width: 100%; }
170
+ .legend-head:hover { color: var(--kgdiff-text-2); background: var(--kgdiff-hover); }
171
+ .legend-head .icon { width: 14px; height: 14px; margin-left: auto; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
224
172
  .legend[data-open="false"] .legend-head .icon { transform: rotate(180deg); }
225
173
  .legend[data-open="false"] .legend-body { display: none; }
226
174
  .legend[data-open="false"] { padding: 2px; }
227
175
  .legend-body { display: grid; gap: 2px; padding: 2px 4px 4px; }
228
176
  .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); }
177
+ .legend-row .name { font-size: 12px; font-weight: 550; color: var(--kgdiff-text); }
230
178
  .swatch { width: 22px; height: 22px; display: block; }
231
179
  .mini-legend { display: none; }
232
180
  .stage-wrap[data-legend="mini"] .mini-legend {
233
181
  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);
182
+ font-size: 11px; line-height: 16px; color: var(--kgdiff-text-2);
235
183
  }
236
184
  .mini-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
237
185
  .mini-item .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--fam-base); }
@@ -243,7 +191,7 @@ ${familyRules}
243
191
  font-size: 12.5px; font-weight: 550;
244
192
  }
245
193
  .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; }
194
+ .caption .count { color: var(--kgdiff-text-3); font-weight: 500; font-variant-numeric: tabular-nums; flex: none; }
247
195
  .caption[data-empty="true"] { display: none; }
248
196
 
249
197
  .glyph-box {
@@ -258,24 +206,24 @@ ${familyRules}
258
206
  }
259
207
  .dock > * { pointer-events: auto; }
260
208
  .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); }
209
+ .dock-sep { width: 1px; height: 20px; margin: 0 2px; flex: none; background: var(--kgdiff-border-strong); }
262
210
  .scope-btn {
263
211
  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);
212
+ font-size: 12.5px; font-weight: 500; color: var(--kgdiff-text-2); white-space: nowrap;
213
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
266
214
  }
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); }
215
+ .scope-btn:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
216
+ .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
217
  .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); }
218
+ .context { display: inline-flex; align-items: center; gap: 8px; padding-left: 2px; flex: none; animation: reveal var(--kgdiff-base) var(--kgdiff-ease); }
271
219
  @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); }
220
+ .context .label { font-size: 12px; font-weight: 550; color: var(--kgdiff-text-3); white-space: nowrap; }
221
+ .context .opts { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px; background: var(--kgdiff-hover); }
222
+ .context .opt { width: 28px; height: 28px; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--kgdiff-text-2); font-variant-numeric: tabular-nums; }
223
+ .context .opt:hover { color: var(--kgdiff-text); }
224
+ .context .opt[aria-checked="true"] { background: var(--kgdiff-raised); color: var(--kgdiff-text); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
225
+ .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-2); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); font-variant-numeric: tabular-nums; }
226
+ .context .expand:hover:not(:disabled) { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
279
227
  .context .expand .icon { width: 14px; height: 14px; }
280
228
  /* Narrower canvases keep the dock clear of the legend and the minimap: labels give way to icons
281
229
  (each control keeps its name as a tooltip). */
@@ -289,18 +237,18 @@ ${familyRules}
289
237
  position: absolute; top: 0; bottom: 0; left: 0; width: 24px; margin-left: -12px; cursor: ew-resize; pointer-events: auto;
290
238
  touch-action: none; display: flex; justify-content: center;
291
239
  }
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); }
240
+ .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
241
  .swipe .handle {
294
242
  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);
243
+ display: flex; align-items: center; justify-content: center; gap: 0; color: var(--kgdiff-accent-text);
244
+ background: var(--kgdiff-overlay); box-shadow: var(--kgdiff-shadow), 0 0 0 1.5px var(--kgdiff-accent);
297
245
  backdrop-filter: blur(16px);
298
- transition: transform var(--kg-fast) var(--kg-ease);
246
+ transition: transform var(--kgdiff-fast) var(--kgdiff-ease);
299
247
  }
300
248
  .swipe .handle .icon { width: 12px; height: 12px; }
301
249
  .swipe .divider:hover .handle, .swipe .divider.dragging .handle { transform: scale(1.08); }
302
250
  .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); }
251
+ .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
252
  /* Below the HUD's title and actions. */
305
253
  .swipe .side-chip { position: absolute; top: 64px; pointer-events: none; }
306
254
  .swipe .side-chip.before { left: 12px; }
@@ -311,18 +259,18 @@ ${familyRules}
311
259
  width: min(600px, calc(100% - 464px)); min-width: min(360px, 100%);
312
260
  display: flex; align-items: center; gap: 4px; height: 48px; padding: 0 8px;
313
261
  }
314
- .transport .play { width: 36px; height: 36px; border-radius: 10px; color: var(--kg-text); }
262
+ .transport .play { width: 36px; height: 36px; border-radius: 10px; color: var(--kgdiff-text); }
315
263
  .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); }
264
+ .transport .pos { flex: none; min-width: 64px; text-align: center; font-size: 12px; font-weight: 550; color: var(--kgdiff-text-2); font-variant-numeric: tabular-nums; }
265
+ .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-2); font-variant-numeric: tabular-nums; box-shadow: inset 0 0 0 1px var(--kgdiff-border); }
266
+ .transport .speed:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
319
267
  .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; }
268
+ .scrubber .track { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 2px; background: var(--kgdiff-track); overflow: hidden; }
269
+ .scrubber .fill { position: absolute; left: 0; top: 0; bottom: 0; width: 100%; transform-origin: left; background: var(--kgdiff-accent); opacity: 0.85; }
322
270
  .scrubber .ticks { position: absolute; left: 0; right: 0; top: 50%; height: 14px; margin-top: -7px; width: 100%; pointer-events: none; overflow: visible; }
323
271
  .scrubber .knob {
324
272
  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;
273
+ 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
274
  }
327
275
  .scrubber:focus-visible { outline-offset: 0; }
328
276
  .scrub-tip {
@@ -332,25 +280,25 @@ ${familyRules}
332
280
  }
333
281
  .finished {
334
282
  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);
283
+ height: 36px; padding: 0 6px 0 12px; font-size: 12.5px; font-weight: 550; color: var(--kgdiff-text);
284
+ animation: rise var(--kgdiff-base) var(--kgdiff-ease);
337
285
  }
338
- .finished .icon { color: var(--kg-accent-text); }
286
+ .finished .icon { color: var(--kgdiff-accent-text); }
339
287
  @keyframes rise { from { opacity: 0; transform: translateY(6px); } }
340
288
 
341
289
  /* Tooltip */
342
290
  .tooltip {
343
291
  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);
292
+ border-radius: 10px; opacity: 0; transition: opacity var(--kgdiff-fast) var(--kgdiff-ease);
345
293
  }
346
294
  .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; }
295
+ .tooltip .t-label { font-size: 13px; font-weight: 600; color: var(--kgdiff-text); overflow-wrap: anywhere; }
296
+ .tooltip .t-old { font-size: 12px; color: var(--kgdiff-text-3); text-decoration: line-through; overflow-wrap: anywhere; }
297
+ .tooltip .t-id { margin-top: 2px; font: 11px/1.4 var(--kgdiff-mono); color: var(--kgdiff-text-3); overflow-wrap: anywhere; }
350
298
  .tooltip .t-status { margin-top: 8px; display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 550; color: var(--fam-text); }
351
299
  .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); }
300
+ .tooltip .t-sum { margin-top: 4px; font-size: 12px; color: var(--kgdiff-text-2); overflow-wrap: anywhere; }
301
+ .tooltip .t-hint { margin-top: 6px; font-size: 11px; color: var(--kgdiff-text-3); }
354
302
 
355
303
  /* States (loading / identical / error / empty) */
356
304
  .state {
@@ -358,36 +306,36 @@ ${familyRules}
358
306
  pointer-events: none;
359
307
  }
360
308
  .state > * { pointer-events: auto; }
361
- .state.blocking { background: var(--kg-bg); }
309
+ .state.blocking { background: var(--kgdiff-bg); }
362
310
  .state-card { width: min(380px, 100%); padding: 24px; text-align: left; }
363
311
  .state-card .state-icon {
364
312
  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;
313
+ color: var(--kgdiff-accent-text); background: var(--kgdiff-accent-soft); margin-bottom: 16px;
366
314
  }
367
315
  .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); }
316
+ .state-card.error .state-icon { color: var(--kgdiff-removed-text); background: var(--kgdiff-removed-soft); }
317
+ .state-card.ok .state-icon { color: var(--kgdiff-added-text); background: var(--kgdiff-added-soft); }
370
318
  .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; }
319
+ .state-card p { margin: 6px 0 0; color: var(--kgdiff-text-2); font-size: 13px; }
320
+ .state-card details { margin-top: 16px; font-size: 12px; color: var(--kgdiff-text-2); }
321
+ .state-card summary { cursor: pointer; font-weight: 550; color: var(--kgdiff-text-2); border-radius: 4px; width: max-content; }
374
322
  .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);
323
+ margin: 8px 0 0; max-height: 200px; padding: 10px 12px; border-radius: 8px; background: var(--kgdiff-input);
324
+ font: 11.5px/1.5 var(--kgdiff-mono); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--kgdiff-text-2);
325
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border);
378
326
  }
379
327
  .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); }
328
+ .steps li { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--kgdiff-text-3); transition: color var(--kgdiff-base) var(--kgdiff-ease); }
329
+ .steps li[data-state="active"] { color: var(--kgdiff-text); font-weight: 550; }
330
+ .steps li[data-state="done"] { color: var(--kgdiff-text-2); }
331
+ .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); }
332
+ .steps li[data-state="done"] .mark { background: var(--kgdiff-accent-soft); box-shadow: none; color: var(--kgdiff-accent-text); }
385
333
  .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; }
334
+ .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
335
  @keyframes spin { to { transform: rotate(360deg); } }
388
336
  .skeleton-grid {
389
337
  position: absolute; inset: 0;
390
- background-image: radial-gradient(var(--kg-skeleton-hi) 1.2px, transparent 1.4px); background-size: 24px 24px;
338
+ background-image: radial-gradient(var(--kgdiff-skeleton-hi) 1.2px, transparent 1.4px); background-size: 24px 24px;
391
339
  mask-image: radial-gradient(ellipse at center, #000 20%, transparent 75%);
392
340
  animation: breathe 2.4s ease-in-out infinite;
393
341
  }
@@ -396,46 +344,46 @@ ${familyRules}
396
344
  /* ── Sidebar ─────────────────────────────────────────────────────────────── */
397
345
  .sidebar {
398
346
  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);
347
+ background: var(--kgdiff-panel); border-left: 1px solid var(--kgdiff-border);
348
+ transition: width var(--kgdiff-base) var(--kgdiff-ease), border-color var(--kgdiff-base) var(--kgdiff-ease);
401
349
  }
402
350
  .sidebar[data-open="false"] { width: 0; border-left-color: transparent; }
403
351
  .sidebar-inner { width: 360px; flex: none; display: flex; flex-direction: column; min-height: 0; }
404
352
  .sheet-handle { display: none; }
405
- .sb-head { flex: none; padding: 12px 12px 8px; display: grid; gap: 10px; border-bottom: 1px solid var(--kg-border); }
353
+ .sb-head { flex: none; padding: 12px 12px 8px; display: grid; gap: 10px; border-bottom: 1px solid var(--kgdiff-border); }
406
354
  .sb-title { display: flex; align-items: center; gap: 8px; height: 24px; padding: 0 2px; }
407
355
  .sb-title h2 { margin: 0; font-size: 13px; font-weight: 650; }
408
356
  .count-badge {
409
357
  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;
358
+ color: var(--kgdiff-text-2); background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border); font-variant-numeric: tabular-nums;
411
359
  }
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); }
360
+ .progress { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--kgdiff-text-3); font-variant-numeric: tabular-nums; }
361
+ .progress .bar { width: 64px; height: 4px; border-radius: 2px; background: var(--kgdiff-track); overflow: hidden; }
362
+ .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); }
363
+ .progress[data-complete="true"] { color: var(--kgdiff-accent-text); }
416
364
 
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; }
365
+ .search { position: relative; display: flex; align-items: center; height: 32px; border-radius: 8px; background: var(--kgdiff-input);
366
+ box-shadow: inset 0 0 0 1px var(--kgdiff-border); transition: box-shadow var(--kgdiff-fast) var(--kgdiff-ease); }
367
+ .search:focus-within { box-shadow: inset 0 0 0 1px var(--kgdiff-accent), 0 0 0 3px var(--kgdiff-accent-soft); }
368
+ .search > .icon { position: absolute; left: 10px; color: var(--kgdiff-text-3); pointer-events: none; }
421
369
  .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);
370
+ flex: 1; min-width: 0; height: 100%; padding: 0 64px 0 32px; border: 0; background: transparent; font-size: 13px; color: var(--kgdiff-text);
423
371
  }
424
- .search input::placeholder { color: var(--kg-text-3); }
372
+ .search input::placeholder { color: var(--kgdiff-text-3); }
425
373
  .search input::-webkit-search-cancel-button { display: none; }
426
374
  .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); }
375
+ .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-3); }
376
+ .search .clear:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
429
377
 
430
378
  .filters { display: flex; flex-wrap: wrap; gap: 4px; }
431
379
  .fchip {
432
380
  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);
381
+ font-size: 11.5px; font-weight: 550; color: var(--kgdiff-text-2); box-shadow: inset 0 0 0 1px var(--kgdiff-border);
382
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease), color var(--kgdiff-fast) var(--kgdiff-ease);
435
383
  }
436
- .fchip:hover { background: var(--kg-hover); color: var(--kg-text); }
384
+ .fchip:hover { background: var(--kgdiff-hover); color: var(--kgdiff-text); }
437
385
  .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; }
386
+ .fchip .n { color: var(--kgdiff-text-3); font-variant-numeric: tabular-nums; font-weight: 500; }
439
387
  .fchip[aria-pressed="true"] { background: var(--fam-soft); color: var(--fam-text); box-shadow: inset 0 0 0 1px var(--fam-base); }
440
388
  .fchip[aria-pressed="true"] .n { color: var(--fam-text); opacity: 0.8; }
441
389
  .fchip:disabled { opacity: 0.35; }
@@ -443,73 +391,73 @@ ${familyRules}
443
391
  .list-wrap { position: relative; flex: 1 1 50%; min-height: 96px; display: flex; flex-direction: column; }
444
392
  .list { position: relative; flex: 1; min-height: 0; outline: none; }
445
393
  .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); }
394
+ .list:focus-visible:not(:has(.row[aria-selected="true"])) { box-shadow: inset 0 0 0 2px var(--kgdiff-accent); }
395
+ .list:focus-visible .row[aria-selected="true"] { box-shadow: inset 0 0 0 2px var(--kgdiff-accent); }
448
396
  .list-inner { position: relative; width: 100%; }
449
397
  .row {
450
398
  position: absolute; left: 8px; right: 8px; height: 52px; display: flex; align-items: center; gap: 10px; padding: 0 6px 0 14px;
451
399
  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);
400
+ transition: background var(--kgdiff-fast) var(--kgdiff-ease), box-shadow var(--kgdiff-fast) var(--kgdiff-ease);
453
401
  }
454
402
  .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); }
403
+ .row:hover { background: var(--kgdiff-hover); }
404
+ .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
405
  .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); }
406
+ .row .title { font-size: 13px; font-weight: 500; color: var(--kgdiff-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
407
+ .row .sub { font-size: 12px; color: var(--kgdiff-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-height: 17px; }
408
+ .row[data-reviewed="true"] .title { color: var(--kgdiff-text-2); }
461
409
  .row[data-reviewed="true"] .glyph-box { opacity: 0.7; }
462
410
  .rv {
463
411
  flex: none; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer;
464
412
  }
465
413
  .rv .ring {
466
414
  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);
415
+ box-shadow: inset 0 0 0 1.5px var(--kgdiff-control-border); color: transparent;
416
+ 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
417
  }
470
418
  .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); }
419
+ .rv:hover .ring { box-shadow: inset 0 0 0 1.5px var(--kgdiff-accent); color: var(--kgdiff-text-3); }
420
+ .rv[data-pressed="true"] .ring { background: var(--kgdiff-accent); box-shadow: none; color: var(--kgdiff-bg); }
473
421
  .rv[data-pressed="true"]:active .ring { transform: scale(0.9); }
474
422
  .group {
475
423
  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);
424
+ font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-3);
477
425
  }
478
426
  .group .n { font-weight: 500; letter-spacing: 0; font-variant-numeric: tabular-nums; }
479
427
  .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;
428
+ position: absolute; top: 0; left: 0; right: 0; z-index: 1; height: 32px; background: var(--kgdiff-panel);
429
+ box-shadow: 0 1px 0 var(--kgdiff-border); pointer-events: none;
482
430
  }
483
431
  .sticky .group { position: static; height: 32px; padding-top: 0; }
484
432
  .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; }
433
+ .list-empty { padding: 32px 24px; text-align: center; color: var(--kgdiff-text-3); font-size: 12.5px; display: grid; gap: 10px; justify-items: center; }
486
434
  .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); }
435
+ .skeleton-row .b { flex: 1; max-width: 70%; height: 10px; border-radius: 5px; background: var(--kgdiff-skeleton); }
488
436
  .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; }
437
+ .skeleton-row .g { width: 20px; height: 20px; border-radius: 6px; background: var(--kgdiff-skeleton); flex: none; }
490
438
  .shimmer { animation: breathe 1.6s ease-in-out infinite; }
491
439
 
492
440
  .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); }
441
+ .results-head { padding: 8px 16px 6px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-3); }
494
442
  .result {
495
443
  display: flex; align-items: center; gap: 10px; width: calc(100% - 16px); margin: 0 8px; height: 44px; padding: 0 10px; border-radius: 8px;
496
444
  text-align: left;
497
445
  }
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); }
446
+ .result:hover, .result[aria-selected="true"] { background: var(--kgdiff-hover); }
447
+ .result[aria-selected="true"] { box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
500
448
  .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); }
449
+ .result[data-family="neutral"] .sdot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--kgdiff-neutral-stroke); }
502
450
  .result .txt { flex: 1; min-width: 0; display: grid; }
503
451
  .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; }
452
+ .result .sub { font: 11px/1.4 var(--kgdiff-mono); color: var(--kgdiff-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
453
+ .result mark { background: var(--kgdiff-accent-soft); color: inherit; border-radius: 3px; padding: 0 1px; }
506
454
  .result .status { font-size: 11.5px; color: var(--fam-text); flex: none; }
507
455
 
508
456
  /* ── 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); }
457
+ .detail { 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); }
510
458
  .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); }
459
+ .detail-empty { padding: 16px; display: flex; align-items: center; gap: 12px; color: var(--kgdiff-text-3); font-size: 12.5px; }
460
+ .detail-empty .icon { color: var(--kgdiff-text-3); }
513
461
  .detail-empty .sub { font-size: 11.5px; margin-top: 2px; }
514
462
  .detail-scroll { flex: 1; min-height: 0; padding: 14px 16px 20px; }
515
463
  .d-top { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
@@ -517,104 +465,104 @@ ${familyRules}
517
465
  .fam-pill .g { font-weight: 700; }
518
466
  .d-nav { margin-left: auto; display: flex; align-items: center; gap: 2px; }
519
467
  .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; }
468
+ .d-sub { margin-top: 4px; font-size: 12.5px; color: var(--kgdiff-text-2); overflow-wrap: anywhere; }
469
+ .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-2); box-shadow: inset 0 0 0 1px var(--kgdiff-border); margin-right: 4px; }
470
+ .rv-btn:hover { color: var(--kgdiff-text); background: var(--kgdiff-hover); }
471
+ .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
472
  .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); }
473
+ .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); }
474
+ .rv-btn[aria-pressed="true"] .ring { background: var(--kgdiff-accent); box-shadow: none; color: var(--kgdiff-bg); }
527
475
  .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; }
476
+ .section > h3 { margin: 0 0 8px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-3); display: flex; align-items: center; gap: 6px; }
529
477
  .section > h3 .n { font-weight: 500; letter-spacing: 0; }
530
478
  .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); }
479
+ .kv dt { color: var(--kgdiff-text-3); }
532
480
  .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; }
481
+ .id-text { font: 12px/1.5 var(--kgdiff-mono); color: var(--kgdiff-text); overflow-wrap: anywhere; }
534
482
  .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; }
483
+ .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; }
484
+ .vchip.add { color: var(--kgdiff-added-text); background: var(--kgdiff-added-soft); box-shadow: none; }
485
+ .vchip.del { color: var(--kgdiff-removed-text); background: var(--kgdiff-removed-soft); box-shadow: none; text-decoration: line-through; text-decoration-thickness: 1px; }
538
486
  .vchip .sign { font-weight: 700; text-decoration: none; display: inline-block; }
539
487
  .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); }
488
+ .pair .b, .line.old { color: var(--kgdiff-text-2); }
489
+ .pair .arr { color: var(--kgdiff-text-3); }
542
490
  .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; }
491
+ .line .sign { font: 600 12px/1 var(--kgdiff-mono); text-align: center; }
492
+ .line.old .sign { color: var(--kgdiff-removed-text); }
493
+ .line.new .sign { color: var(--kgdiff-added-text); }
494
+ 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; }
495
+ ins.hl { text-decoration: none; background: var(--kgdiff-added-soft); color: var(--kgdiff-added-text); border-radius: 3px; padding: 0 1px; }
548
496
  .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; }
497
+ .attrs td { vertical-align: baseline; padding: 6px 8px 6px 0; border-top: 1px solid var(--kgdiff-border); overflow-wrap: anywhere; }
550
498
  .attrs tr:first-child td { border-top: 0; padding-top: 0; }
551
499
  .attrs td > * + * { margin-top: 4px; }
552
- .attrs td.k { color: var(--kg-text-2); }
500
+ .attrs td.k { color: var(--kgdiff-text-2); }
553
501
  .attrs col.k { width: 28%; }
554
502
  .val { white-space: pre-wrap; }
555
503
  .val.clamped { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
556
504
  .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); }
505
+ .disclosure { display: inline-flex; align-items: center; gap: 4px; min-height: 28px; margin-top: 4px; font-size: 12px; color: var(--kgdiff-text-2); border-radius: 4px; }
506
+ .disclosure:hover { color: var(--kgdiff-text); }
507
+ .disclosure .icon { width: 12px; height: 12px; transition: transform var(--kgdiff-base) var(--kgdiff-ease); }
560
508
  .disclosure[aria-expanded="true"] .icon { transform: rotate(180deg); }
561
509
  .rels { display: grid; gap: 2px; }
562
510
  .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; }
511
+ .rel:hover { background: var(--kgdiff-hover); }
512
+ .rel .sign { flex: none; width: 12px; font: 600 12px/1.5 var(--kgdiff-mono); color: var(--fam-text); text-align: center; }
565
513
  .rel .s { min-width: 0; overflow-wrap: anywhere; }
566
514
  .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); }
515
+ .rel .pred { color: var(--kgdiff-text-2); white-space: nowrap; }
516
+ .rel .was { color: var(--kgdiff-text-3); }
517
+ .rel.removed .s { text-decoration: line-through; text-decoration-color: var(--kgdiff-text-3); color: var(--kgdiff-text-2); }
570
518
  .nodes { display: grid; gap: 2px; }
571
519
  .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); }
520
+ .node-link:hover { background: var(--kgdiff-hover); }
521
+ .node-link[aria-current="true"] { background: var(--kgdiff-hover); box-shadow: inset 0 0 0 1px var(--kgdiff-border-strong); }
574
522
  .node-link .sdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--fam-base); }
575
523
  .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; }
524
+ .node-link .meta { flex: none; color: var(--kgdiff-text-3); font-size: 11.5px; }
577
525
  .comp { display: grid; gap: 6px; }
578
526
  .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; }
527
+ .comp-row .arrow { color: var(--kgdiff-text-3); }
528
+ .comp-row .note { color: var(--kgdiff-text-3); font-size: 11.5px; }
581
529
  .bars { display: grid; gap: 6px; }
582
530
  .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; }
531
+ .bar-row .lbl { color: var(--kgdiff-text-2); }
532
+ .bar-row .track { height: 6px; border-radius: 3px; background: var(--kgdiff-track); overflow: hidden; }
533
+ .bar-row .fill { height: 100%; border-radius: 3px; background: var(--kgdiff-identity); }
534
+ .bar-row .v { text-align: right; color: var(--kgdiff-text-2); font-variant-numeric: tabular-nums; }
535
+ .note-line { font-size: 12px; color: var(--kgdiff-text-3); margin-top: 6px; }
588
536
 
589
537
  /* ── Popovers ────────────────────────────────────────────────────────────── */
590
538
  .menu {
591
539
  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);
540
+ animation: pop var(--kgdiff-fast) var(--kgdiff-ease);
593
541
  }
594
542
  @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; }
543
+ .menu-group + .menu-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--kgdiff-border); }
544
+ .menu-label { padding: 6px 8px 4px; font-size: 11px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kgdiff-text-3); }
545
+ .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; }
546
+ .menu-item:hover, .menu-item:focus-visible { background: var(--kgdiff-hover); outline: none; }
547
+ .menu-item .icon { color: var(--kgdiff-text-2); }
548
+ .menu-item .check { margin-left: auto; color: var(--kgdiff-accent-text); opacity: 0; }
601
549
  .menu-item[aria-checked="true"] .check { opacity: 1; }
602
550
  .menu-item .sw { display: inline-flex; gap: 2px; }
603
551
  .menu-item .sw i { width: 6px; height: 6px; border-radius: 50%; display: block; }
604
552
 
605
553
  .overlay-scrim {
606
554
  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);
555
+ background: var(--kgdiff-scrim); animation: fade var(--kgdiff-fast) var(--kgdiff-ease);
608
556
  }
609
557
  @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); }
558
+ .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
559
  .dialog-head { display: flex; align-items: center; gap: 10px; padding: 14px 12px 10px 20px; }
612
560
  .dialog-head h2 { margin: 0; font-size: 15px; font-weight: 650; }
613
561
  .dialog-head .icon-btn { margin-left: auto; }
614
562
  .dialog-body { padding: 0 20px 18px; }
615
563
  .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; }
564
+ .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-2); }
565
+ .key-row .ks { display: inline-flex; align-items: center; gap: 3px; flex: none; color: var(--kgdiff-text-3); font-size: 11px; }
618
566
 
619
567
  /* ── chrome="none": the graph, its hover tooltip and the legend line only ──── */
620
568
  .root[data-chrome="none"] .hud, .root[data-chrome="none"] .sidebar, .root[data-chrome="none"] .dock,
@@ -624,9 +572,9 @@ ins.hl { text-decoration: none; background: var(--kg-added-soft); color: var(--k
624
572
  /* ── Compact (< 760 px): bottom sheet ────────────────────────────────────── */
625
573
  .root[data-compact="true"] .body { flex-direction: column; }
626
574
  .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);
575
+ width: auto; height: var(--sheet-h, 40%); min-height: 0; border-left: 0; border-top: 1px solid var(--kgdiff-border);
628
576
  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);
577
+ transition: height var(--kgdiff-base) var(--kgdiff-ease);
630
578
  }
631
579
  .root[data-compact="true"] .sidebar.dragging { transition: none; }
632
580
  .root[data-compact="true"] .sidebar[data-open="false"] { height: 0; border-top-color: transparent; box-shadow: none; }
@@ -634,7 +582,7 @@ ins.hl { text-decoration: none; background: var(--kg-added-soft); color: var(--k
634
582
  .root[data-compact="true"] .sheet-handle {
635
583
  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
584
  }
637
- .root[data-compact="true"] .sheet-handle::before { content: ""; width: 36px; height: 4px; border-radius: 2px; background: var(--kg-control-border); }
585
+ .root[data-compact="true"] .sheet-handle::before { content: ""; width: 36px; height: 4px; border-radius: 2px; background: var(--kgdiff-control-border); }
638
586
  .root[data-compact="true"] .sb-head { padding-top: 4px; }
639
587
  .root[data-compact="true"] .versions, .root[data-compact="true"] .header-sep,
640
588
  .root[data-compact="true"] .brand-name { display: none; }
@@ -654,3 +602,27 @@ ins.hl { text-decoration: none; background: var(--kg-added-soft); color: var(--k
654
602
  .skeleton-grid, .shimmer { animation: none; }
655
603
  }
656
604
  `;
605
+
606
+ /**
607
+ * A stylesheet of `doc` holding `css`. A constructed sheet can only be adopted in the document
608
+ * it was made for, so it is made with that document's own `CSSStyleSheet` (a viewer moved into
609
+ * an iframe or a popup gets sheets of its new document).
610
+ */
611
+ export function sheetFor(doc: Document, css: string): CSSStyleSheet {
612
+ const Sheet = (doc.defaultView as (Window & typeof globalThis) | null)?.CSSStyleSheet;
613
+ const sheet = new (Sheet ?? CSSStyleSheet)();
614
+ sheet.replaceSync(css);
615
+ return sheet;
616
+ }
617
+
618
+ const shared = new WeakMap<Document, CSSStyleSheet>();
619
+
620
+ /** `STYLES` as a stylesheet of `doc`, parsed once per document. */
621
+ export function stylesheet(doc: Document): CSSStyleSheet {
622
+ let sheet = shared.get(doc);
623
+ if (!sheet) {
624
+ sheet = sheetFor(doc, STYLES);
625
+ shared.set(doc, sheet);
626
+ }
627
+ return sheet;
628
+ }