@inflexa-ai/harness 0.19.1 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/agents/report-session-agent.d.ts +7 -0
  2. package/dist/agents/report-session-agent.js +2 -1
  3. package/dist/auth/local-run-authorizer.d.ts +3 -2
  4. package/dist/auth/local-run-authorizer.js +4 -2
  5. package/dist/execution/run-authorizer.d.ts +18 -0
  6. package/dist/execution/run-canceler.d.ts +67 -0
  7. package/dist/execution/run-canceler.js +130 -0
  8. package/dist/index.d.ts +5 -2
  9. package/dist/index.js +6 -1
  10. package/dist/report-render/assets.d.ts +42 -0
  11. package/dist/report-render/assets.js +61 -0
  12. package/dist/report-render/chart.js +31 -10
  13. package/dist/report-render/design.d.ts +120 -0
  14. package/dist/report-render/design.js +808 -0
  15. package/dist/report-render/page.d.ts +32 -115
  16. package/dist/report-render/page.js +130 -273
  17. package/dist/report-render/render.js +34 -11
  18. package/dist/report-render/views/chart-view.d.ts +6 -3
  19. package/dist/report-render/views/chart-view.js +10 -5
  20. package/dist/report-render/views/page-view.d.ts +19 -8
  21. package/dist/report-render/views/page-view.js +31 -11
  22. package/dist/report-render/views/prose.d.ts +1 -0
  23. package/dist/report-render/views/prose.js +7 -13
  24. package/dist/report-render/views/references-view.js +5 -5
  25. package/dist/report-render/views/values.d.ts +15 -6
  26. package/dist/report-render/views/values.js +29 -10
  27. package/dist/runtime/assemble.d.ts +10 -0
  28. package/dist/runtime/assemble.js +1 -0
  29. package/dist/sandbox/k8s-client.js +18 -0
  30. package/dist/sandbox/types.d.ts +6 -0
  31. package/dist/state/analyses.d.ts +3 -26
  32. package/dist/state/analyses.js +6 -64
  33. package/dist/state/index.d.ts +2 -2
  34. package/dist/state/index.js +2 -2
  35. package/dist/state/init.js +3 -3
  36. package/dist/state/runs.d.ts +10 -0
  37. package/dist/state/runs.js +21 -0
  38. package/dist/tasks/data-profile.d.ts +4 -0
  39. package/dist/tasks/data-profile.js +14 -0
  40. package/dist/tools/approval/gateway.d.ts +8 -1
  41. package/dist/tools/approval/gateway.js +9 -2
  42. package/dist/tools/approval/queries.d.ts +9 -2
  43. package/dist/tools/approval/queries.js +12 -4
  44. package/dist/tools/report-session/index.d.ts +1 -1
  45. package/dist/tools/report-session/index.js +1 -1
  46. package/dist/tools/report-session/preview-report.d.ts +11 -1
  47. package/dist/tools/report-session/preview-report.js +35 -12
  48. package/dist/workflows/sandbox-step.d.ts +4 -0
  49. package/dist/workflows/sandbox-step.js +21 -2
  50. package/package.json +5 -1
@@ -0,0 +1,808 @@
1
+ /**
2
+ * The design sheet of the report page, and the ECharts theme.
3
+ *
4
+ * The page carries no external stylesheet and no CSS framework. Thus this module holds the whole identity:
5
+ * the tokens, the font faces, the typography, the components, the textures, the motion rules, and the print
6
+ * rules. The identity source is the Inflexa design system.
7
+ *
8
+ * Two invariants bind an edit of the sheet:
9
+ *
10
+ * - Each class rule matches a class that one view emits. A rule with no emitter is dead, and a dead rule is
11
+ * a cost with no visible effect.
12
+ * - Each font URL comes from the asset manifest. Thus the sheet and the stage step of the caller cannot
13
+ * disagree over a file name.
14
+ *
15
+ * The token block is the exception to the first invariant. The token set is the contract of the identity,
16
+ * thus it stays complete even where no current rule reads a token.
17
+ */
18
+ import { assetSource, MONO_FONT_400_ASSET, MONO_FONT_500_ASSET, MONO_FONT_600_ASSET, MONO_FONT_700_ASSET, SANS_FONT_ASSET } from "./assets.js";
19
+ /**
20
+ * The `@font-face` rules of the staged fonts.
21
+ *
22
+ * Space Grotesk ships as one variable file, thus one face declares the whole weight axis. IBM Plex Mono has
23
+ * no variable release, thus each weight that the identity uses is a separate face and a separate staged file.
24
+ */
25
+ const FONT_FACES = `@font-face {
26
+ font-family: "Space Grotesk Variable";
27
+ src: url("${assetSource(SANS_FONT_ASSET)}") format("woff2-variations");
28
+ font-weight: 300 700;
29
+ font-style: normal;
30
+ font-display: swap;
31
+ }
32
+ @font-face {
33
+ font-family: "IBM Plex Mono";
34
+ src: url("${assetSource(MONO_FONT_400_ASSET)}") format("woff2");
35
+ font-weight: 400;
36
+ font-style: normal;
37
+ font-display: swap;
38
+ }
39
+ @font-face {
40
+ font-family: "IBM Plex Mono";
41
+ src: url("${assetSource(MONO_FONT_500_ASSET)}") format("woff2");
42
+ font-weight: 500;
43
+ font-style: normal;
44
+ font-display: swap;
45
+ }
46
+ @font-face {
47
+ font-family: "IBM Plex Mono";
48
+ src: url("${assetSource(MONO_FONT_600_ASSET)}") format("woff2");
49
+ font-weight: 600;
50
+ font-style: normal;
51
+ font-display: swap;
52
+ }
53
+ @font-face {
54
+ font-family: "IBM Plex Mono";
55
+ src: url("${assetSource(MONO_FONT_700_ASSET)}") format("woff2");
56
+ font-weight: 700;
57
+ font-style: normal;
58
+ font-display: swap;
59
+ }`;
60
+ /** The style rules of the page. The renderer inlines them in one `<style>` block. */
61
+ export const DESIGN_CSS = `${FONT_FACES}
62
+
63
+ /* ── Design tokens ────────────────────────────────────── */
64
+ :root {
65
+ /* Primary scale */
66
+ --color-primary-50: #f0f1fe;
67
+ --color-primary-100: #dde0fc;
68
+ --color-primary-200: #bcc2f9;
69
+ --color-primary-300: #9ba5f5;
70
+ --color-primary-400: #7987f0;
71
+ --color-primary-500: #576dea;
72
+ --color-primary-600: #4458d4;
73
+ --color-primary-700: #3545b0;
74
+
75
+ /* Text */
76
+ --color-heading: #0f172a;
77
+ --color-text-strong: #334155;
78
+ --color-text: #475569;
79
+ --color-text-secondary: #64748b;
80
+ --color-text-muted: #94a3b8;
81
+
82
+ /* Surface */
83
+ --color-bg: #ffffff;
84
+ --color-bg-alt: #f8fafc;
85
+ --color-card: #ffffff;
86
+ --color-border-subtle: #f1f5f9;
87
+ --color-border: #e2e8f0;
88
+ --color-border-hover: #cbd5e1;
89
+ --color-surface-dark: #0f172a;
90
+
91
+ /* Data visualization */
92
+ --color-up: #ef4444;
93
+ --color-down: #576dea;
94
+ --color-ns: #94a3b8;
95
+
96
+ /* Semantic tags */
97
+ --color-high-bg: #f0fdf4;
98
+ --color-high-text: #15803d;
99
+ --color-medium-bg: #fffbeb;
100
+ --color-medium-text: #b45309;
101
+ --color-low-bg: #fef2f2;
102
+ --color-low-text: #b91c1c;
103
+
104
+ /* Stat accents */
105
+ --color-stat-primary: #576dea;
106
+ --color-stat-green: #22c55e;
107
+ --color-stat-red: #ef4444;
108
+ --color-stat-purple: #a78bfa;
109
+ --color-stat-amber: #f59e0b;
110
+
111
+ /* Typography and layout */
112
+ --font-sans: "Space Grotesk Variable", system-ui, sans-serif;
113
+ --font-mono: "IBM Plex Mono", ui-monospace, monospace;
114
+ --layout-max: 1600px;
115
+ --nav-width: 15rem;
116
+ }
117
+
118
+ /* ── Base ─────────────────────────────────────────────── */
119
+ *,
120
+ *::before,
121
+ *::after {
122
+ box-sizing: border-box;
123
+ }
124
+
125
+ body {
126
+ margin: 0;
127
+ font-family: var(--font-sans);
128
+ font-size: 16px;
129
+ line-height: 1.6;
130
+ color: var(--color-text);
131
+ background: var(--color-bg);
132
+ }
133
+
134
+ h1,
135
+ h2,
136
+ h3,
137
+ h4,
138
+ p,
139
+ figure,
140
+ ol {
141
+ margin: 0;
142
+ }
143
+
144
+ img {
145
+ display: block;
146
+ max-width: 100%;
147
+ height: auto;
148
+ }
149
+
150
+ /* A heading uses the sans family. A label, a tag, a table header, and a data value use the mono family. */
151
+ .report-heading {
152
+ color: var(--color-heading);
153
+ font-weight: 600;
154
+ line-height: 1.2;
155
+ letter-spacing: -0.02em;
156
+ }
157
+ .report-heading-2 {
158
+ font-size: 30px;
159
+ margin-bottom: 16px;
160
+ }
161
+ .report-heading-3 {
162
+ font-size: 24px;
163
+ margin-bottom: 12px;
164
+ }
165
+ .report-heading-4 {
166
+ font-size: 20px;
167
+ margin-bottom: 8px;
168
+ }
169
+
170
+ /* ── Layout ───────────────────────────────────────────── */
171
+ .report-container {
172
+ position: relative;
173
+ margin: 0 auto;
174
+ max-width: var(--layout-max);
175
+ padding-left: 24px;
176
+ padding-right: 24px;
177
+ }
178
+
179
+ .report-hero {
180
+ position: relative;
181
+ background: var(--color-bg);
182
+ padding-top: 48px;
183
+ padding-bottom: 40px;
184
+ }
185
+ .report-eyebrow {
186
+ font-family: var(--font-mono);
187
+ font-size: 12px;
188
+ font-weight: 600;
189
+ letter-spacing: 0.18em;
190
+ text-transform: uppercase;
191
+ color: var(--color-primary-500);
192
+ }
193
+ .report-display {
194
+ margin-top: 12px;
195
+ max-width: 56rem;
196
+ font-size: 40px;
197
+ font-weight: 700;
198
+ line-height: 1.05;
199
+ letter-spacing: -0.03em;
200
+ color: var(--color-heading);
201
+ }
202
+
203
+ .report-band {
204
+ position: relative;
205
+ padding-top: 48px;
206
+ padding-bottom: 48px;
207
+ }
208
+ .report-band-white {
209
+ background: var(--color-bg);
210
+ }
211
+ .report-band-slate {
212
+ background: var(--color-bg-alt);
213
+ }
214
+
215
+ .report-section {
216
+ margin-bottom: 40px;
217
+ }
218
+ .report-section:last-child {
219
+ margin-bottom: 0;
220
+ }
221
+
222
+ .report-footer {
223
+ background: var(--color-surface-dark);
224
+ padding-top: 32px;
225
+ padding-bottom: 32px;
226
+ }
227
+ .report-footer-row {
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: space-between;
231
+ gap: 16px;
232
+ }
233
+ .report-footer-title {
234
+ font-size: 14px;
235
+ color: var(--color-text-muted);
236
+ }
237
+ .report-footer-note {
238
+ font-family: var(--font-mono);
239
+ font-size: 11px;
240
+ letter-spacing: 0.08em;
241
+ color: var(--color-text-secondary);
242
+ }
243
+
244
+ /* ── Navigation ───────────────────────────────────────── */
245
+ .report-nav {
246
+ position: fixed;
247
+ top: 0;
248
+ left: 0;
249
+ z-index: 40;
250
+ display: none;
251
+ width: var(--nav-width);
252
+ height: 100vh;
253
+ flex-direction: column;
254
+ border-right: 1px solid var(--color-border);
255
+ background: var(--color-card);
256
+ }
257
+ .report-nav-brand {
258
+ display: flex;
259
+ align-items: center;
260
+ gap: 8px;
261
+ padding: 20px;
262
+ border-bottom: 1px solid var(--color-border);
263
+ }
264
+ .report-nav-brand-name {
265
+ font-family: var(--font-mono);
266
+ font-size: 14px;
267
+ font-weight: 600;
268
+ letter-spacing: 0.08em;
269
+ color: var(--color-primary-500);
270
+ }
271
+ .report-nav-list {
272
+ flex: 1 1 auto;
273
+ overflow-y: auto;
274
+ padding-top: 12px;
275
+ padding-bottom: 12px;
276
+ }
277
+ .report-nav-link {
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 12px;
281
+ padding: 8px 20px;
282
+ font-size: 14px;
283
+ text-decoration: none;
284
+ color: var(--color-text-secondary);
285
+ border-left: 2px solid transparent;
286
+ transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
287
+ }
288
+ .report-nav-link:hover {
289
+ color: var(--color-heading);
290
+ background-color: var(--color-bg-alt);
291
+ border-left-color: var(--color-primary-500);
292
+ }
293
+ .report-nav-index {
294
+ flex-shrink: 0;
295
+ width: 16px;
296
+ text-align: right;
297
+ font-family: var(--font-mono);
298
+ font-size: 10px;
299
+ font-weight: 600;
300
+ letter-spacing: 0.08em;
301
+ color: var(--color-border-hover);
302
+ }
303
+ .report-nav-label {
304
+ overflow: hidden;
305
+ white-space: nowrap;
306
+ text-overflow: ellipsis;
307
+ }
308
+
309
+ /* ── Prose and evidence markers ───────────────────────── */
310
+ .report-prose {
311
+ max-width: 72ch;
312
+ margin-bottom: 16px;
313
+ line-height: 1.7;
314
+ color: var(--color-text);
315
+ }
316
+ .report-marker {
317
+ font-family: var(--font-mono);
318
+ font-size: 10px;
319
+ }
320
+ .report-marker a {
321
+ color: var(--color-primary-500);
322
+ text-decoration: none;
323
+ padding-left: 2px;
324
+ }
325
+ .report-marker a:hover {
326
+ text-decoration: underline;
327
+ }
328
+
329
+ /* ── Corner-accent card ───────────────────────────────── */
330
+ /* The data cards keep square corners. The L-shaped accents grow on hover. */
331
+ .corner-accents {
332
+ position: relative;
333
+ border: 1px solid var(--color-border);
334
+ background: var(--color-card);
335
+ transition: box-shadow 0.2s ease;
336
+ }
337
+ .corner-accents::before,
338
+ .corner-accents::after {
339
+ content: "";
340
+ position: absolute;
341
+ width: 20px;
342
+ height: 20px;
343
+ z-index: 1;
344
+ pointer-events: none;
345
+ transition: width 0.3s ease, height 0.3s ease, border-color 0.3s ease;
346
+ }
347
+ .corner-accents::before {
348
+ top: -1px;
349
+ left: -1px;
350
+ border-top: 2px solid var(--color-primary-200);
351
+ border-left: 2px solid var(--color-primary-200);
352
+ }
353
+ .corner-accents::after {
354
+ bottom: -1px;
355
+ right: -1px;
356
+ border-bottom: 2px solid var(--color-primary-200);
357
+ border-right: 2px solid var(--color-primary-200);
358
+ }
359
+ .corner-accents:hover::before,
360
+ .corner-accents:hover::after {
361
+ width: 28px;
362
+ height: 28px;
363
+ border-color: var(--color-primary-500);
364
+ }
365
+ .corner-accents:hover {
366
+ box-shadow: 0 4px 12px -4px rgba(15, 23, 42, 0.08);
367
+ }
368
+
369
+ /* ── Stat card ────────────────────────────────────────── */
370
+ .report-metric-grid {
371
+ display: grid;
372
+ grid-template-columns: minmax(0, 1fr);
373
+ gap: 24px;
374
+ margin-bottom: 32px;
375
+ }
376
+ /* A lone card keeps a card-sized measure. A card inside the grid fills its cell. */
377
+ .stat-card {
378
+ padding: 24px;
379
+ margin-bottom: 24px;
380
+ max-width: 360px;
381
+ }
382
+ .report-metric-grid > .stat-card {
383
+ margin-bottom: 0;
384
+ max-width: none;
385
+ }
386
+ .stat-card-value {
387
+ font-family: var(--font-mono);
388
+ font-size: 30px;
389
+ font-weight: 700;
390
+ line-height: 1.2;
391
+ color: var(--color-stat-primary);
392
+ }
393
+ .stat-card-label {
394
+ margin-top: 8px;
395
+ font-family: var(--font-mono);
396
+ font-size: 12px;
397
+ font-weight: 500;
398
+ letter-spacing: 0.1em;
399
+ text-transform: uppercase;
400
+ color: var(--color-text-secondary);
401
+ }
402
+
403
+ /* ── Data table ───────────────────────────────────────── */
404
+ .report-table {
405
+ margin-bottom: 32px;
406
+ }
407
+ .report-table-title {
408
+ margin-bottom: 12px;
409
+ font-family: var(--font-mono);
410
+ font-size: 12px;
411
+ font-weight: 600;
412
+ letter-spacing: 0.1em;
413
+ text-transform: uppercase;
414
+ color: var(--color-primary-500);
415
+ }
416
+ .data-table-scroll {
417
+ overflow-x: auto;
418
+ }
419
+ .data-table {
420
+ width: 100%;
421
+ border-collapse: collapse;
422
+ font-size: 14px;
423
+ }
424
+ .data-table th {
425
+ padding: 12px 16px;
426
+ text-align: left;
427
+ font-family: var(--font-mono);
428
+ font-size: 11px;
429
+ font-weight: 600;
430
+ letter-spacing: 0.06em;
431
+ text-transform: uppercase;
432
+ color: var(--color-text-secondary);
433
+ background-color: var(--color-bg-alt);
434
+ border-bottom: 1px solid var(--color-border);
435
+ }
436
+ .data-table td {
437
+ padding: 10px 16px;
438
+ color: var(--color-text-strong);
439
+ border-bottom: 1px solid var(--color-border-subtle);
440
+ }
441
+ .report-row {
442
+ transition: background-color 0.15s ease;
443
+ }
444
+ .report-row:hover {
445
+ background-color: var(--color-bg-alt);
446
+ }
447
+ .report-row:last-child td {
448
+ border-bottom: 0;
449
+ }
450
+
451
+ /* ── Figure and citation cards ────────────────────────── */
452
+ .report-figure {
453
+ padding: 16px;
454
+ margin-bottom: 32px;
455
+ }
456
+ /* The image keeps its own size up to the card width. Thus a small figure does not stretch. */
457
+ .report-figure-image {
458
+ border: 1px solid var(--color-border);
459
+ }
460
+ .report-citation {
461
+ padding: 16px 20px;
462
+ margin-bottom: 24px;
463
+ font-size: 14px;
464
+ color: var(--color-text-secondary);
465
+ }
466
+ .report-citation-note {
467
+ color: var(--color-text);
468
+ }
469
+ .report-caption {
470
+ margin-top: 8px;
471
+ font-size: 13px;
472
+ color: var(--color-text-muted);
473
+ }
474
+
475
+ /* ── Window-chrome chart panel ────────────────────────── */
476
+ /* The chart panel is the one component with rounded corners. */
477
+ .report-chart {
478
+ margin-bottom: 32px;
479
+ }
480
+ .window-chrome {
481
+ overflow: hidden;
482
+ border: 1px solid var(--color-border);
483
+ border-radius: 12px;
484
+ background: var(--color-card);
485
+ box-shadow: 0 8px 30px -8px rgba(15, 23, 42, 0.08), 0 4px 12px -4px rgba(15, 23, 42, 0.04);
486
+ transition: transform 0.5s ease, box-shadow 0.5s ease, border-color 0.5s ease;
487
+ }
488
+ .window-chrome:hover {
489
+ transform: translateY(-4px);
490
+ border-color: var(--color-border-hover);
491
+ box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.18), 0 12px 24px -8px rgba(15, 23, 42, 0.1);
492
+ }
493
+ .window-chrome-bar {
494
+ display: flex;
495
+ align-items: center;
496
+ justify-content: space-between;
497
+ gap: 16px;
498
+ padding: 12px 16px;
499
+ background-color: var(--color-bg-alt);
500
+ border-bottom: 1px solid var(--color-border-subtle);
501
+ }
502
+ .chrome-dots {
503
+ display: flex;
504
+ align-items: center;
505
+ gap: 6px;
506
+ }
507
+ .chrome-dot {
508
+ display: block;
509
+ width: 10px;
510
+ height: 10px;
511
+ border-radius: 50%;
512
+ background-color: var(--color-border-hover);
513
+ transition: background-color 0.3s ease;
514
+ }
515
+ .window-chrome:hover .dot-1 {
516
+ background-color: #fb7185;
517
+ }
518
+ .window-chrome:hover .dot-2 {
519
+ background-color: #fbbf24;
520
+ }
521
+ .window-chrome:hover .dot-3 {
522
+ background-color: #4ade80;
523
+ }
524
+ .window-chrome-title {
525
+ flex: 1 1 auto;
526
+ text-align: center;
527
+ font-size: 14px;
528
+ font-weight: 600;
529
+ color: var(--color-heading);
530
+ }
531
+ .window-chrome-badge {
532
+ font-family: var(--font-mono);
533
+ font-size: 11px;
534
+ font-weight: 600;
535
+ letter-spacing: 0.1em;
536
+ color: var(--color-primary-500);
537
+ }
538
+ .window-chrome-body {
539
+ padding: 16px;
540
+ }
541
+ /* The chart runtime measures the container. A container with no height shows no chart. */
542
+ .chart-container {
543
+ width: 100%;
544
+ height: 400px;
545
+ }
546
+
547
+ /* ── Reference list ───────────────────────────────────── */
548
+ .report-references {
549
+ padding-left: 24px;
550
+ list-style: decimal;
551
+ }
552
+ .report-ref-item {
553
+ margin-bottom: 8px;
554
+ font-size: 14px;
555
+ color: var(--color-text);
556
+ }
557
+ .report-ref-kind {
558
+ font-family: var(--font-mono);
559
+ font-size: 11px;
560
+ letter-spacing: 0.06em;
561
+ text-transform: uppercase;
562
+ color: var(--color-text-muted);
563
+ }
564
+ .report-ref-path {
565
+ font-family: var(--font-mono);
566
+ font-size: 13px;
567
+ color: var(--color-primary-700);
568
+ }
569
+ .report-ref-detail {
570
+ color: var(--color-text-secondary);
571
+ }
572
+
573
+ /* ── Section textures ─────────────────────────────────── */
574
+ /* A texture is felt, not seen: a low opacity and a faded edge. */
575
+ .texture-dots,
576
+ .texture-grid,
577
+ .texture-noise {
578
+ position: relative;
579
+ }
580
+ .texture-dots > *,
581
+ .texture-grid > *,
582
+ .texture-noise > * {
583
+ position: relative;
584
+ z-index: 1;
585
+ }
586
+ .texture-dots::before {
587
+ content: "";
588
+ position: absolute;
589
+ inset: 0;
590
+ z-index: 0;
591
+ pointer-events: none;
592
+ background: rgba(148, 163, 184, 0.2);
593
+ -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%), radial-gradient(circle 0.75px at center, white 100%, transparent 100%);
594
+ -webkit-mask-size: 100% 100%, 6px 6px;
595
+ -webkit-mask-composite: source-in;
596
+ mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%), radial-gradient(circle 0.75px at center, white 100%, transparent 100%);
597
+ mask-size: 100% 100%, 6px 6px;
598
+ mask-composite: intersect;
599
+ }
600
+ .texture-grid::before {
601
+ content: "";
602
+ position: absolute;
603
+ inset: 0;
604
+ z-index: 0;
605
+ pointer-events: none;
606
+ background-image: linear-gradient(to right, transparent calc(100% - 1px), rgba(148, 163, 184, 0.15) 100%), linear-gradient(to bottom, transparent calc(100% - 1px), rgba(148, 163, 184, 0.15) 100%);
607
+ background-size: 32px 32px;
608
+ -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%);
609
+ mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%);
610
+ }
611
+ .texture-noise::after {
612
+ content: "";
613
+ position: absolute;
614
+ inset: 0;
615
+ z-index: 0;
616
+ pointer-events: none;
617
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
618
+ background-size: 200px 200px;
619
+ }
620
+
621
+ /* ── Fade-in ──────────────────────────────────────────── */
622
+ /* The page script adds the visible class. The \`data-delay\` attribute staggers a group. */
623
+ .fade-in {
624
+ opacity: 0;
625
+ transform: translateY(20px);
626
+ transition: opacity 0.7s ease-out, transform 0.7s ease-out;
627
+ }
628
+ .fade-in-visible {
629
+ opacity: 1;
630
+ transform: translateY(0);
631
+ }
632
+ /* The first pass of the observer reveals what is already in view. That reveal drops the transition, thus
633
+ the page is settled when it signals readiness and a capture shows the final state. */
634
+ .fade-in-instant {
635
+ transition: none;
636
+ }
637
+
638
+ /* ── Scrollbar ────────────────────────────────────────── */
639
+ ::-webkit-scrollbar {
640
+ width: 6px;
641
+ height: 6px;
642
+ }
643
+ ::-webkit-scrollbar-track {
644
+ background: transparent;
645
+ }
646
+ ::-webkit-scrollbar-thumb {
647
+ background: var(--color-border-hover);
648
+ border-radius: 3px;
649
+ }
650
+ ::-webkit-scrollbar-thumb:hover {
651
+ background: var(--color-text-muted);
652
+ }
653
+
654
+ /* ── Responsive ───────────────────────────────────────── */
655
+ @media (min-width: 640px) {
656
+ .report-metric-grid {
657
+ grid-template-columns: repeat(2, minmax(0, 1fr));
658
+ }
659
+ }
660
+ @media (min-width: 768px) {
661
+ .report-container {
662
+ padding-left: 32px;
663
+ padding-right: 32px;
664
+ }
665
+ .report-hero {
666
+ padding-top: 80px;
667
+ padding-bottom: 56px;
668
+ }
669
+ .report-display {
670
+ font-size: 56px;
671
+ }
672
+ .report-band {
673
+ padding-top: 64px;
674
+ padding-bottom: 64px;
675
+ }
676
+ }
677
+ @media (min-width: 1024px) {
678
+ .report-container {
679
+ padding-left: 48px;
680
+ padding-right: 48px;
681
+ }
682
+ .report-metric-grid {
683
+ grid-template-columns: repeat(4, minmax(0, 1fr));
684
+ }
685
+ .report-nav {
686
+ display: flex;
687
+ }
688
+ /* The fixed navigation shifts the page body, thus the two never overlap. */
689
+ body:has(#report-sidebar) {
690
+ padding-left: var(--nav-width);
691
+ }
692
+ }
693
+
694
+ /* ── Reduced motion ───────────────────────────────────── */
695
+ @media (prefers-reduced-motion: reduce) {
696
+ *,
697
+ *::before,
698
+ *::after {
699
+ animation-duration: 0.01ms !important;
700
+ animation-iteration-count: 1 !important;
701
+ transition-duration: 0.01ms !important;
702
+ }
703
+ /* The reveal transition collapses, thus each element starts in its visible state. */
704
+ .fade-in {
705
+ opacity: 1;
706
+ transform: none;
707
+ }
708
+ }
709
+
710
+ /* ── Print ────────────────────────────────────────────── */
711
+ @media print {
712
+ .report-nav {
713
+ display: none;
714
+ }
715
+ /* The printer gives the page margin. Thus the screen padding of the container drops out. */
716
+ body:has(#report-sidebar) {
717
+ padding-left: 0;
718
+ }
719
+ .report-container {
720
+ padding-left: 0;
721
+ padding-right: 0;
722
+ }
723
+ .report-band {
724
+ padding-top: 24px;
725
+ padding-bottom: 24px;
726
+ }
727
+ .fade-in {
728
+ opacity: 1;
729
+ transform: none;
730
+ }
731
+ .texture-dots::before,
732
+ .texture-grid::before,
733
+ .texture-noise::after {
734
+ display: none;
735
+ }
736
+ .report-footer {
737
+ background: var(--color-bg);
738
+ }
739
+ .report-footer-title,
740
+ .report-footer-note {
741
+ color: var(--color-text-strong);
742
+ }
743
+ .window-chrome {
744
+ box-shadow: none;
745
+ }
746
+ }`;
747
+ /**
748
+ * The registered name of the ECharts theme. The registration script writes this name, and the bootstrap
749
+ * reads it. One source prevents a silent mismatch between the two sites.
750
+ */
751
+ export const ECHARTS_THEME_NAME = "inflexa";
752
+ /**
753
+ * The ECharts theme as a plain object. The renderer serializes this object and registers it, thus the theme
754
+ * never rides a JSON file and no run-time read is necessary. The palette and the axis styles mirror the
755
+ * light design tokens above.
756
+ */
757
+ export const ECHARTS_THEME = {
758
+ color: ["#576dea", "#ef4444", "#22c55e", "#a78bfa", "#f59e0b", "#ec4899", "#06b6d4", "#f97316", "#8b5cf6", "#14b8a6"],
759
+ backgroundColor: "transparent",
760
+ textStyle: {
761
+ fontFamily: "'Space Grotesk Variable', system-ui, sans-serif",
762
+ fontSize: 12,
763
+ color: "#64748b",
764
+ },
765
+ title: {
766
+ show: false,
767
+ },
768
+ legend: {
769
+ bottom: 0,
770
+ textStyle: { fontSize: 11, color: "#64748b" },
771
+ itemGap: 16,
772
+ itemWidth: 12,
773
+ itemHeight: 12,
774
+ },
775
+ grid: {
776
+ left: 60,
777
+ right: 24,
778
+ top: 24,
779
+ bottom: 48,
780
+ containLabel: false,
781
+ },
782
+ tooltip: {
783
+ backgroundColor: "#ffffff",
784
+ borderColor: "#e2e8f0",
785
+ textStyle: { color: "#334155", fontSize: 12 },
786
+ extraCssText: "border-radius: 4px; box-shadow: 0 4px 12px rgba(15,23,42,0.1);",
787
+ },
788
+ toolbox: {
789
+ feature: {
790
+ saveAsImage: { title: "Save", pixelRatio: 2 },
791
+ },
792
+ iconStyle: { borderColor: "#94a3b8" },
793
+ right: 16,
794
+ top: 0,
795
+ },
796
+ xAxis: {
797
+ axisLine: { lineStyle: { color: "#e2e8f0" } },
798
+ axisTick: { lineStyle: { color: "#e2e8f0" } },
799
+ axisLabel: { color: "#64748b", fontSize: 11 },
800
+ splitLine: { lineStyle: { color: "#f1f5f9", type: "dashed" } },
801
+ },
802
+ yAxis: {
803
+ axisLine: { show: false },
804
+ axisTick: { show: false },
805
+ axisLabel: { color: "#64748b", fontSize: 11 },
806
+ splitLine: { lineStyle: { color: "#f1f5f9", type: "dashed" } },
807
+ },
808
+ };