@kgdiff/viewer 0.5.0 → 0.6.0

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