@memberjunction/ng-clustering 5.43.0 → 5.44.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.
@@ -32,6 +32,7 @@ import { CLUSTER_COLORS, } from './clustering.types';
32
32
  import * as i0 from "@angular/core";
33
33
  import * as i1 from "@angular/forms";
34
34
  import * as i2 from "@memberjunction/ng-entity-card";
35
+ import * as i3 from "@memberjunction/ng-ui-components";
35
36
  const _c0 = ["svgElement"];
36
37
  const _forTrack0 = ($index, $item) => $item.ClusterId;
37
38
  const _forTrack1 = ($index, $item) => $item.Name;
@@ -43,19 +44,12 @@ function ClusterScatterComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
43
44
  i0.ɵɵelementEnd()()();
44
45
  } }
45
46
  function ClusterScatterComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
46
- i0.ɵɵelementStart(0, "div", 3);
47
- i0.ɵɵelement(1, "i", 11);
48
- i0.ɵɵelementStart(2, "p");
49
- i0.ɵɵtext(3, "No data to display");
50
- i0.ɵɵelementEnd();
51
- i0.ɵɵelementStart(4, "p", 12);
52
- i0.ɵɵtext(5, "Configure and run clustering to see results");
53
- i0.ɵɵelementEnd()();
47
+ i0.ɵɵelement(0, "mj-empty-state", 3);
54
48
  } }
55
49
  function ClusterScatterComponent_Conditional_3_For_4_Template(rf, ctx) { if (rf & 1) {
56
50
  i0.ɵɵnamespaceSVG();
57
- i0.ɵɵelementStart(0, "radialGradient", 14);
58
- i0.ɵɵelement(1, "stop", 17)(2, "stop", 18);
51
+ i0.ɵɵelementStart(0, "radialGradient", 12);
52
+ i0.ɵɵelement(1, "stop", 15)(2, "stop", 16);
59
53
  i0.ɵɵelementEnd();
60
54
  } if (rf & 2) {
61
55
  const centroid_r3 = ctx.$implicit;
@@ -88,7 +82,7 @@ function ClusterScatterComponent_Conditional_3_Conditional_10_For_1_Template(rf,
88
82
  } }
89
83
  function ClusterScatterComponent_Conditional_3_Conditional_10_For_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
90
84
  i0.ɵɵnamespaceSVG();
91
- i0.ɵɵelementStart(0, "text", 19);
85
+ i0.ɵɵelementStart(0, "text", 17);
92
86
  i0.ɵɵtext(1);
93
87
  i0.ɵɵelementEnd();
94
88
  } if (rf & 2) {
@@ -98,7 +92,7 @@ function ClusterScatterComponent_Conditional_3_Conditional_10_For_3_Conditional_
98
92
  i0.ɵɵtextInterpolate(ctx);
99
93
  } }
100
94
  function ClusterScatterComponent_Conditional_3_Conditional_10_For_3_Template(rf, ctx) { if (rf & 1) {
101
- i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_10_For_3_Conditional_0_Template, 2, 4, ":svg:text", 19);
95
+ i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_10_For_3_Conditional_0_Template, 2, 4, ":svg:text", 17);
102
96
  } if (rf & 2) {
103
97
  let tmp_13_0;
104
98
  const centroid_r7 = ctx.$implicit;
@@ -117,7 +111,7 @@ function ClusterScatterComponent_Conditional_3_Conditional_10_Template(rf, ctx)
117
111
  function ClusterScatterComponent_Conditional_3_For_12_Template(rf, ctx) { if (rf & 1) {
118
112
  const _r8 = i0.ɵɵgetCurrentView();
119
113
  i0.ɵɵnamespaceSVG();
120
- i0.ɵɵelementStart(0, "circle", 20);
114
+ i0.ɵɵelementStart(0, "circle", 18);
121
115
  i0.ɵɵlistener("mouseenter", function ClusterScatterComponent_Conditional_3_For_12_Template_circle_mouseenter_0_listener($event) { const point_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnPointMouseEnter(point_r9, $event)); })("mouseleave", function ClusterScatterComponent_Conditional_3_For_12_Template_circle_mouseleave_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnPointMouseLeave()); })("click", function ClusterScatterComponent_Conditional_3_For_12_Template_circle_click_0_listener($event) { const point_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.OnPointClick(point_r9); return i0.ɵɵresetView($event.stopPropagation()); });
122
116
  i0.ɵɵelementEnd();
123
117
  } if (rf & 2) {
@@ -127,7 +121,7 @@ function ClusterScatterComponent_Conditional_3_For_12_Template(rf, ctx) { if (rf
127
121
  } }
128
122
  function ClusterScatterComponent_Conditional_3_Conditional_13_For_1_Conditional_0_Template(rf, ctx) { if (rf & 1) {
129
123
  i0.ɵɵnamespaceSVG();
130
- i0.ɵɵelementStart(0, "text", 21);
124
+ i0.ɵɵelementStart(0, "text", 19);
131
125
  i0.ɵɵtext(1);
132
126
  i0.ɵɵelementEnd();
133
127
  } if (rf & 2) {
@@ -137,7 +131,7 @@ function ClusterScatterComponent_Conditional_3_Conditional_13_For_1_Conditional_
137
131
  i0.ɵɵtextInterpolate(ctx);
138
132
  } }
139
133
  function ClusterScatterComponent_Conditional_3_Conditional_13_For_1_Template(rf, ctx) { if (rf & 1) {
140
- i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_13_For_1_Conditional_0_Template, 2, 4, ":svg:text", 21);
134
+ i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_13_For_1_Conditional_0_Template, 2, 4, ":svg:text", 19);
141
135
  } if (rf & 2) {
142
136
  let tmp_13_0;
143
137
  const lbl_r10 = ctx.$implicit;
@@ -152,14 +146,14 @@ function ClusterScatterComponent_Conditional_3_Conditional_13_Template(rf, ctx)
152
146
  } }
153
147
  function ClusterScatterComponent_Conditional_3_For_15_Conditional_0_Template(rf, ctx) { if (rf & 1) {
154
148
  i0.ɵɵnamespaceSVG();
155
- i0.ɵɵelement(0, "circle", 22);
149
+ i0.ɵɵelement(0, "circle", 20);
156
150
  } if (rf & 2) {
157
151
  const point_r11 = i0.ɵɵnextContext().$implicit;
158
152
  const ctx_r1 = i0.ɵɵnextContext(2);
159
153
  i0.ɵɵattribute("cx", ctx_r1.PX(point_r11))("cy", ctx_r1.PY(point_r11))("r", ctx_r1.HighlightRadius + 2)("stroke", ctx_r1.GetPointColor(point_r11));
160
154
  } }
161
155
  function ClusterScatterComponent_Conditional_3_For_15_Template(rf, ctx) { if (rf & 1) {
162
- i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_For_15_Conditional_0_Template, 1, 4, ":svg:circle", 22);
156
+ i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_For_15_Conditional_0_Template, 1, 4, ":svg:circle", 20);
163
157
  } if (rf & 2) {
164
158
  const point_r11 = ctx.$implicit;
165
159
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -167,14 +161,14 @@ function ClusterScatterComponent_Conditional_3_For_15_Template(rf, ctx) { if (rf
167
161
  } }
168
162
  function ClusterScatterComponent_Conditional_3_Conditional_16_For_1_Conditional_0_Template(rf, ctx) { if (rf & 1) {
169
163
  i0.ɵɵnamespaceSVG();
170
- i0.ɵɵelement(0, "circle", 23);
164
+ i0.ɵɵelement(0, "circle", 21);
171
165
  } if (rf & 2) {
172
166
  const point_r12 = i0.ɵɵnextContext().$implicit;
173
167
  const ctx_r1 = i0.ɵɵnextContext(3);
174
168
  i0.ɵɵattribute("cx", ctx_r1.PX(point_r12))("cy", ctx_r1.PY(point_r12))("r", ctx_r1.HighlightRadius + 4)("stroke", ctx_r1.GetPointColor(point_r12));
175
169
  } }
176
170
  function ClusterScatterComponent_Conditional_3_Conditional_16_For_1_Template(rf, ctx) { if (rf & 1) {
177
- i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_16_For_1_Conditional_0_Template, 1, 4, ":svg:circle", 23);
171
+ i0.ɵɵconditionalCreate(0, ClusterScatterComponent_Conditional_3_Conditional_16_For_1_Conditional_0_Template, 1, 4, ":svg:circle", 21);
178
172
  } if (rf & 2) {
179
173
  const point_r12 = ctx.$implicit;
180
174
  const ctx_r1 = i0.ɵɵnextContext(3);
@@ -189,17 +183,17 @@ function ClusterScatterComponent_Conditional_3_Conditional_16_Template(rf, ctx)
189
183
  function ClusterScatterComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
190
184
  const _r1 = i0.ɵɵgetCurrentView();
191
185
  i0.ɵɵnamespaceSVG();
192
- i0.ɵɵelementStart(0, "svg", 13, 0);
186
+ i0.ɵɵelementStart(0, "svg", 11, 0);
193
187
  i0.ɵɵlistener("wheel", function ClusterScatterComponent_Conditional_3_Template_svg_wheel_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnWheel($event)); })("mousedown", function ClusterScatterComponent_Conditional_3_Template_svg_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnMouseDown($event)); });
194
188
  i0.ɵɵelementStart(2, "defs");
195
- i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_3_For_4_Template, 3, 3, ":svg:radialGradient", 14, i0.ɵɵcomponentInstance().TrackCentroidBy, true);
189
+ i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_3_For_4_Template, 3, 3, ":svg:radialGradient", 12, i0.ɵɵcomponentInstance().TrackCentroidBy, true);
196
190
  i0.ɵɵelementEnd();
197
- i0.ɵɵelementStart(5, "g", 15);
191
+ i0.ɵɵelementStart(5, "g", 13);
198
192
  i0.ɵɵrepeaterCreate(6, ClusterScatterComponent_Conditional_3_For_7_Template, 1, 4, ":svg:line", null, i0.ɵɵrepeaterTrackByIdentity);
199
193
  i0.ɵɵrepeaterCreate(8, ClusterScatterComponent_Conditional_3_For_9_Template, 1, 4, ":svg:line", null, i0.ɵɵrepeaterTrackByIdentity);
200
194
  i0.ɵɵelementEnd();
201
195
  i0.ɵɵconditionalCreate(10, ClusterScatterComponent_Conditional_3_Conditional_10_Template, 4, 0);
202
- i0.ɵɵrepeaterCreate(11, ClusterScatterComponent_Conditional_3_For_12_Template, 1, 5, ":svg:circle", 16, i0.ɵɵcomponentInstance().TrackPointBy, true);
196
+ i0.ɵɵrepeaterCreate(11, ClusterScatterComponent_Conditional_3_For_12_Template, 1, 5, ":svg:circle", 14, i0.ɵɵcomponentInstance().TrackPointBy, true);
203
197
  i0.ɵɵconditionalCreate(13, ClusterScatterComponent_Conditional_3_Conditional_13_Template, 2, 0);
204
198
  i0.ɵɵrepeaterCreate(14, ClusterScatterComponent_Conditional_3_For_15_Template, 1, 1, null, null, i0.ɵɵcomponentInstance().TrackPointBy, true);
205
199
  i0.ɵɵconditionalCreate(16, ClusterScatterComponent_Conditional_3_Conditional_16_Template, 2, 0);
@@ -228,22 +222,22 @@ function ClusterScatterComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
228
222
  function ClusterScatterComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
229
223
  const _r13 = i0.ɵɵgetCurrentView();
230
224
  i0.ɵɵelementStart(0, "div", 5);
231
- i0.ɵɵelement(1, "i", 24);
225
+ i0.ɵɵelement(1, "i", 22);
232
226
  i0.ɵɵelementStart(2, "span");
233
227
  i0.ɵɵtext(3, "3D \u00B7 drag to rotate");
234
228
  i0.ɵɵelementEnd();
235
- i0.ɵɵelementStart(4, "button", 25);
229
+ i0.ɵɵelementStart(4, "button", 23);
236
230
  i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_4_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ResetRotation()); });
237
- i0.ɵɵelement(5, "i", 26);
231
+ i0.ɵɵelement(5, "i", 24);
238
232
  i0.ɵɵelementEnd()();
239
233
  } }
240
234
  function ClusterScatterComponent_Conditional_5_For_4_Template(rf, ctx) { if (rf & 1) {
241
- i0.ɵɵelementStart(0, "div", 28);
242
- i0.ɵɵelement(1, "div", 29);
243
- i0.ɵɵelementStart(2, "span", 30);
235
+ i0.ɵɵelementStart(0, "div", 26);
236
+ i0.ɵɵelement(1, "div", 27);
237
+ i0.ɵɵelementStart(2, "span", 28);
244
238
  i0.ɵɵtext(3);
245
239
  i0.ɵɵelementEnd();
246
- i0.ɵɵelementStart(4, "span", 31);
240
+ i0.ɵɵelementStart(4, "span", 29);
247
241
  i0.ɵɵtext(5);
248
242
  i0.ɵɵelementEnd()();
249
243
  } if (rf & 2) {
@@ -256,10 +250,10 @@ function ClusterScatterComponent_Conditional_5_For_4_Template(rf, ctx) { if (rf
256
250
  i0.ɵɵtextInterpolate(ent_r14.Count);
257
251
  } }
258
252
  function ClusterScatterComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
259
- i0.ɵɵelementStart(0, "div", 6)(1, "div", 27);
253
+ i0.ɵɵelementStart(0, "div", 6)(1, "div", 25);
260
254
  i0.ɵɵtext(2, "Entities");
261
255
  i0.ɵɵelementEnd();
262
- i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_5_For_4_Template, 6, 4, "div", 28, _forTrack1);
256
+ i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_5_For_4_Template, 6, 4, "div", 26, _forTrack1);
263
257
  i0.ɵɵelementEnd();
264
258
  } if (rf & 2) {
265
259
  const ctx_r1 = i0.ɵɵnextContext();
@@ -268,7 +262,7 @@ function ClusterScatterComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
268
262
  } }
269
263
  function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
270
264
  const _r17 = i0.ɵɵgetCurrentView();
271
- i0.ɵɵelementStart(0, "input", 34);
265
+ i0.ɵɵelementStart(0, "input", 32);
272
266
  i0.ɵɵtwoWayListener("ngModelChange", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingLabelDraft, $event) || (ctx_r1.EditingLabelDraft = $event); return i0.ɵɵresetView($event); });
273
267
  i0.ɵɵlistener("keydown", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_keydown_0_listener($event) { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnLabelEditKeydown($event, cluster_r16)); })("blur", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_blur_0_listener() { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.CommitLabelEdit(cluster_r16)); })("click", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r17); return i0.ɵɵresetView($event.stopPropagation()); });
274
268
  i0.ɵɵelementEnd();
@@ -278,12 +272,12 @@ function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template(rf,
278
272
  } }
279
273
  function ClusterScatterComponent_Conditional_6_For_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
280
274
  const _r18 = i0.ɵɵgetCurrentView();
281
- i0.ɵɵelementStart(0, "span", 30);
275
+ i0.ɵɵelementStart(0, "span", 28);
282
276
  i0.ɵɵtext(1);
283
277
  i0.ɵɵelementEnd();
284
- i0.ɵɵelementStart(2, "button", 35);
278
+ i0.ɵɵelementStart(2, "button", 33);
285
279
  i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_6_For_4_Conditional_3_Template_button_click_2_listener($event) { i0.ɵɵrestoreView(_r18); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.StartLabelEdit(cluster_r16, $event)); });
286
- i0.ɵɵelement(3, "i", 36);
280
+ i0.ɵɵelement(3, "i", 34);
287
281
  i0.ɵɵelementEnd();
288
282
  } if (rf & 2) {
289
283
  const cluster_r16 = i0.ɵɵnextContext().$implicit;
@@ -292,11 +286,11 @@ function ClusterScatterComponent_Conditional_6_For_4_Conditional_3_Template(rf,
292
286
  } }
293
287
  function ClusterScatterComponent_Conditional_6_For_4_Template(rf, ctx) { if (rf & 1) {
294
288
  const _r15 = i0.ɵɵgetCurrentView();
295
- i0.ɵɵelementStart(0, "div", 32);
289
+ i0.ɵɵelementStart(0, "div", 30);
296
290
  i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_6_For_4_Template_div_click_0_listener() { const cluster_r16 = i0.ɵɵrestoreView(_r15).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnClusterLegendClick(cluster_r16)); });
297
- i0.ɵɵelement(1, "div", 29);
298
- i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template, 1, 1, "input", 33)(3, ClusterScatterComponent_Conditional_6_For_4_Conditional_3_Template, 4, 1);
299
- i0.ɵɵelementStart(4, "span", 31);
291
+ i0.ɵɵelement(1, "div", 27);
292
+ i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template, 1, 1, "input", 31)(3, ClusterScatterComponent_Conditional_6_For_4_Conditional_3_Template, 4, 1);
293
+ i0.ɵɵelementStart(4, "span", 29);
300
294
  i0.ɵɵtext(5);
301
295
  i0.ɵɵelementEnd()();
302
296
  } if (rf & 2) {
@@ -310,10 +304,10 @@ function ClusterScatterComponent_Conditional_6_For_4_Template(rf, ctx) { if (rf
310
304
  i0.ɵɵtextInterpolate(cluster_r16.MemberCount);
311
305
  } }
312
306
  function ClusterScatterComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
313
- i0.ɵɵelementStart(0, "div", 6)(1, "div", 27);
307
+ i0.ɵɵelementStart(0, "div", 6)(1, "div", 25);
314
308
  i0.ɵɵtext(2, "Clusters");
315
309
  i0.ɵɵelementEnd();
316
- i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_6_For_4_Template, 6, 4, "div", 28, i0.ɵɵcomponentInstance().TrackClusterBy, true);
310
+ i0.ɵɵrepeaterCreate(3, ClusterScatterComponent_Conditional_6_For_4_Template, 6, 4, "div", 26, i0.ɵɵcomponentInstance().TrackClusterBy, true);
317
311
  i0.ɵɵelementEnd();
318
312
  } if (rf & 2) {
319
313
  const ctx_r1 = i0.ɵɵnextContext();
@@ -321,14 +315,14 @@ function ClusterScatterComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
321
315
  i0.ɵɵrepeater(ctx_r1.Clusters);
322
316
  } }
323
317
  function ClusterScatterComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
324
- i0.ɵɵelementStart(0, "div", 37);
325
- i0.ɵɵelement(1, "mj-entity-card", 38);
326
- i0.ɵɵelementStart(2, "div", 39);
327
- i0.ɵɵelement(3, "i", 40);
318
+ i0.ɵɵelementStart(0, "div", 35);
319
+ i0.ɵɵelement(1, "mj-entity-card", 36);
320
+ i0.ɵɵelementStart(2, "div", 37);
321
+ i0.ɵɵelement(3, "i", 38);
328
322
  i0.ɵɵtext(4);
329
323
  i0.ɵɵelementEnd();
330
- i0.ɵɵelementStart(5, "div", 41);
331
- i0.ɵɵelement(6, "i", 42);
324
+ i0.ɵɵelementStart(5, "div", 39);
325
+ i0.ɵɵelement(6, "i", 40);
332
326
  i0.ɵɵtext(7, " Click to view details ");
333
327
  i0.ɵɵelementEnd()();
334
328
  } if (rf & 2) {
@@ -340,7 +334,7 @@ function ClusterScatterComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
340
334
  i0.ɵɵtextInterpolate1(" ", ctx_r1.GetClusterLabel(ctx_r1.HoveredPoint), " ");
341
335
  } }
342
336
  function ClusterScatterComponent_Conditional_9_Conditional_2_Template(rf, ctx) { if (rf & 1) {
343
- i0.ɵɵelementStart(0, "div", 45);
337
+ i0.ɵɵelementStart(0, "div", 43);
344
338
  i0.ɵɵelement(1, "i");
345
339
  i0.ɵɵelementEnd();
346
340
  } if (rf & 2) {
@@ -349,7 +343,7 @@ function ClusterScatterComponent_Conditional_9_Conditional_2_Template(rf, ctx) {
349
343
  i0.ɵɵclassMap(ctx_r1.SelectedPoint.Metadata["EntityIcon"]);
350
344
  } }
351
345
  function ClusterScatterComponent_Conditional_9_Conditional_6_Template(rf, ctx) { if (rf & 1) {
352
- i0.ɵɵelementStart(0, "span", 48);
346
+ i0.ɵɵelementStart(0, "span", 46);
353
347
  i0.ɵɵtext(1);
354
348
  i0.ɵɵelementEnd();
355
349
  } if (rf & 2) {
@@ -357,32 +351,42 @@ function ClusterScatterComponent_Conditional_9_Conditional_6_Template(rf, ctx) {
357
351
  i0.ɵɵadvance();
358
352
  i0.ɵɵtextInterpolate(ctx_r1.SelectedPoint.Metadata["Entity"]);
359
353
  } }
360
- function ClusterScatterComponent_Conditional_9_Conditional_20_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
361
- i0.ɵɵelement(0, "i", 65);
354
+ function ClusterScatterComponent_Conditional_9_ng_template_15_Template(rf, ctx) { if (rf & 1) {
355
+ i0.ɵɵtext(0, " Cluster Members ");
356
+ i0.ɵɵelementStart(1, "span", 58);
357
+ i0.ɵɵtext(2);
358
+ i0.ɵɵelementEnd();
359
+ } if (rf & 2) {
360
+ const ctx_r1 = i0.ɵɵnextContext(2);
361
+ i0.ɵɵadvance(2);
362
+ i0.ɵɵtextInterpolate(ctx_r1.ClusterMembers.length);
363
+ } }
364
+ function ClusterScatterComponent_Conditional_9_ng_template_16_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
365
+ i0.ɵɵelement(0, "i", 64);
362
366
  } if (rf & 2) {
363
367
  const member_r21 = i0.ɵɵnextContext().$implicit;
364
368
  i0.ɵɵclassMap(member_r21.Metadata["EntityIcon"]);
365
369
  } }
366
- function ClusterScatterComponent_Conditional_9_Conditional_20_For_2_Template(rf, ctx) { if (rf & 1) {
370
+ function ClusterScatterComponent_Conditional_9_ng_template_16_For_2_Template(rf, ctx) { if (rf & 1) {
367
371
  const _r20 = i0.ɵɵgetCurrentView();
368
- i0.ɵɵelementStart(0, "div", 62);
369
- i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_Conditional_20_For_2_Template_div_click_0_listener() { const member_r21 = i0.ɵɵrestoreView(_r20).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnClusterMemberClick(member_r21)); });
370
- i0.ɵɵconditionalCreate(1, ClusterScatterComponent_Conditional_9_Conditional_20_For_2_Conditional_1_Template, 1, 2, "i", 63);
371
- i0.ɵɵelementStart(2, "span", 64);
372
+ i0.ɵɵelementStart(0, "div", 61);
373
+ i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_ng_template_16_For_2_Template_div_click_0_listener() { const member_r21 = i0.ɵɵrestoreView(_r20).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnClusterMemberClick(member_r21)); });
374
+ i0.ɵɵconditionalCreate(1, ClusterScatterComponent_Conditional_9_ng_template_16_For_2_Conditional_1_Template, 1, 2, "i", 62);
375
+ i0.ɵɵelementStart(2, "span", 63);
372
376
  i0.ɵɵtext(3);
373
377
  i0.ɵɵelementEnd()();
374
378
  } if (rf & 2) {
375
379
  const member_r21 = ctx.$implicit;
376
380
  const ctx_r1 = i0.ɵɵnextContext(3);
377
- i0.ɵɵclassProp("active", member_r21.VectorKey === ctx_r1.SelectedPoint.VectorKey);
381
+ i0.ɵɵclassProp("active", member_r21.VectorKey === (ctx_r1.SelectedPoint == null ? null : ctx_r1.SelectedPoint.VectorKey));
378
382
  i0.ɵɵadvance();
379
383
  i0.ɵɵconditional((member_r21.Metadata == null ? null : member_r21.Metadata["EntityIcon"]) ? 1 : -1);
380
384
  i0.ɵɵadvance(2);
381
385
  i0.ɵɵtextInterpolate(member_r21.Label);
382
386
  } }
383
- function ClusterScatterComponent_Conditional_9_Conditional_20_Template(rf, ctx) { if (rf & 1) {
384
- i0.ɵɵelementStart(0, "div", 58);
385
- i0.ɵɵrepeaterCreate(1, ClusterScatterComponent_Conditional_9_Conditional_20_For_2_Template, 4, 4, "div", 61, i0.ɵɵcomponentInstance().TrackMemberBy, true);
387
+ function ClusterScatterComponent_Conditional_9_ng_template_16_Template(rf, ctx) { if (rf & 1) {
388
+ i0.ɵɵelementStart(0, "div", 59);
389
+ i0.ɵɵrepeaterCreate(1, ClusterScatterComponent_Conditional_9_ng_template_16_For_2_Template, 4, 4, "div", 60, i0.ɵɵcomponentInstance().TrackMemberBy, true);
386
390
  i0.ɵɵelementEnd();
387
391
  } if (rf & 2) {
388
392
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -391,37 +395,30 @@ function ClusterScatterComponent_Conditional_9_Conditional_20_Template(rf, ctx)
391
395
  } }
392
396
  function ClusterScatterComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
393
397
  const _r19 = i0.ɵɵgetCurrentView();
394
- i0.ɵɵelementStart(0, "div", 43)(1, "div", 44);
395
- i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_9_Conditional_2_Template, 2, 2, "div", 45);
396
- i0.ɵɵelementStart(3, "div", 46)(4, "div", 47);
398
+ i0.ɵɵelementStart(0, "div", 41)(1, "div", 42);
399
+ i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_9_Conditional_2_Template, 2, 2, "div", 43);
400
+ i0.ɵɵelementStart(3, "div", 44)(4, "div", 45);
397
401
  i0.ɵɵtext(5);
398
402
  i0.ɵɵelementEnd();
399
- i0.ɵɵconditionalCreate(6, ClusterScatterComponent_Conditional_9_Conditional_6_Template, 2, 1, "span", 48);
403
+ i0.ɵɵconditionalCreate(6, ClusterScatterComponent_Conditional_9_Conditional_6_Template, 2, 1, "span", 46);
400
404
  i0.ɵɵelementEnd()();
401
- i0.ɵɵelementStart(7, "button", 49);
405
+ i0.ɵɵelementStart(7, "button", 47);
402
406
  i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseDetailPanel()); });
403
- i0.ɵɵelement(8, "i", 50);
407
+ i0.ɵɵelement(8, "i", 48);
404
408
  i0.ɵɵelementEnd()();
405
- i0.ɵɵelementStart(9, "div", 51);
406
- i0.ɵɵelement(10, "span", 52);
409
+ i0.ɵɵelementStart(9, "div", 49);
410
+ i0.ɵɵelement(10, "span", 50);
407
411
  i0.ɵɵtext(11);
408
412
  i0.ɵɵelementEnd();
409
- i0.ɵɵelement(12, "mj-entity-card", 53);
410
- i0.ɵɵelementStart(13, "div", 54)(14, "button", 55);
411
- i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleClusterMembers()); });
412
- i0.ɵɵelement(15, "i", 56);
413
- i0.ɵɵelementStart(16, "span");
414
- i0.ɵɵtext(17, "Cluster Members");
415
- i0.ɵɵelementEnd();
416
- i0.ɵɵelementStart(18, "span", 57);
417
- i0.ɵɵtext(19);
413
+ i0.ɵɵelement(12, "mj-entity-card", 51);
414
+ i0.ɵɵelementStart(13, "div", 52)(14, "mj-accordion-panel", 53);
415
+ i0.ɵɵtwoWayListener("ExpandedChange", function ClusterScatterComponent_Conditional_9_Template_mj_accordion_panel_ExpandedChange_14_listener($event) { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.ClusterMembersExpanded, $event) || (ctx_r1.ClusterMembersExpanded = $event); return i0.ɵɵresetView($event); });
416
+ i0.ɵɵtemplate(15, ClusterScatterComponent_Conditional_9_ng_template_15_Template, 3, 1, "ng-template", 54)(16, ClusterScatterComponent_Conditional_9_ng_template_16_Template, 3, 0, "ng-template", 55);
418
417
  i0.ɵɵelementEnd()();
419
- i0.ɵɵconditionalCreate(20, ClusterScatterComponent_Conditional_9_Conditional_20_Template, 3, 0, "div", 58);
420
- i0.ɵɵelementEnd();
421
- i0.ɵɵelementStart(21, "div", 59)(22, "button", 60);
422
- i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_Template_button_click_22_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnOpenRecordClick()); });
423
- i0.ɵɵelement(23, "i", 42);
424
- i0.ɵɵtext(24, " Open Record ");
418
+ i0.ɵɵelementStart(17, "div", 56)(18, "button", 57);
419
+ i0.ɵɵlistener("click", function ClusterScatterComponent_Conditional_9_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnOpenRecordClick()); });
420
+ i0.ɵɵelement(19, "i", 40);
421
+ i0.ɵɵtext(20, " Open Record ");
425
422
  i0.ɵɵelementEnd()();
426
423
  } if (rf & 2) {
427
424
  const ctx_r1 = i0.ɵɵnextContext();
@@ -437,12 +434,9 @@ function ClusterScatterComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
437
434
  i0.ɵɵtextInterpolate1(" ", ctx_r1.GetClusterLabel(ctx_r1.SelectedPoint), " ");
438
435
  i0.ɵɵadvance();
439
436
  i0.ɵɵproperty("EntityName", ctx_r1.EntityName)("Record", ctx_r1.SelectedPoint.Metadata);
440
- i0.ɵɵadvance(3);
441
- i0.ɵɵclassProp("fa-chevron-down", ctx_r1.ClusterMembersExpanded)("fa-chevron-right", !ctx_r1.ClusterMembersExpanded);
442
- i0.ɵɵadvance(4);
443
- i0.ɵɵtextInterpolate(ctx_r1.ClusterMembers.length);
444
- i0.ɵɵadvance();
445
- i0.ɵɵconditional(ctx_r1.ClusterMembersExpanded ? 20 : -1);
437
+ i0.ɵɵadvance(2);
438
+ i0.ɵɵproperty("Fill", true)("FlushBody", true);
439
+ i0.ɵɵtwoWayProperty("Expanded", ctx_r1.ClusterMembersExpanded);
446
440
  } }
447
441
  export class ClusterScatterComponent {
448
442
  constructor() {
@@ -1554,17 +1548,17 @@ export class ClusterScatterComponent {
1554
1548
  } if (rf & 2) {
1555
1549
  let _t;
1556
1550
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.svgRef = _t.first);
1557
- } }, inputs: { Points: "Points", Clusters: "Clusters", ColorBy: "ColorBy", IsLoading: "IsLoading", DotRadius: "DotRadius", DotOpacity: "DotOpacity", HighlightRadius: "HighlightRadius", ShowLegend: "ShowLegend", ShowTooltip: "ShowTooltip", TooltipFields: "TooltipFields", FieldPriority: "FieldPriority", FieldDisplayNames: "FieldDisplayNames", EntityName: "EntityName", ColorPalette: "ColorPalette", EnableZoom: "EnableZoom", EnablePan: "EnablePan", AnimateTransitions: "AnimateTransitions", SelectedPointIds: "SelectedPointIds", MinZoom: "MinZoom", MaxZoom: "MaxZoom" }, outputs: { PointClicked: "PointClicked", OpenRecordRequested: "OpenRecordRequested", PointHovered: "PointHovered", BeforePointClick: "BeforePointClick", BeforePointHover: "BeforePointHover", BeforeZoom: "BeforeZoom", AfterClusteringComplete: "AfterClusteringComplete", ClusterSelected: "ClusterSelected", SelectionChanged: "SelectionChanged", ViewportChanged: "ViewportChanged", LabelEdited: "LabelEdited" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 10, vars: 10, consts: [["svgElement", ""], [1, "scatter-container"], [1, "loading-overlay"], [1, "empty-state"], ["preserveAspectRatio", "xMidYMid meet", 1, "scatter-svg", 3, "no-transitions"], [1, "threed-hint"], [1, "legend"], [1, "tooltip", 3, "left", "top"], [1, "detail-panel"], [1, "loading-spinner"], [1, "fa-solid", "fa-circle-nodes", "fa-spin"], [1, "fa-solid", "fa-chart-scatter"], [1, "empty-hint"], ["preserveAspectRatio", "xMidYMid meet", 1, "scatter-svg", 3, "wheel", "mousedown"], ["cx", "50%", "cy", "50%", "r", "50%"], [1, "grid-lines"], [1, "data-point"], ["offset", "0%", "stop-opacity", "0.12"], ["offset", "100%", "stop-opacity", "0"], ["text-anchor", "middle", 1, "cluster-label"], [1, "data-point", 3, "mouseenter", "mouseleave", "click"], ["text-anchor", "middle", 1, "cluster-label", "cluster-label-3d"], ["fill", "none", "stroke-width", "2", "opacity", "0.8", 1, "selection-ring"], ["fill", "none", "stroke-width", "2.5", "opacity", "0.9"], [1, "fa-solid", "fa-cube"], ["title", "Reset view", 1, "threed-reset", 3, "click"], [1, "fa-solid", "fa-rotate-left"], [1, "legend-title"], [1, "legend-item"], [1, "legend-dot"], [1, "legend-label"], [1, "legend-count"], [1, "legend-item", 3, "click"], ["autofocus", "", 1, "legend-label-input", 3, "ngModel"], ["autofocus", "", 1, "legend-label-input", 3, "ngModelChange", "keydown", "blur", "click", "ngModel"], ["title", "Edit label", 1, "legend-edit-btn", 3, "click"], [1, "fa-solid", "fa-pencil"], [1, "tooltip"], ["Variant", "compact", 3, "EntityName", "Record"], [1, "tt-cluster"], [1, "fa-solid", "fa-circle", 2, "font-size", "7px", "vertical-align", "middle"], [1, "tt-action"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "detail-header"], [1, "detail-header-content"], [1, "detail-icon"], [1, "detail-header-text"], [1, "detail-name"], [1, "detail-entity-badge"], ["title", "Close", 1, "detail-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "detail-cluster"], [1, "detail-cluster-dot"], ["Variant", "detail", "CssClass", "detail-entity-card", 3, "EntityName", "Record"], [1, "detail-members-section"], [1, "detail-members-toggle", 3, "click"], [1, "fa-solid"], [1, "detail-members-count"], [1, "detail-members-list"], [1, "detail-footer"], [1, "detail-open-btn", 3, "click"], [1, "detail-member-row", 3, "active"], [1, "detail-member-row", 3, "click"], [1, "member-icon", 3, "class"], [1, "member-label"], [1, "member-icon"]], template: function ClusterScatterComponent_Template(rf, ctx) { if (rf & 1) {
1551
+ } }, inputs: { Points: "Points", Clusters: "Clusters", ColorBy: "ColorBy", IsLoading: "IsLoading", DotRadius: "DotRadius", DotOpacity: "DotOpacity", HighlightRadius: "HighlightRadius", ShowLegend: "ShowLegend", ShowTooltip: "ShowTooltip", TooltipFields: "TooltipFields", FieldPriority: "FieldPriority", FieldDisplayNames: "FieldDisplayNames", EntityName: "EntityName", ColorPalette: "ColorPalette", EnableZoom: "EnableZoom", EnablePan: "EnablePan", AnimateTransitions: "AnimateTransitions", SelectedPointIds: "SelectedPointIds", MinZoom: "MinZoom", MaxZoom: "MaxZoom" }, outputs: { PointClicked: "PointClicked", OpenRecordRequested: "OpenRecordRequested", PointHovered: "PointHovered", BeforePointClick: "BeforePointClick", BeforePointHover: "BeforePointHover", BeforeZoom: "BeforeZoom", AfterClusteringComplete: "AfterClusteringComplete", ClusterSelected: "ClusterSelected", SelectionChanged: "SelectionChanged", ViewportChanged: "ViewportChanged", LabelEdited: "LabelEdited" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 10, vars: 10, consts: [["svgElement", ""], [1, "scatter-container"], [1, "loading-overlay"], ["Icon", "fa-solid fa-chart-simple", "Title", "No data to display", "Message", "Configure and run clustering to see results", 1, "empty-state-fill"], ["preserveAspectRatio", "xMidYMid meet", 1, "scatter-svg", 3, "no-transitions"], [1, "threed-hint"], [1, "legend"], [1, "tooltip", 3, "left", "top"], [1, "detail-panel"], [1, "loading-spinner"], [1, "fa-solid", "fa-circle-nodes", "fa-spin"], ["preserveAspectRatio", "xMidYMid meet", 1, "scatter-svg", 3, "wheel", "mousedown"], ["cx", "50%", "cy", "50%", "r", "50%"], [1, "grid-lines"], [1, "data-point"], ["offset", "0%", "stop-opacity", "0.12"], ["offset", "100%", "stop-opacity", "0"], ["text-anchor", "middle", 1, "cluster-label"], [1, "data-point", 3, "mouseenter", "mouseleave", "click"], ["text-anchor", "middle", 1, "cluster-label", "cluster-label-3d"], ["fill", "none", "stroke-width", "2", "opacity", "0.8", 1, "selection-ring"], ["fill", "none", "stroke-width", "2.5", "opacity", "0.9"], [1, "fa-solid", "fa-cube"], ["title", "Reset view", 1, "threed-reset", 3, "click"], [1, "fa-solid", "fa-rotate-left"], [1, "legend-title"], [1, "legend-item"], [1, "legend-dot"], [1, "legend-label"], [1, "legend-count"], [1, "legend-item", 3, "click"], ["autofocus", "", 1, "legend-label-input", 3, "ngModel"], ["autofocus", "", 1, "legend-label-input", 3, "ngModelChange", "keydown", "blur", "click", "ngModel"], ["title", "Edit label", 1, "legend-edit-btn", 3, "click"], [1, "fa-solid", "fa-pencil"], [1, "tooltip"], ["Variant", "compact", 3, "EntityName", "Record"], [1, "tt-cluster"], [1, "fa-solid", "fa-circle", 2, "font-size", "7px", "vertical-align", "middle"], [1, "tt-action"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "detail-header"], [1, "detail-header-content"], [1, "detail-icon"], [1, "detail-header-text"], [1, "detail-name"], [1, "detail-entity-badge"], ["title", "Close", 1, "detail-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "detail-cluster"], [1, "detail-cluster-dot"], ["Variant", "detail", "CssClass", "detail-entity-card", 3, "EntityName", "Record"], [1, "detail-members-section"], ["Size", "sm", 3, "ExpandedChange", "Fill", "FlushBody", "Expanded"], ["mjAccordionTitle", ""], ["mjAccordionBody", ""], [1, "detail-footer"], [1, "detail-open-btn", 3, "click"], [1, "mj-accordion-badge", "mj-accordion-badge--right"], [1, "detail-members-list"], [1, "detail-member-row", 3, "active"], [1, "detail-member-row", 3, "click"], [1, "member-icon", 3, "class"], [1, "member-label"], [1, "member-icon"]], template: function ClusterScatterComponent_Template(rf, ctx) { if (rf & 1) {
1558
1552
  i0.ɵɵelementStart(0, "div", 1);
1559
1553
  i0.ɵɵconditionalCreate(1, ClusterScatterComponent_Conditional_1_Template, 5, 0, "div", 2);
1560
- i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_2_Template, 6, 0, "div", 3);
1554
+ i0.ɵɵconditionalCreate(2, ClusterScatterComponent_Conditional_2_Template, 1, 0, "mj-empty-state", 3);
1561
1555
  i0.ɵɵconditionalCreate(3, ClusterScatterComponent_Conditional_3_Template, 17, 6, ":svg:svg", 4);
1562
1556
  i0.ɵɵconditionalCreate(4, ClusterScatterComponent_Conditional_4_Template, 6, 0, "div", 5);
1563
1557
  i0.ɵɵconditionalCreate(5, ClusterScatterComponent_Conditional_5_Template, 5, 0, "div", 6);
1564
1558
  i0.ɵɵconditionalCreate(6, ClusterScatterComponent_Conditional_6_Template, 5, 0, "div", 6);
1565
1559
  i0.ɵɵconditionalCreate(7, ClusterScatterComponent_Conditional_7_Template, 8, 7, "div", 7);
1566
1560
  i0.ɵɵelementStart(8, "div", 8);
1567
- i0.ɵɵconditionalCreate(9, ClusterScatterComponent_Conditional_9_Template, 25, 14);
1561
+ i0.ɵɵconditionalCreate(9, ClusterScatterComponent_Conditional_9_Template, 21, 11);
1568
1562
  i0.ɵɵelementEnd()();
1569
1563
  } if (rf & 2) {
1570
1564
  i0.ɵɵadvance();
@@ -1585,11 +1579,11 @@ export class ClusterScatterComponent {
1585
1579
  i0.ɵɵclassProp("detail-panel-open", ctx.ShowDetailPanel && ctx.SelectedPoint);
1586
1580
  i0.ɵɵadvance();
1587
1581
  i0.ɵɵconditional(ctx.SelectedPoint ? 9 : -1);
1588
- } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJEntityCardComponent], styles: [":host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.scatter-container {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-page);\n}\n\n/* SVG */\n.scatter-svg {\n width: 100%;\n height: 100%;\n cursor: grab;\n user-select: none;\n}\n\n.scatter-svg:active {\n cursor: grabbing;\n}\n\n/* Grid lines */\n.grid-lines line {\n stroke: var(--mj-border-subtle);\n stroke-width: 0.5;\n}\n\n/* Data points */\n.data-point {\n cursor: pointer;\n transition: opacity 0.15s ease, r 0.15s ease;\n}\n\n.data-point:hover {\n filter: brightness(1.2);\n}\n\n/* Selection rings */\n.selection-ring {\n pointer-events: none;\n transition: r 0.15s ease;\n}\n\n/* Disable transitions when AnimateTransitions is false */\n.no-transitions .data-point,\n.no-transitions .selection-ring {\n transition: none;\n}\n\n/* Loading overlay */\n.loading-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-page) 80%, transparent);\n z-index: 5;\n}\n\n.loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.loading-spinner i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.empty-state {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n gap: 8px;\n}\n\n.empty-state i {\n font-size: 3rem;\n margin-bottom: 8px;\n opacity: 0.4;\n}\n\n.empty-state p {\n font-size: 1rem;\n margin: 0;\n}\n\n.empty-hint {\n font-size: 0.8rem !important;\n opacity: 0.7;\n}\n\n/* Legend */\n.legend {\n position: absolute;\n bottom: 20px;\n left: 20px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(12px);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 14px 18px;\n z-index: 3;\n}\n\n.legend-title {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n margin-bottom: 4px;\n color: var(--mj-text-primary);\n cursor: pointer;\n border-radius: 4px;\n padding: 2px 4px;\n margin-left: -4px;\n margin-right: -4px;\n transition: background 0.15s ease;\n}\n\n.legend-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.legend-label {\n flex: 1;\n}\n\n.legend-edit-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n background: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 0.6rem;\n border-radius: 4px;\n transition: color 0.15s ease;\n}\n\n.legend-item:hover .legend-edit-btn {\n display: inline-flex;\n}\n\n.legend-edit-btn:hover {\n color: var(--mj-brand-primary);\n}\n\n.legend-label-input {\n flex: 1;\n padding: 2px 4px;\n border: 1px solid var(--mj-border-focus);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.75rem;\n outline: none;\n min-width: 60px;\n}\n\n.legend-count {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n margin-left: auto;\n padding-left: 16px;\n}\n\n/* Cluster labels on chart */\n.cluster-label {\n font-size: 14px;\n font-weight: 600;\n opacity: 0.85;\n pointer-events: none;\n text-shadow:\n 0 0 6px var(--mj-bg-page),\n 0 0 12px var(--mj-bg-page),\n 1px 1px 2px var(--mj-bg-page);\n}\n\n/* Tooltip */\n.tooltip {\n position: fixed;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 96%, transparent);\n backdrop-filter: blur(16px);\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n padding: 14px 16px;\n width: 280px;\n box-shadow: 0 12px 40px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n pointer-events: none;\n z-index: 50;\n}\n\n.tt-header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.tt-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.tt-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.tt-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-top: 1px;\n}\n\n.tt-cluster {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.tt-fields {\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.tt-field-row {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-action {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.tt-action i {\n margin-right: 4px;\n}\n\n/* ================================================================\n Detail Panel (right slide-in)\n ================================================================ */\n\n.detail-panel {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 320px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(16px);\n border-left: 1px solid var(--mj-border-default);\n z-index: 10;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-bg-page) 30%, transparent);\n overflow: hidden;\n}\n\n.detail-panel-open {\n transform: translateX(0);\n}\n\n/* Header */\n.detail-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-header-content {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.detail-icon {\n width: 2.25rem;\n height: 2.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.95rem;\n flex-shrink: 0;\n}\n\n.detail-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.detail-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.detail-entity-badge {\n display: inline-block;\n margin-top: 4px;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 0.68rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.detail-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n padding: 4px 6px;\n border-radius: 4px;\n transition: color 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.detail-close:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n/* Cluster badge */\n.detail-cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.detail-cluster-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n/* Metadata fields */\n.detail-fields {\n padding: 4px 16px 8px;\n overflow-y: auto;\n flex-shrink: 0;\n}\n\n.detail-field {\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-field:last-child {\n border-bottom: none;\n}\n\n.detail-field-key {\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n}\n\n.detail-field-value {\n font-size: 0.8rem;\n color: var(--mj-text-primary);\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n/* Cluster Members Section */\n.detail-members-section {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.detail-members-toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-align: left;\n transition: background 0.15s ease;\n}\n\n.detail-members-toggle:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-members-toggle i {\n font-size: 0.65rem;\n width: 12px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n.detail-members-count {\n margin-left: auto;\n font-size: 0.7rem;\n font-weight: 400;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-sunken);\n padding: 1px 7px;\n border-radius: 8px;\n}\n\n.detail-members-list {\n flex: 1;\n overflow-y: auto;\n padding: 0 8px 8px;\n}\n\n.detail-member-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.12s ease;\n}\n\n.detail-member-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-member-row.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.detail-member-row .member-icon {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.detail-member-row.active .member-icon {\n color: var(--mj-brand-primary);\n}\n\n.member-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Footer */\n.detail-footer {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.detail-open-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.detail-open-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.detail-open-btn i {\n font-size: 0.75rem;\n}\n\n/* 3D orbit hint + reset */\n.threed-hint {\n position: absolute;\n top: 12px;\n right: 12px;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 8px 5px 10px;\n background: color-mix(in srgb, var(--mj-bg-surface) 88%, transparent);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n backdrop-filter: blur(4px);\n}\n.threed-hint > i { color: var(--mj-brand-primary); }\n.threed-reset {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.threed-reset:hover { color: var(--mj-brand-primary); border-color: var(--mj-brand-primary); }\n\n/* 3D cluster labels \u2014 halo for readability over points */\n.cluster-label-3d {\n font-weight: 700;\n font-size: 13px;\n paint-order: stroke;\n stroke: var(--mj-bg-surface);\n stroke-width: 3px;\n stroke-linejoin: round;\n pointer-events: none;\n}\n"], encapsulation: 2 }); }
1582
+ } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJEntityCardComponent, i3.MJEmptyStateComponent, i3.MJAccordionPanelComponent, i3.MJAccordionTitleDirective, i3.MJAccordionBodyDirective], styles: [":host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.scatter-container {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-page);\n}\n\n/* SVG */\n.scatter-svg {\n width: 100%;\n height: 100%;\n cursor: grab;\n user-select: none;\n}\n\n.scatter-svg:active {\n cursor: grabbing;\n}\n\n/* Grid lines */\n.grid-lines line {\n stroke: var(--mj-border-subtle);\n stroke-width: 0.5;\n}\n\n/* Data points */\n.data-point {\n cursor: pointer;\n transition: opacity 0.15s ease, r 0.15s ease;\n}\n\n.data-point:hover {\n filter: brightness(1.2);\n}\n\n/* Selection rings */\n.selection-ring {\n pointer-events: none;\n transition: r 0.15s ease;\n}\n\n/* Disable transitions when AnimateTransitions is false */\n.no-transitions .data-point,\n.no-transitions .selection-ring {\n transition: none;\n}\n\n/* Loading overlay */\n.loading-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-page) 80%, transparent);\n z-index: 5;\n}\n\n.loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.loading-spinner i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n\n/* Empty state \u2014 overlay the full scatter container */\n.empty-state-fill {\n position: absolute;\n inset: 0;\n}\n\n/* Legend */\n.legend {\n position: absolute;\n bottom: 20px;\n left: 20px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(12px);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 14px 18px;\n z-index: 3;\n}\n\n.legend-title {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n margin-bottom: 4px;\n color: var(--mj-text-primary);\n cursor: pointer;\n border-radius: 4px;\n padding: 2px 4px;\n margin-left: -4px;\n margin-right: -4px;\n transition: background 0.15s ease;\n}\n\n.legend-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.legend-label {\n flex: 1;\n}\n\n.legend-edit-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n background: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 0.6rem;\n border-radius: 4px;\n transition: color 0.15s ease;\n}\n\n.legend-item:hover .legend-edit-btn {\n display: inline-flex;\n}\n\n.legend-edit-btn:hover {\n color: var(--mj-brand-primary);\n}\n\n.legend-label-input {\n flex: 1;\n padding: 2px 4px;\n border: 1px solid var(--mj-border-focus);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.75rem;\n outline: none;\n min-width: 60px;\n}\n\n.legend-count {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n margin-left: auto;\n padding-left: 16px;\n}\n\n/* Cluster labels on chart */\n.cluster-label {\n font-size: 14px;\n font-weight: 600;\n opacity: 0.85;\n pointer-events: none;\n text-shadow:\n 0 0 6px var(--mj-bg-page),\n 0 0 12px var(--mj-bg-page),\n 1px 1px 2px var(--mj-bg-page);\n}\n\n/* Tooltip */\n.tooltip {\n position: fixed;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 96%, transparent);\n backdrop-filter: blur(16px);\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n padding: 14px 16px;\n width: 280px;\n box-shadow: 0 12px 40px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n pointer-events: none;\n z-index: 50;\n}\n\n.tt-header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.tt-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.tt-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.tt-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-top: 1px;\n}\n\n.tt-cluster {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.tt-fields {\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.tt-field-row {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-action {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.tt-action i {\n margin-right: 4px;\n}\n\n/* ================================================================\n Detail Panel (right slide-in)\n ================================================================ */\n\n.detail-panel {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 320px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(16px);\n border-left: 1px solid var(--mj-border-default);\n z-index: 10;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-bg-page) 30%, transparent);\n overflow: hidden;\n}\n\n.detail-panel-open {\n transform: translateX(0);\n}\n\n/* Header */\n.detail-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-header-content {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.detail-icon {\n width: 2.25rem;\n height: 2.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.95rem;\n flex-shrink: 0;\n}\n\n.detail-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.detail-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.detail-entity-badge {\n display: inline-block;\n margin-top: 4px;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 0.68rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.detail-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n padding: 4px 6px;\n border-radius: 4px;\n transition: color 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.detail-close:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n/* Cluster badge */\n.detail-cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.detail-cluster-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n/* Metadata fields */\n.detail-fields {\n padding: 4px 16px 8px;\n overflow-y: auto;\n flex-shrink: 0;\n}\n\n.detail-field {\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-field:last-child {\n border-bottom: none;\n}\n\n.detail-field-key {\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n}\n\n.detail-field-value {\n font-size: 0.8rem;\n color: var(--mj-text-primary);\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n/* Cluster Members Section */\n.detail-members-section {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* .detail-members-toggle / .detail-members-count removed: the bespoke\n disclosure toggle + count pill were replaced by <mj-accordion-panel>\n (header/chevron) and the standard .mj-accordion-badge--right. */\n\n.detail-members-list {\n flex: 1;\n overflow-y: auto;\n padding: 0 8px 8px;\n}\n\n.detail-member-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.12s ease;\n}\n\n.detail-member-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-member-row.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.detail-member-row .member-icon {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.detail-member-row.active .member-icon {\n color: var(--mj-brand-primary);\n}\n\n.member-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Footer */\n.detail-footer {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.detail-open-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.detail-open-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.detail-open-btn i {\n font-size: 0.75rem;\n}\n\n/* 3D orbit hint + reset */\n.threed-hint {\n position: absolute;\n top: 12px;\n right: 12px;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 8px 5px 10px;\n background: color-mix(in srgb, var(--mj-bg-surface) 88%, transparent);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n backdrop-filter: blur(4px);\n}\n.threed-hint > i { color: var(--mj-brand-primary); }\n.threed-reset {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.threed-reset:hover { color: var(--mj-brand-primary); border-color: var(--mj-brand-primary); }\n\n/* 3D cluster labels \u2014 halo for readability over points */\n.cluster-label-3d {\n font-weight: 700;\n font-size: 13px;\n paint-order: stroke;\n stroke: var(--mj-bg-surface);\n stroke-width: 3px;\n stroke-linejoin: round;\n pointer-events: none;\n}\n"], encapsulation: 2 }); }
1589
1583
  }
1590
1584
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClusterScatterComponent, [{
1591
1585
  type: Component,
1592
- args: [{ standalone: false, selector: 'mj-cluster-scatter', encapsulation: ViewEncapsulation.None, template: "<div class=\"scatter-container\">\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\">\n <div class=\"loading-spinner\">\n <i class=\"fa-solid fa-circle-nodes fa-spin\"></i>\n <span>Computing clusters...</span>\n </div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && Points.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-chart-scatter\"></i>\n <p>No data to display</p>\n <p class=\"empty-hint\">Configure and run clustering to see results</p>\n </div>\n }\n\n <!-- SVG scatter plot -->\n @if (Points.length > 0) {\n <svg #svgElement\n class=\"scatter-svg\"\n [class.no-transitions]=\"!AnimateTransitions\"\n [attr.viewBox]=\"ViewBoxString\"\n preserveAspectRatio=\"xMidYMid meet\"\n (wheel)=\"OnWheel($event)\"\n (mousedown)=\"OnMouseDown($event)\">\n\n <!-- Gradient definitions for cluster glow -->\n <defs>\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <radialGradient [attr.id]=\"'cluster-glow-' + centroid.ClusterId\"\n cx=\"50%\" cy=\"50%\" r=\"50%\">\n <stop offset=\"0%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0.12\"/>\n <stop offset=\"100%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0\"/>\n </radialGradient>\n }\n </defs>\n\n <!-- Background grid lines -->\n <g class=\"grid-lines\">\n @for (y of HorizontalGridLines; track y) {\n <line [attr.x1]=\"0\" [attr.y1]=\"y\" [attr.x2]=\"1000\" [attr.y2]=\"y\"/>\n }\n @for (x of VerticalGridLines; track x) {\n <line [attr.x1]=\"x\" [attr.y1]=\"0\" [attr.x2]=\"x\" [attr.y2]=\"700\"/>\n }\n </g>\n\n <!-- Cluster glow + labels (2D only; centroids are 2D aesthetics) -->\n @if (!Is3D) {\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <circle [attr.cx]=\"centroid.Cx\"\n [attr.cy]=\"centroid.Cy\"\n [attr.r]=\"centroid.R\"\n [attr.fill]=\"'url(#cluster-glow-' + centroid.ClusterId + ')'\"/>\n }\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n @if (GetClusterLabelById(centroid.ClusterId); as label) {\n <text class=\"cluster-label\"\n [attr.x]=\"centroid.Cx\"\n [attr.y]=\"centroid.Cy - centroid.R - 8\"\n text-anchor=\"middle\"\n [attr.fill]=\"centroid.Color\">{{ label }}</text>\n }\n }\n }\n\n <!-- Data points (RenderPoints is depth-sorted in 3D) -->\n @for (point of RenderPoints; track TrackPointBy($index, point)) {\n <circle class=\"data-point\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"GetPointRadius(point)\"\n [attr.fill]=\"GetPointColor(point)\"\n [attr.opacity]=\"GetPointOpacity(point)\"\n (mouseenter)=\"OnPointMouseEnter(point, $event)\"\n (mouseleave)=\"OnPointMouseLeave()\"\n (click)=\"OnPointClick(point); $event.stopPropagation()\"/>\n }\n\n <!-- 3D cluster labels (at projected centroids; follow rotation) -->\n @if (Is3D && ColorBy !== 'entity') {\n @for (lbl of ProjectedClusterLabels; track lbl.ClusterId) {\n @if (GetClusterLabelById(lbl.ClusterId); as text) {\n <text class=\"cluster-label cluster-label-3d\"\n [attr.x]=\"lbl.X\"\n [attr.y]=\"lbl.Y\"\n text-anchor=\"middle\"\n [attr.fill]=\"lbl.Color\">{{ text }}</text>\n }\n }\n }\n\n <!-- Selection rings for selected points -->\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsSelected(point)) {\n <circle class=\"selection-ring\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 2\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2\"\n opacity=\"0.8\"/>\n }\n }\n\n <!-- Highlight ring for last-clicked point -->\n @if (HighlightedKey) {\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsHighlighted(point)) {\n <circle [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 4\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2.5\"\n opacity=\"0.9\"/>\n }\n }\n }\n </svg>\n }\n\n <!-- 3D orbit hint + reset -->\n @if (Points.length > 0 && Is3D) {\n <div class=\"threed-hint\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>3D \u00B7 drag to rotate</span>\n <button class=\"threed-reset\" (click)=\"ResetRotation()\" title=\"Reset view\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n </div>\n }\n\n <!-- Entity legend (multi-entity color mode) -->\n @if (ShowLegend && ColorBy === 'entity' && EntityLegend.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Entities</div>\n @for (ent of EntityLegend; track ent.Name) {\n <div class=\"legend-item\">\n <div class=\"legend-dot\" [style.background]=\"ent.Color\"></div>\n <span class=\"legend-label\">{{ ent.Name }}</span>\n <span class=\"legend-count\">{{ ent.Count }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Legend overlay (cluster color mode) -->\n @if (ShowLegend && ColorBy !== 'entity' && Clusters.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Clusters</div>\n @for (cluster of Clusters; track TrackClusterBy($index, cluster)) {\n <div class=\"legend-item\" (click)=\"OnClusterLegendClick(cluster)\">\n <div class=\"legend-dot\" [style.background]=\"cluster.Color\"></div>\n @if (EditingClusterId === cluster.Id) {\n <input class=\"legend-label-input\"\n [(ngModel)]=\"EditingLabelDraft\"\n (keydown)=\"OnLabelEditKeydown($event, cluster)\"\n (blur)=\"CommitLabelEdit(cluster)\"\n (click)=\"$event.stopPropagation()\"\n autofocus />\n } @else {\n <span class=\"legend-label\">{{ cluster.Label }}</span>\n <button class=\"legend-edit-btn\"\n title=\"Edit label\"\n (click)=\"StartLabelEdit(cluster, $event)\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n }\n <span class=\"legend-count\">{{ cluster.MemberCount }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Tooltip -->\n @if (ShowTooltip && HoveredPoint) {\n <div class=\"tooltip\"\n [style.left.px]=\"TooltipX\"\n [style.top.px]=\"TooltipY\">\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"HoveredPoint.Metadata\"\n Variant=\"compact\">\n </mj-entity-card>\n <div class=\"tt-cluster\">\n <i class=\"fa-solid fa-circle\" style=\"font-size: 7px; vertical-align: middle\"></i>\n {{ GetClusterLabel(HoveredPoint) }}\n </div>\n <div class=\"tt-action\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Click to view details\n </div>\n </div>\n }\n\n <!-- Detail Panel (slide-in from right) -->\n <div class=\"detail-panel\" [class.detail-panel-open]=\"ShowDetailPanel && SelectedPoint\">\n @if (SelectedPoint) {\n <!-- Panel Header -->\n <div class=\"detail-header\">\n <div class=\"detail-header-content\">\n @if (SelectedPoint.Metadata?.['EntityIcon']) {\n <div class=\"detail-icon\">\n <i [class]=\"SelectedPoint.Metadata['EntityIcon']\"></i>\n </div>\n }\n <div class=\"detail-header-text\">\n <div class=\"detail-name\">{{ SelectedPoint.Label }}</div>\n @if (SelectedPoint.Metadata?.['Entity']) {\n <span class=\"detail-entity-badge\">{{ SelectedPoint.Metadata['Entity'] }}</span>\n }\n </div>\n </div>\n <button class=\"detail-close\" (click)=\"CloseDetailPanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Cluster badge -->\n <div class=\"detail-cluster\">\n <span class=\"detail-cluster-dot\"\n [style.background]=\"GetClusterColor(SelectedPoint.ClusterId)\"></span>\n {{ GetClusterLabel(SelectedPoint) }}\n </div>\n\n <!-- Metadata fields via entity card -->\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"SelectedPoint.Metadata\"\n Variant=\"detail\"\n CssClass=\"detail-entity-card\">\n </mj-entity-card>\n\n <!-- Cluster Members -->\n <div class=\"detail-members-section\">\n <button class=\"detail-members-toggle\" (click)=\"ToggleClusterMembers()\">\n <i class=\"fa-solid\"\n [class.fa-chevron-down]=\"ClusterMembersExpanded\"\n [class.fa-chevron-right]=\"!ClusterMembersExpanded\"></i>\n <span>Cluster Members</span>\n <span class=\"detail-members-count\">{{ ClusterMembers.length }}</span>\n </button>\n\n @if (ClusterMembersExpanded) {\n <div class=\"detail-members-list\">\n @for (member of ClusterMembers; track TrackMemberBy($index, member)) {\n <div class=\"detail-member-row\"\n [class.active]=\"member.VectorKey === SelectedPoint.VectorKey\"\n (click)=\"OnClusterMemberClick(member)\">\n @if (member.Metadata?.['EntityIcon']) {\n <i [class]=\"member.Metadata['EntityIcon']\" class=\"member-icon\"></i>\n }\n <span class=\"member-label\">{{ member.Label }}</span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Open Record button -->\n <div class=\"detail-footer\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecordClick()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Record\n </button>\n </div>\n }\n </div>\n</div>\n", styles: [":host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.scatter-container {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-page);\n}\n\n/* SVG */\n.scatter-svg {\n width: 100%;\n height: 100%;\n cursor: grab;\n user-select: none;\n}\n\n.scatter-svg:active {\n cursor: grabbing;\n}\n\n/* Grid lines */\n.grid-lines line {\n stroke: var(--mj-border-subtle);\n stroke-width: 0.5;\n}\n\n/* Data points */\n.data-point {\n cursor: pointer;\n transition: opacity 0.15s ease, r 0.15s ease;\n}\n\n.data-point:hover {\n filter: brightness(1.2);\n}\n\n/* Selection rings */\n.selection-ring {\n pointer-events: none;\n transition: r 0.15s ease;\n}\n\n/* Disable transitions when AnimateTransitions is false */\n.no-transitions .data-point,\n.no-transitions .selection-ring {\n transition: none;\n}\n\n/* Loading overlay */\n.loading-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-page) 80%, transparent);\n z-index: 5;\n}\n\n.loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.loading-spinner i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.empty-state {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n gap: 8px;\n}\n\n.empty-state i {\n font-size: 3rem;\n margin-bottom: 8px;\n opacity: 0.4;\n}\n\n.empty-state p {\n font-size: 1rem;\n margin: 0;\n}\n\n.empty-hint {\n font-size: 0.8rem !important;\n opacity: 0.7;\n}\n\n/* Legend */\n.legend {\n position: absolute;\n bottom: 20px;\n left: 20px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(12px);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 14px 18px;\n z-index: 3;\n}\n\n.legend-title {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n margin-bottom: 4px;\n color: var(--mj-text-primary);\n cursor: pointer;\n border-radius: 4px;\n padding: 2px 4px;\n margin-left: -4px;\n margin-right: -4px;\n transition: background 0.15s ease;\n}\n\n.legend-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.legend-label {\n flex: 1;\n}\n\n.legend-edit-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n background: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 0.6rem;\n border-radius: 4px;\n transition: color 0.15s ease;\n}\n\n.legend-item:hover .legend-edit-btn {\n display: inline-flex;\n}\n\n.legend-edit-btn:hover {\n color: var(--mj-brand-primary);\n}\n\n.legend-label-input {\n flex: 1;\n padding: 2px 4px;\n border: 1px solid var(--mj-border-focus);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.75rem;\n outline: none;\n min-width: 60px;\n}\n\n.legend-count {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n margin-left: auto;\n padding-left: 16px;\n}\n\n/* Cluster labels on chart */\n.cluster-label {\n font-size: 14px;\n font-weight: 600;\n opacity: 0.85;\n pointer-events: none;\n text-shadow:\n 0 0 6px var(--mj-bg-page),\n 0 0 12px var(--mj-bg-page),\n 1px 1px 2px var(--mj-bg-page);\n}\n\n/* Tooltip */\n.tooltip {\n position: fixed;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 96%, transparent);\n backdrop-filter: blur(16px);\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n padding: 14px 16px;\n width: 280px;\n box-shadow: 0 12px 40px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n pointer-events: none;\n z-index: 50;\n}\n\n.tt-header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.tt-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.tt-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.tt-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-top: 1px;\n}\n\n.tt-cluster {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.tt-fields {\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.tt-field-row {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-action {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.tt-action i {\n margin-right: 4px;\n}\n\n/* ================================================================\n Detail Panel (right slide-in)\n ================================================================ */\n\n.detail-panel {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 320px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(16px);\n border-left: 1px solid var(--mj-border-default);\n z-index: 10;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-bg-page) 30%, transparent);\n overflow: hidden;\n}\n\n.detail-panel-open {\n transform: translateX(0);\n}\n\n/* Header */\n.detail-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-header-content {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.detail-icon {\n width: 2.25rem;\n height: 2.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.95rem;\n flex-shrink: 0;\n}\n\n.detail-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.detail-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.detail-entity-badge {\n display: inline-block;\n margin-top: 4px;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 0.68rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.detail-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n padding: 4px 6px;\n border-radius: 4px;\n transition: color 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.detail-close:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n/* Cluster badge */\n.detail-cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.detail-cluster-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n/* Metadata fields */\n.detail-fields {\n padding: 4px 16px 8px;\n overflow-y: auto;\n flex-shrink: 0;\n}\n\n.detail-field {\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-field:last-child {\n border-bottom: none;\n}\n\n.detail-field-key {\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n}\n\n.detail-field-value {\n font-size: 0.8rem;\n color: var(--mj-text-primary);\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n/* Cluster Members Section */\n.detail-members-section {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.detail-members-toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-align: left;\n transition: background 0.15s ease;\n}\n\n.detail-members-toggle:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-members-toggle i {\n font-size: 0.65rem;\n width: 12px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n.detail-members-count {\n margin-left: auto;\n font-size: 0.7rem;\n font-weight: 400;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-sunken);\n padding: 1px 7px;\n border-radius: 8px;\n}\n\n.detail-members-list {\n flex: 1;\n overflow-y: auto;\n padding: 0 8px 8px;\n}\n\n.detail-member-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.12s ease;\n}\n\n.detail-member-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-member-row.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.detail-member-row .member-icon {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.detail-member-row.active .member-icon {\n color: var(--mj-brand-primary);\n}\n\n.member-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Footer */\n.detail-footer {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.detail-open-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.detail-open-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.detail-open-btn i {\n font-size: 0.75rem;\n}\n\n/* 3D orbit hint + reset */\n.threed-hint {\n position: absolute;\n top: 12px;\n right: 12px;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 8px 5px 10px;\n background: color-mix(in srgb, var(--mj-bg-surface) 88%, transparent);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n backdrop-filter: blur(4px);\n}\n.threed-hint > i { color: var(--mj-brand-primary); }\n.threed-reset {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.threed-reset:hover { color: var(--mj-brand-primary); border-color: var(--mj-brand-primary); }\n\n/* 3D cluster labels \u2014 halo for readability over points */\n.cluster-label-3d {\n font-weight: 700;\n font-size: 13px;\n paint-order: stroke;\n stroke: var(--mj-bg-surface);\n stroke-width: 3px;\n stroke-linejoin: round;\n pointer-events: none;\n}\n"] }]
1586
+ args: [{ standalone: false, selector: 'mj-cluster-scatter', encapsulation: ViewEncapsulation.None, template: "<div class=\"scatter-container\">\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\">\n <div class=\"loading-spinner\">\n <i class=\"fa-solid fa-circle-nodes fa-spin\"></i>\n <span>Computing clusters...</span>\n </div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && Points.length === 0) {\n <mj-empty-state\n class=\"empty-state-fill\"\n Icon=\"fa-solid fa-chart-simple\"\n Title=\"No data to display\"\n Message=\"Configure and run clustering to see results\" />\n }\n\n <!-- SVG scatter plot -->\n @if (Points.length > 0) {\n <svg #svgElement\n class=\"scatter-svg\"\n [class.no-transitions]=\"!AnimateTransitions\"\n [attr.viewBox]=\"ViewBoxString\"\n preserveAspectRatio=\"xMidYMid meet\"\n (wheel)=\"OnWheel($event)\"\n (mousedown)=\"OnMouseDown($event)\">\n\n <!-- Gradient definitions for cluster glow -->\n <defs>\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <radialGradient [attr.id]=\"'cluster-glow-' + centroid.ClusterId\"\n cx=\"50%\" cy=\"50%\" r=\"50%\">\n <stop offset=\"0%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0.12\"/>\n <stop offset=\"100%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0\"/>\n </radialGradient>\n }\n </defs>\n\n <!-- Background grid lines -->\n <g class=\"grid-lines\">\n @for (y of HorizontalGridLines; track y) {\n <line [attr.x1]=\"0\" [attr.y1]=\"y\" [attr.x2]=\"1000\" [attr.y2]=\"y\"/>\n }\n @for (x of VerticalGridLines; track x) {\n <line [attr.x1]=\"x\" [attr.y1]=\"0\" [attr.x2]=\"x\" [attr.y2]=\"700\"/>\n }\n </g>\n\n <!-- Cluster glow + labels (2D only; centroids are 2D aesthetics) -->\n @if (!Is3D) {\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <circle [attr.cx]=\"centroid.Cx\"\n [attr.cy]=\"centroid.Cy\"\n [attr.r]=\"centroid.R\"\n [attr.fill]=\"'url(#cluster-glow-' + centroid.ClusterId + ')'\"/>\n }\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n @if (GetClusterLabelById(centroid.ClusterId); as label) {\n <text class=\"cluster-label\"\n [attr.x]=\"centroid.Cx\"\n [attr.y]=\"centroid.Cy - centroid.R - 8\"\n text-anchor=\"middle\"\n [attr.fill]=\"centroid.Color\">{{ label }}</text>\n }\n }\n }\n\n <!-- Data points (RenderPoints is depth-sorted in 3D) -->\n @for (point of RenderPoints; track TrackPointBy($index, point)) {\n <circle class=\"data-point\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"GetPointRadius(point)\"\n [attr.fill]=\"GetPointColor(point)\"\n [attr.opacity]=\"GetPointOpacity(point)\"\n (mouseenter)=\"OnPointMouseEnter(point, $event)\"\n (mouseleave)=\"OnPointMouseLeave()\"\n (click)=\"OnPointClick(point); $event.stopPropagation()\"/>\n }\n\n <!-- 3D cluster labels (at projected centroids; follow rotation) -->\n @if (Is3D && ColorBy !== 'entity') {\n @for (lbl of ProjectedClusterLabels; track lbl.ClusterId) {\n @if (GetClusterLabelById(lbl.ClusterId); as text) {\n <text class=\"cluster-label cluster-label-3d\"\n [attr.x]=\"lbl.X\"\n [attr.y]=\"lbl.Y\"\n text-anchor=\"middle\"\n [attr.fill]=\"lbl.Color\">{{ text }}</text>\n }\n }\n }\n\n <!-- Selection rings for selected points -->\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsSelected(point)) {\n <circle class=\"selection-ring\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 2\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2\"\n opacity=\"0.8\"/>\n }\n }\n\n <!-- Highlight ring for last-clicked point -->\n @if (HighlightedKey) {\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsHighlighted(point)) {\n <circle [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 4\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2.5\"\n opacity=\"0.9\"/>\n }\n }\n }\n </svg>\n }\n\n <!-- 3D orbit hint + reset -->\n @if (Points.length > 0 && Is3D) {\n <div class=\"threed-hint\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>3D \u00B7 drag to rotate</span>\n <button class=\"threed-reset\" (click)=\"ResetRotation()\" title=\"Reset view\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n </div>\n }\n\n <!-- Entity legend (multi-entity color mode) -->\n @if (ShowLegend && ColorBy === 'entity' && EntityLegend.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Entities</div>\n @for (ent of EntityLegend; track ent.Name) {\n <div class=\"legend-item\">\n <div class=\"legend-dot\" [style.background]=\"ent.Color\"></div>\n <span class=\"legend-label\">{{ ent.Name }}</span>\n <span class=\"legend-count\">{{ ent.Count }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Legend overlay (cluster color mode) -->\n @if (ShowLegend && ColorBy !== 'entity' && Clusters.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Clusters</div>\n @for (cluster of Clusters; track TrackClusterBy($index, cluster)) {\n <div class=\"legend-item\" (click)=\"OnClusterLegendClick(cluster)\">\n <div class=\"legend-dot\" [style.background]=\"cluster.Color\"></div>\n @if (EditingClusterId === cluster.Id) {\n <input class=\"legend-label-input\"\n [(ngModel)]=\"EditingLabelDraft\"\n (keydown)=\"OnLabelEditKeydown($event, cluster)\"\n (blur)=\"CommitLabelEdit(cluster)\"\n (click)=\"$event.stopPropagation()\"\n autofocus />\n } @else {\n <span class=\"legend-label\">{{ cluster.Label }}</span>\n <button class=\"legend-edit-btn\"\n title=\"Edit label\"\n (click)=\"StartLabelEdit(cluster, $event)\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n }\n <span class=\"legend-count\">{{ cluster.MemberCount }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Tooltip -->\n @if (ShowTooltip && HoveredPoint) {\n <div class=\"tooltip\"\n [style.left.px]=\"TooltipX\"\n [style.top.px]=\"TooltipY\">\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"HoveredPoint.Metadata\"\n Variant=\"compact\">\n </mj-entity-card>\n <div class=\"tt-cluster\">\n <i class=\"fa-solid fa-circle\" style=\"font-size: 7px; vertical-align: middle\"></i>\n {{ GetClusterLabel(HoveredPoint) }}\n </div>\n <div class=\"tt-action\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Click to view details\n </div>\n </div>\n }\n\n <!-- Detail Panel (slide-in from right) -->\n <div class=\"detail-panel\" [class.detail-panel-open]=\"ShowDetailPanel && SelectedPoint\">\n @if (SelectedPoint) {\n <!-- Panel Header -->\n <div class=\"detail-header\">\n <div class=\"detail-header-content\">\n @if (SelectedPoint.Metadata?.['EntityIcon']) {\n <div class=\"detail-icon\">\n <i [class]=\"SelectedPoint.Metadata['EntityIcon']\"></i>\n </div>\n }\n <div class=\"detail-header-text\">\n <div class=\"detail-name\">{{ SelectedPoint.Label }}</div>\n @if (SelectedPoint.Metadata?.['Entity']) {\n <span class=\"detail-entity-badge\">{{ SelectedPoint.Metadata['Entity'] }}</span>\n }\n </div>\n </div>\n <button class=\"detail-close\" (click)=\"CloseDetailPanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Cluster badge -->\n <div class=\"detail-cluster\">\n <span class=\"detail-cluster-dot\"\n [style.background]=\"GetClusterColor(SelectedPoint.ClusterId)\"></span>\n {{ GetClusterLabel(SelectedPoint) }}\n </div>\n\n <!-- Metadata fields via entity card -->\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"SelectedPoint.Metadata\"\n Variant=\"detail\"\n CssClass=\"detail-entity-card\">\n </mj-entity-card>\n\n <!-- Cluster Members -->\n <div class=\"detail-members-section\">\n <mj-accordion-panel\n Size=\"sm\"\n [Fill]=\"true\"\n [FlushBody]=\"true\"\n [(Expanded)]=\"ClusterMembersExpanded\">\n <ng-template mjAccordionTitle>\n Cluster Members\n <span class=\"mj-accordion-badge mj-accordion-badge--right\">{{ ClusterMembers.length }}</span>\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"detail-members-list\">\n @for (member of ClusterMembers; track TrackMemberBy($index, member)) {\n <div class=\"detail-member-row\"\n [class.active]=\"member.VectorKey === SelectedPoint?.VectorKey\"\n (click)=\"OnClusterMemberClick(member)\">\n @if (member.Metadata?.['EntityIcon']) {\n <i [class]=\"member.Metadata['EntityIcon']\" class=\"member-icon\"></i>\n }\n <span class=\"member-label\">{{ member.Label }}</span>\n </div>\n }\n </div>\n </ng-template>\n </mj-accordion-panel>\n </div>\n\n <!-- Open Record button -->\n <div class=\"detail-footer\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecordClick()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Record\n </button>\n </div>\n }\n </div>\n</div>\n", styles: [":host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.scatter-container {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-page);\n}\n\n/* SVG */\n.scatter-svg {\n width: 100%;\n height: 100%;\n cursor: grab;\n user-select: none;\n}\n\n.scatter-svg:active {\n cursor: grabbing;\n}\n\n/* Grid lines */\n.grid-lines line {\n stroke: var(--mj-border-subtle);\n stroke-width: 0.5;\n}\n\n/* Data points */\n.data-point {\n cursor: pointer;\n transition: opacity 0.15s ease, r 0.15s ease;\n}\n\n.data-point:hover {\n filter: brightness(1.2);\n}\n\n/* Selection rings */\n.selection-ring {\n pointer-events: none;\n transition: r 0.15s ease;\n}\n\n/* Disable transitions when AnimateTransitions is false */\n.no-transitions .data-point,\n.no-transitions .selection-ring {\n transition: none;\n}\n\n/* Loading overlay */\n.loading-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-page) 80%, transparent);\n z-index: 5;\n}\n\n.loading-spinner {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n}\n\n.loading-spinner i {\n font-size: 2rem;\n color: var(--mj-brand-primary);\n}\n\n/* Empty state \u2014 overlay the full scatter container */\n.empty-state-fill {\n position: absolute;\n inset: 0;\n}\n\n/* Legend */\n.legend {\n position: absolute;\n bottom: 20px;\n left: 20px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(12px);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 14px 18px;\n z-index: 3;\n}\n\n.legend-title {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n\n.legend-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n margin-bottom: 4px;\n color: var(--mj-text-primary);\n cursor: pointer;\n border-radius: 4px;\n padding: 2px 4px;\n margin-left: -4px;\n margin-right: -4px;\n transition: background 0.15s ease;\n}\n\n.legend-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.legend-label {\n flex: 1;\n}\n\n.legend-edit-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n background: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 0.6rem;\n border-radius: 4px;\n transition: color 0.15s ease;\n}\n\n.legend-item:hover .legend-edit-btn {\n display: inline-flex;\n}\n\n.legend-edit-btn:hover {\n color: var(--mj-brand-primary);\n}\n\n.legend-label-input {\n flex: 1;\n padding: 2px 4px;\n border: 1px solid var(--mj-border-focus);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.75rem;\n outline: none;\n min-width: 60px;\n}\n\n.legend-count {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n margin-left: auto;\n padding-left: 16px;\n}\n\n/* Cluster labels on chart */\n.cluster-label {\n font-size: 14px;\n font-weight: 600;\n opacity: 0.85;\n pointer-events: none;\n text-shadow:\n 0 0 6px var(--mj-bg-page),\n 0 0 12px var(--mj-bg-page),\n 1px 1px 2px var(--mj-bg-page);\n}\n\n/* Tooltip */\n.tooltip {\n position: fixed;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 96%, transparent);\n backdrop-filter: blur(16px);\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n padding: 14px 16px;\n width: 280px;\n box-shadow: 0 12px 40px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n pointer-events: none;\n z-index: 50;\n}\n\n.tt-header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.tt-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.tt-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.tt-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-top: 1px;\n}\n\n.tt-cluster {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.tt-fields {\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.tt-field-row {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tt-action {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.tt-action i {\n margin-right: 4px;\n}\n\n/* ================================================================\n Detail Panel (right slide-in)\n ================================================================ */\n\n.detail-panel {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 320px;\n background: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(16px);\n border-left: 1px solid var(--mj-border-default);\n z-index: 10;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-bg-page) 30%, transparent);\n overflow: hidden;\n}\n\n.detail-panel-open {\n transform: translateX(0);\n}\n\n/* Header */\n.detail-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-header-content {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.detail-icon {\n width: 2.25rem;\n height: 2.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.95rem;\n flex-shrink: 0;\n}\n\n.detail-header-text {\n min-width: 0;\n flex: 1;\n}\n\n.detail-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.detail-entity-badge {\n display: inline-block;\n margin-top: 4px;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 0.68rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.detail-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n padding: 4px 6px;\n border-radius: 4px;\n transition: color 0.15s ease, background 0.15s ease;\n flex-shrink: 0;\n}\n\n.detail-close:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n/* Cluster badge */\n.detail-cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.detail-cluster-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n/* Metadata fields */\n.detail-fields {\n padding: 4px 16px 8px;\n overflow-y: auto;\n flex-shrink: 0;\n}\n\n.detail-field {\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.detail-field:last-child {\n border-bottom: none;\n}\n\n.detail-field-key {\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n}\n\n.detail-field-value {\n font-size: 0.8rem;\n color: var(--mj-text-primary);\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n/* Cluster Members Section */\n.detail-members-section {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* .detail-members-toggle / .detail-members-count removed: the bespoke\n disclosure toggle + count pill were replaced by <mj-accordion-panel>\n (header/chevron) and the standard .mj-accordion-badge--right. */\n\n.detail-members-list {\n flex: 1;\n overflow-y: auto;\n padding: 0 8px 8px;\n}\n\n.detail-member-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.12s ease;\n}\n\n.detail-member-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.detail-member-row.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n.detail-member-row .member-icon {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.detail-member-row.active .member-icon {\n color: var(--mj-brand-primary);\n}\n\n.member-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Footer */\n.detail-footer {\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.detail-open-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.detail-open-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.detail-open-btn i {\n font-size: 0.75rem;\n}\n\n/* 3D orbit hint + reset */\n.threed-hint {\n position: absolute;\n top: 12px;\n right: 12px;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 8px 5px 10px;\n background: color-mix(in srgb, var(--mj-bg-surface) 88%, transparent);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n backdrop-filter: blur(4px);\n}\n.threed-hint > i { color: var(--mj-brand-primary); }\n.threed-reset {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.threed-reset:hover { color: var(--mj-brand-primary); border-color: var(--mj-brand-primary); }\n\n/* 3D cluster labels \u2014 halo for readability over points */\n.cluster-label-3d {\n font-weight: 700;\n font-size: 13px;\n paint-order: stroke;\n stroke: var(--mj-bg-surface);\n stroke-width: 3px;\n stroke-linejoin: round;\n pointer-events: none;\n}\n"] }]
1593
1587
  }], null, { Points: [{
1594
1588
  type: Input
1595
1589
  }], Clusters: [{