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