jeopi-stats 16.2.13

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 (118) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/README.md +82 -0
  3. package/build.ts +95 -0
  4. package/dist/client/index.css +1 -0
  5. package/dist/client/index.html +24 -0
  6. package/dist/client/index.js +257 -0
  7. package/dist/client/styles.css +1655 -0
  8. package/dist/types/aggregator.d.ts +82 -0
  9. package/dist/types/client/App.d.ts +1 -0
  10. package/dist/types/client/api.d.ts +20 -0
  11. package/dist/types/client/app/AppLayout.d.ts +16 -0
  12. package/dist/types/client/app/NavRail.d.ts +7 -0
  13. package/dist/types/client/app/RangeControl.d.ts +7 -0
  14. package/dist/types/client/app/SyncButton.d.ts +14 -0
  15. package/dist/types/client/app/ThemeToggle.d.ts +1 -0
  16. package/dist/types/client/app/TopBar.d.ts +15 -0
  17. package/dist/types/client/app/routes.d.ts +12 -0
  18. package/dist/types/client/components/AgentTokenShare.d.ts +5 -0
  19. package/dist/types/client/components/chart-shared.d.ts +173 -0
  20. package/dist/types/client/components/models-table-shared.d.ts +175 -0
  21. package/dist/types/client/components/range-meta.d.ts +21 -0
  22. package/dist/types/client/data/charts.d.ts +1 -0
  23. package/dist/types/client/data/formatters.d.ts +8 -0
  24. package/dist/types/client/data/useHashRoute.d.ts +8 -0
  25. package/dist/types/client/data/useResource.d.ts +13 -0
  26. package/dist/types/client/data/view-models.d.ts +59 -0
  27. package/dist/types/client/index.d.ts +2 -0
  28. package/dist/types/client/routes/BehaviorRoute.d.ts +7 -0
  29. package/dist/types/client/routes/CostsRoute.d.ts +7 -0
  30. package/dist/types/client/routes/ErrorsRoute.d.ts +8 -0
  31. package/dist/types/client/routes/GainRoute.d.ts +7 -0
  32. package/dist/types/client/routes/ModelsRoute.d.ts +7 -0
  33. package/dist/types/client/routes/OverviewRoute.d.ts +8 -0
  34. package/dist/types/client/routes/ProjectsRoute.d.ts +7 -0
  35. package/dist/types/client/routes/RequestsRoute.d.ts +8 -0
  36. package/dist/types/client/routes/index.d.ts +8 -0
  37. package/dist/types/client/types.d.ts +63 -0
  38. package/dist/types/client/ui/AsyncBoundary.d.ts +12 -0
  39. package/dist/types/client/ui/DataTable.d.ts +17 -0
  40. package/dist/types/client/ui/EmptyState.d.ts +7 -0
  41. package/dist/types/client/ui/ErrorState.d.ts +6 -0
  42. package/dist/types/client/ui/JsonBlock.d.ts +7 -0
  43. package/dist/types/client/ui/MetricCluster.d.ts +5 -0
  44. package/dist/types/client/ui/Panel.d.ts +7 -0
  45. package/dist/types/client/ui/RequestDrawer.d.ts +5 -0
  46. package/dist/types/client/ui/SegmentedControl.d.ts +12 -0
  47. package/dist/types/client/ui/Skeleton.d.ts +8 -0
  48. package/dist/types/client/ui/StatusPill.d.ts +7 -0
  49. package/dist/types/client/ui/index.d.ts +11 -0
  50. package/dist/types/client/useSystemTheme.d.ts +11 -0
  51. package/dist/types/db.d.ts +114 -0
  52. package/dist/types/embedded-client.d.ts +18 -0
  53. package/dist/types/gain-aggregator.d.ts +26 -0
  54. package/dist/types/index.d.ts +7 -0
  55. package/dist/types/parser.d.ts +51 -0
  56. package/dist/types/server.d.ts +7 -0
  57. package/dist/types/shared-types.d.ts +253 -0
  58. package/dist/types/sync-worker.d.ts +31 -0
  59. package/dist/types/types.d.ts +123 -0
  60. package/dist/types/user-metrics.d.ts +72 -0
  61. package/package.json +95 -0
  62. package/src/aggregator.ts +480 -0
  63. package/src/client/App.tsx +106 -0
  64. package/src/client/api.ts +94 -0
  65. package/src/client/app/AppLayout.tsx +93 -0
  66. package/src/client/app/NavRail.tsx +44 -0
  67. package/src/client/app/RangeControl.tsx +39 -0
  68. package/src/client/app/SyncButton.tsx +75 -0
  69. package/src/client/app/ThemeToggle.tsx +37 -0
  70. package/src/client/app/TopBar.tsx +73 -0
  71. package/src/client/app/routes.ts +63 -0
  72. package/src/client/components/AgentTokenShare.tsx +68 -0
  73. package/src/client/components/chart-shared.tsx +257 -0
  74. package/src/client/components/models-table-shared.tsx +255 -0
  75. package/src/client/components/range-meta.ts +73 -0
  76. package/src/client/css.d.ts +1 -0
  77. package/src/client/data/charts.ts +14 -0
  78. package/src/client/data/formatters.ts +45 -0
  79. package/src/client/data/useHashRoute.ts +86 -0
  80. package/src/client/data/useResource.ts +154 -0
  81. package/src/client/data/view-models.ts +233 -0
  82. package/src/client/index.tsx +10 -0
  83. package/src/client/routes/BehaviorRoute.tsx +623 -0
  84. package/src/client/routes/CostsRoute.tsx +234 -0
  85. package/src/client/routes/ErrorsRoute.tsx +118 -0
  86. package/src/client/routes/GainRoute.tsx +226 -0
  87. package/src/client/routes/ModelsRoute.tsx +430 -0
  88. package/src/client/routes/OverviewRoute.tsx +342 -0
  89. package/src/client/routes/ProjectsRoute.tsx +163 -0
  90. package/src/client/routes/RequestsRoute.tsx +123 -0
  91. package/src/client/routes/index.ts +8 -0
  92. package/src/client/styles.css +1333 -0
  93. package/src/client/types.ts +80 -0
  94. package/src/client/ui/AsyncBoundary.tsx +54 -0
  95. package/src/client/ui/DataTable.tsx +122 -0
  96. package/src/client/ui/EmptyState.tsx +16 -0
  97. package/src/client/ui/ErrorState.tsx +25 -0
  98. package/src/client/ui/JsonBlock.tsx +75 -0
  99. package/src/client/ui/MetricCluster.tsx +67 -0
  100. package/src/client/ui/Panel.tsx +24 -0
  101. package/src/client/ui/RequestDrawer.tsx +208 -0
  102. package/src/client/ui/SegmentedControl.tsx +36 -0
  103. package/src/client/ui/Skeleton.tsx +17 -0
  104. package/src/client/ui/StatusPill.tsx +15 -0
  105. package/src/client/ui/index.ts +11 -0
  106. package/src/client/useSystemTheme.ts +87 -0
  107. package/src/db.ts +1278 -0
  108. package/src/embedded-client.generated.txt +0 -0
  109. package/src/embedded-client.ts +26 -0
  110. package/src/gain-aggregator.ts +281 -0
  111. package/src/index.ts +189 -0
  112. package/src/parser.ts +368 -0
  113. package/src/server.ts +346 -0
  114. package/src/shared-types.ts +271 -0
  115. package/src/sync-worker.ts +40 -0
  116. package/src/types.ts +128 -0
  117. package/src/user-metrics.ts +685 -0
  118. package/tailwind.config.js +40 -0
@@ -0,0 +1,1333 @@
1
+ @import "tailwindcss/index.css";
2
+
3
+ /* ==========================================================================
4
+ OMP Theme Variables & Color Scheme
5
+ ========================================================================== */
6
+ :root {
7
+ color-scheme: dark;
8
+ /* OMP brand palette — packages/collab-web/src/styles/tokens.css:
9
+ deep-purple surfaces (hue 307), pink accent #ed4abf, cyan focus ring #5ad8e6. */
10
+ --page: oklch(0.16 0.02 307);
11
+ --surface: oklch(0.19 0.022 307);
12
+ --surface-2: oklch(0.235 0.026 307);
13
+ --border: oklch(1 0 0 / 9%);
14
+ --border-strong: oklch(1 0 0 / 13%);
15
+ --text: oklch(0.92 0.01 307);
16
+ --muted: oklch(0.71 0.016 307);
17
+ --dim: oklch(0.53 0.018 307);
18
+ --accent: oklch(0.674 0.23 341); /* brand pink #ed4abf */
19
+ --accent-fg: oklch(0.2 0.07 341); /* ink on accent fills */
20
+ --accent-muted: oklch(0.674 0.23 341 / 18%);
21
+ --link: oklch(0.817 0.112 205); /* brand cyan #5ad8e6 */
22
+ --success: oklch(0.76 0.14 150);
23
+ --danger: oklch(0.66 0.19 25);
24
+ --warning: oklch(0.79 0.14 85);
25
+
26
+ /* Bridge the view components' token vocabulary to the OMP base tokens so
27
+ muted/secondary text and semantic accents resolve instead of silently
28
+ falling back to inherited text color. Re-resolve per theme via var(). */
29
+ --text-primary: var(--text);
30
+ --text-secondary: var(--muted);
31
+ --text-muted: var(--dim);
32
+ --border-subtle: var(--border);
33
+ --accent-red: var(--danger);
34
+ --accent-green: var(--success);
35
+ --accent-cyan: var(--link);
36
+ --bg-hover: var(--surface-2);
37
+ --bg-elevated: var(--surface-2);
38
+
39
+ --radius-sm: 3px;
40
+ --radius-md: 6px;
41
+ --radius-lg: 8px;
42
+ }
43
+
44
+ /* System-default fallback before JS sets data-theme; an explicit data-theme
45
+ (set by the toggle / anti-flash script) opts out so the override wins. */
46
+ @media (prefers-color-scheme: light) {
47
+ :root:not([data-theme]) {
48
+ color-scheme: light;
49
+ --page: oklch(0.985 0.004 307);
50
+ --surface: oklch(1 0 0);
51
+ --surface-2: oklch(0.965 0.006 307);
52
+ --border: oklch(0 0 0 / 10%);
53
+ --border-strong: oklch(0 0 0 / 15%);
54
+ --text: oklch(0.26 0.03 307);
55
+ --muted: oklch(0.46 0.03 307);
56
+ --dim: oklch(0.58 0.025 307);
57
+ --accent: oklch(0.62 0.23 341);
58
+ --accent-fg: oklch(0.99 0.01 341);
59
+ --accent-muted: oklch(0.62 0.23 341 / 14%);
60
+ --link: oklch(0.58 0.13 230);
61
+ --success: oklch(0.55 0.13 150);
62
+ --danger: oklch(0.55 0.19 25);
63
+ --warning: oklch(0.6 0.13 85);
64
+ }
65
+ }
66
+
67
+ /* Explicit theme override (toggle or persisted preference). */
68
+ [data-theme="light"] {
69
+ color-scheme: light;
70
+ --page: oklch(0.985 0.004 307);
71
+ --surface: oklch(1 0 0);
72
+ --surface-2: oklch(0.965 0.006 307);
73
+ --border: oklch(0 0 0 / 10%);
74
+ --border-strong: oklch(0 0 0 / 15%);
75
+ --text: oklch(0.26 0.03 307);
76
+ --muted: oklch(0.46 0.03 307);
77
+ --dim: oklch(0.58 0.025 307);
78
+ --accent: oklch(0.62 0.23 341);
79
+ --accent-fg: oklch(0.99 0.01 341);
80
+ --accent-muted: oklch(0.62 0.23 341 / 14%);
81
+ --link: oklch(0.58 0.13 230);
82
+ --success: oklch(0.55 0.13 150);
83
+ --danger: oklch(0.55 0.19 25);
84
+ --warning: oklch(0.6 0.13 85);
85
+ }
86
+
87
+ /* ==========================================================================
88
+ Base Styles
89
+ ========================================================================== */
90
+ @layer base {
91
+ * {
92
+ box-sizing: border-box;
93
+ }
94
+
95
+ html {
96
+ -webkit-font-smoothing: antialiased;
97
+ -moz-osx-font-smoothing: grayscale;
98
+ }
99
+
100
+ body {
101
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
102
+ background: var(--page);
103
+ color: var(--text);
104
+ margin: 0;
105
+ min-height: 100vh;
106
+ }
107
+
108
+ /* Accessible focus ring style */
109
+ button:focus-visible,
110
+ a:focus-visible,
111
+ [tabindex]:focus-visible {
112
+ outline: 2px solid var(--link);
113
+ outline-offset: 2px;
114
+ }
115
+ }
116
+
117
+ /* ==========================================================================
118
+ Layout Components
119
+ ========================================================================== */
120
+ .stats-app-container {
121
+ display: flex;
122
+ min-height: 100vh;
123
+ background: var(--page);
124
+ color: var(--text);
125
+ }
126
+
127
+ /* Navigation Rail (Sidebar) */
128
+ .stats-nav-rail {
129
+ width: 240px;
130
+ background: var(--surface);
131
+ border-right: 1px solid var(--border);
132
+ display: flex;
133
+ flex-direction: column;
134
+ flex-shrink: 0;
135
+ }
136
+
137
+ .stats-desktop-nav {
138
+ display: flex;
139
+ }
140
+
141
+ @media (max-width: 1023px) {
142
+ .stats-desktop-nav {
143
+ display: none;
144
+ }
145
+ }
146
+
147
+ .stats-nav-rail-header {
148
+ padding: 20px 24px;
149
+ border-bottom: 1px solid var(--border);
150
+ height: 56px;
151
+ display: flex;
152
+ align-items: center;
153
+ }
154
+
155
+ .stats-logo-container {
156
+ display: flex;
157
+ flex-direction: column;
158
+ }
159
+
160
+ .stats-logo-text {
161
+ font-size: 14px;
162
+ font-weight: 700;
163
+ letter-spacing: 0.1em;
164
+ /* jeopi synthwave sweep: electric blue → violet → hot pink (brand palette) */
165
+ background: linear-gradient(90deg, #3ca0ff 0%, #c86eff 55%, #ff5cc8 100%);
166
+ -webkit-background-clip: text;
167
+ background-clip: text;
168
+ color: transparent;
169
+ }
170
+
171
+ .stats-logo-subtext {
172
+ font-size: 10px;
173
+ letter-spacing: 0.02em;
174
+ color: var(--muted);
175
+ }
176
+
177
+ .stats-nav-rail-menu {
178
+ flex: 1;
179
+ padding: 16px 12px;
180
+ display: flex;
181
+ flex-direction: column;
182
+ gap: 4px;
183
+ }
184
+
185
+ .stats-nav-rail-item {
186
+ display: flex;
187
+ align-items: center;
188
+ gap: 12px;
189
+ padding: 10px 16px;
190
+ font-size: 13px;
191
+ font-weight: 500;
192
+ color: var(--muted);
193
+ background: transparent;
194
+ border: none;
195
+ border-radius: var(--radius-md);
196
+ cursor: pointer;
197
+ text-align: left;
198
+ transition: all 0.15s ease;
199
+ }
200
+
201
+ .stats-nav-rail-item:hover {
202
+ color: var(--text);
203
+ background: var(--surface-2);
204
+ }
205
+
206
+ .stats-nav-rail-item[data-active="true"] {
207
+ color: var(--accent);
208
+ background: var(--accent-muted);
209
+ position: relative;
210
+ }
211
+
212
+ .stats-nav-rail-item[data-active="true"]::before {
213
+ content: "";
214
+ position: absolute;
215
+ left: 0;
216
+ top: 50%;
217
+ height: 18px;
218
+ width: 3px;
219
+ border-radius: 0 3px 3px 0;
220
+ background: var(--accent);
221
+ transform: translateY(-50%);
222
+ }
223
+
224
+ .stats-nav-rail-item-icon {
225
+ flex-shrink: 0;
226
+ }
227
+
228
+ .stats-nav-rail-footer {
229
+ padding: 16px 24px;
230
+ border-top: 1px solid var(--border);
231
+ }
232
+
233
+ .stats-version-tag {
234
+ font-size: 11px;
235
+ color: var(--dim);
236
+ }
237
+
238
+ /* Main Content Area */
239
+ .stats-main-pane {
240
+ flex: 1;
241
+ display: flex;
242
+ flex-direction: column;
243
+ min-width: 0;
244
+ }
245
+
246
+ .stats-top-bar {
247
+ height: 56px;
248
+ background: var(--surface);
249
+ border-bottom: 1px solid var(--border);
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: space-between;
253
+ padding: 0 24px;
254
+ }
255
+
256
+ @media (max-width: 767px) {
257
+ .stats-top-bar {
258
+ padding: 0 16px;
259
+ flex-wrap: wrap;
260
+ height: auto;
261
+ min-height: 56px;
262
+ padding-top: 8px;
263
+ padding-bottom: 8px;
264
+ gap: 12px;
265
+ }
266
+ }
267
+
268
+ .stats-top-bar-left {
269
+ display: flex;
270
+ align-items: center;
271
+ gap: 12px;
272
+ }
273
+
274
+ .stats-mobile-menu-btn {
275
+ display: none;
276
+ background: transparent;
277
+ border: none;
278
+ color: var(--text);
279
+ padding: 4px;
280
+ cursor: pointer;
281
+ }
282
+
283
+ @media (max-width: 1023px) {
284
+ .stats-mobile-menu-btn {
285
+ display: inline-flex;
286
+ align-items: center;
287
+ justify-content: center;
288
+ }
289
+ }
290
+
291
+ .stats-page-title {
292
+ font-size: 16px;
293
+ font-weight: 600;
294
+ color: var(--text);
295
+ margin: 0;
296
+ }
297
+
298
+ .stats-top-bar-right {
299
+ display: flex;
300
+ align-items: center;
301
+ gap: 16px;
302
+ }
303
+
304
+ @media (max-width: 767px) {
305
+ .stats-top-bar-right {
306
+ width: 100%;
307
+ justify-content: space-between;
308
+ gap: 8px;
309
+ }
310
+ }
311
+
312
+ .stats-top-bar-meta {
313
+ display: flex;
314
+ align-items: center;
315
+ }
316
+
317
+ .stats-last-updated {
318
+ font-size: 11px;
319
+ color: var(--dim);
320
+ font-variant-numeric: tabular-nums;
321
+ }
322
+
323
+ .stats-content-area {
324
+ flex: 1;
325
+ overflow-y: auto;
326
+ }
327
+
328
+ .stats-content-inner {
329
+ max-width: 1560px;
330
+ margin: 0 auto;
331
+ padding: 24px;
332
+ display: flex;
333
+ flex-direction: column;
334
+ gap: 24px;
335
+ }
336
+
337
+ @media (max-width: 767px) {
338
+ .stats-content-inner {
339
+ padding: 16px;
340
+ gap: 16px;
341
+ }
342
+ }
343
+
344
+ /* Mobile Nav Drawer */
345
+ .stats-mobile-drawer-overlay {
346
+ position: fixed;
347
+ inset: 0;
348
+ background: rgba(0, 0, 0, 0.4);
349
+ backdrop-filter: none; /* No decorative blurs */
350
+ z-index: 100;
351
+ }
352
+
353
+ .stats-mobile-drawer {
354
+ position: absolute;
355
+ left: 0;
356
+ top: 0;
357
+ bottom: 0;
358
+ width: 260px;
359
+ background: var(--surface);
360
+ border-right: 1px solid var(--border);
361
+ display: flex;
362
+ flex-direction: column;
363
+ z-index: 101;
364
+ animation: slideIn 0.2s ease-out;
365
+ }
366
+
367
+ @keyframes slideIn {
368
+ from { transform: translateX(-100%); }
369
+ to { transform: translateX(0); }
370
+ }
371
+
372
+ .stats-mobile-drawer-header {
373
+ height: 56px;
374
+ display: flex;
375
+ align-items: center;
376
+ justify-content: space-between;
377
+ padding: 0 16px 0 24px;
378
+ border-bottom: 1px solid var(--border);
379
+ }
380
+
381
+ .stats-mobile-nav {
382
+ display: flex;
383
+ height: calc(100% - 56px);
384
+ }
385
+
386
+ /* ==========================================================================
387
+ UI Primitives & Styles
388
+ ========================================================================== */
389
+
390
+ /* Button */
391
+ .stats-button {
392
+ display: inline-flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ gap: 8px;
396
+ padding: 8px 14px;
397
+ font-size: 13px;
398
+ font-weight: 500;
399
+ border-radius: var(--radius-md);
400
+ border: 1px solid transparent;
401
+ cursor: pointer;
402
+ transition: all 0.15s ease;
403
+ font-family: inherit;
404
+ }
405
+
406
+ .stats-button:active:not(:disabled) {
407
+ transform: translateY(1px);
408
+ }
409
+
410
+ .stats-button-primary {
411
+ background: var(--accent);
412
+ color: var(--accent-fg);
413
+ }
414
+
415
+ .stats-button-primary:hover:not(:disabled) {
416
+ opacity: 0.9;
417
+ }
418
+
419
+ .stats-button-primary:disabled {
420
+ opacity: 0.5;
421
+ cursor: not-allowed;
422
+ }
423
+
424
+ .stats-button-secondary {
425
+ background: var(--surface-2);
426
+ color: var(--text);
427
+ border-color: var(--border);
428
+ }
429
+
430
+ .stats-button-secondary:hover:not(:disabled) {
431
+ background: var(--border);
432
+ }
433
+
434
+ .stats-button-secondary:disabled {
435
+ opacity: 0.5;
436
+ cursor: not-allowed;
437
+ }
438
+
439
+ /* RangeControl */
440
+ .stats-range-control {
441
+ display: flex;
442
+ background: var(--surface-2);
443
+ border: 1px solid var(--border);
444
+ border-radius: var(--radius-md);
445
+ padding: 2px;
446
+ }
447
+
448
+ .stats-range-control-btn {
449
+ padding: 4px 10px;
450
+ font-size: 12px;
451
+ font-weight: 500;
452
+ color: var(--muted);
453
+ background: transparent;
454
+ border: none;
455
+ border-radius: var(--radius-sm);
456
+ cursor: pointer;
457
+ transition: all 0.1s ease;
458
+ }
459
+
460
+ .stats-range-control-btn:hover {
461
+ color: var(--text);
462
+ }
463
+
464
+ .stats-range-control-btn[data-active="true"] {
465
+ color: var(--text);
466
+ background: var(--surface);
467
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
468
+ }
469
+
470
+ /* Sync Button & Container */
471
+ .stats-sync-container {
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 12px;
475
+ }
476
+
477
+ .stats-sync-status-msg {
478
+ font-size: 11px;
479
+ font-variant-numeric: tabular-nums;
480
+ max-width: 200px;
481
+ white-space: nowrap;
482
+ overflow: hidden;
483
+ text-overflow: ellipsis;
484
+ }
485
+
486
+ .stats-sync-status-msg[data-type="success"] {
487
+ color: var(--success);
488
+ }
489
+
490
+ .stats-sync-status-msg[data-type="error"] {
491
+ color: var(--danger);
492
+ }
493
+
494
+ .stats-sync-icon {
495
+ flex-shrink: 0;
496
+ }
497
+
498
+ .stats-spin {
499
+ animation: spin 1s linear infinite;
500
+ }
501
+
502
+ @keyframes spin {
503
+ to { transform: rotate(360deg); }
504
+ }
505
+
506
+ /* Panel */
507
+ .stats-panel {
508
+ background: var(--surface);
509
+ border: 1px solid var(--border);
510
+ border-radius: var(--radius-lg);
511
+ display: flex;
512
+ flex-direction: column;
513
+ }
514
+
515
+ .stats-panel-header {
516
+ padding: 16px 20px;
517
+ border-bottom: 1px solid var(--border);
518
+ display: flex;
519
+ align-items: center;
520
+ justify-content: space-between;
521
+ gap: 16px;
522
+ }
523
+
524
+ @media (max-width: 480px) {
525
+ .stats-panel-header {
526
+ flex-direction: column;
527
+ align-items: flex-start;
528
+ gap: 8px;
529
+ }
530
+ }
531
+
532
+ .stats-panel-header-titles {
533
+ display: flex;
534
+ flex-direction: column;
535
+ }
536
+
537
+ .stats-panel-title {
538
+ font-size: 14px;
539
+ font-weight: 600;
540
+ color: var(--text);
541
+ margin: 0;
542
+ }
543
+
544
+ .stats-panel-subtitle {
545
+ font-size: 12px;
546
+ color: var(--muted);
547
+ margin: 2px 0 0 0;
548
+ }
549
+
550
+ .stats-panel-actions {
551
+ display: flex;
552
+ align-items: center;
553
+ gap: 12px;
554
+ }
555
+
556
+ .stats-panel-body {
557
+ padding: 20px;
558
+ }
559
+
560
+ /* Metric Cluster (Overview Dashboard KPI Grid) */
561
+ .stats-metric-cluster {
562
+ display: flex;
563
+ flex-direction: column;
564
+ gap: 16px;
565
+ }
566
+
567
+ .stats-metric-primary-grid {
568
+ display: grid;
569
+ grid-template-columns: repeat(4, 1fr);
570
+ gap: 16px;
571
+ }
572
+
573
+ @media (max-width: 1023px) {
574
+ .stats-metric-primary-grid {
575
+ grid-template-columns: repeat(2, 1fr);
576
+ }
577
+ }
578
+
579
+ @media (max-width: 480px) {
580
+ .stats-metric-primary-grid {
581
+ grid-template-columns: 1fr;
582
+ }
583
+ }
584
+
585
+ .stats-metric-secondary-grid {
586
+ display: grid;
587
+ grid-template-columns: repeat(6, 1fr);
588
+ gap: 12px;
589
+ }
590
+
591
+ @media (max-width: 1023px) {
592
+ .stats-metric-secondary-grid {
593
+ grid-template-columns: repeat(3, 1fr);
594
+ }
595
+ }
596
+
597
+ @media (max-width: 600px) {
598
+ .stats-metric-secondary-grid {
599
+ grid-template-columns: repeat(2, 1fr);
600
+ }
601
+ }
602
+
603
+ .stats-metric-card {
604
+ background: var(--surface);
605
+ border: 1px solid var(--border);
606
+ border-radius: var(--radius-lg);
607
+ padding: 16px;
608
+ display: flex;
609
+ flex-direction: column;
610
+ justify-content: space-between;
611
+ }
612
+
613
+ .stats-metric-card.primary {
614
+ padding: 20px 18px;
615
+ }
616
+
617
+ .stats-metric-card.primary .stats-metric-label {
618
+ color: var(--accent);
619
+ }
620
+
621
+ .stats-metric-card.secondary {
622
+ padding: 12px 14px;
623
+ }
624
+
625
+ .stats-metric-label {
626
+ font-size: 11px;
627
+ font-weight: 600;
628
+ text-transform: uppercase;
629
+ letter-spacing: 0.05em;
630
+ color: var(--muted);
631
+ margin-bottom: 8px;
632
+ }
633
+
634
+ .stats-metric-value {
635
+ font-size: 20px;
636
+ font-weight: 700;
637
+ color: var(--text);
638
+ font-variant-numeric: tabular-nums;
639
+ }
640
+
641
+ .stats-metric-card.secondary .stats-metric-value {
642
+ font-size: 15px;
643
+ font-weight: 600;
644
+ }
645
+
646
+ /* Data Table Component */
647
+ .stats-table-wrapper {
648
+ width: 100%;
649
+ }
650
+
651
+ .stats-table-container {
652
+ width: 100%;
653
+ overflow-x: auto;
654
+ }
655
+
656
+ .stats-table {
657
+ width: 100%;
658
+ border-collapse: collapse;
659
+ font-size: 13px;
660
+ font-variant-numeric: tabular-nums;
661
+ }
662
+
663
+ .stats-table-th {
664
+ font-size: 11px;
665
+ font-weight: 600;
666
+ text-transform: uppercase;
667
+ letter-spacing: 0.05em;
668
+ color: var(--muted);
669
+ padding: 12px 16px;
670
+ border-bottom: 2px solid var(--border);
671
+ }
672
+
673
+ .stats-table-td {
674
+ padding: 10px 16px;
675
+ border-bottom: 1px solid var(--border);
676
+ color: var(--text);
677
+ }
678
+
679
+ .stats-table-tr {
680
+ background: transparent;
681
+ }
682
+
683
+ .stats-table-tr-clickable {
684
+ cursor: pointer;
685
+ transition: background 0.12s ease, box-shadow 0.12s ease;
686
+ }
687
+
688
+ .stats-table-tr-clickable:hover {
689
+ background: var(--surface-2);
690
+ box-shadow: inset 2px 0 0 var(--accent);
691
+ }
692
+
693
+ .stats-text-left {
694
+ text-align: left;
695
+ }
696
+
697
+ .stats-text-right {
698
+ text-align: right;
699
+ }
700
+
701
+ .stats-table-empty {
702
+ padding: 40px;
703
+ text-align: center;
704
+ color: var(--muted);
705
+ font-size: 13px;
706
+ border: 1px dashed var(--border);
707
+ border-radius: var(--radius-lg);
708
+ }
709
+
710
+ /* Project progress bars */
711
+ .stats-progress-bar-track {
712
+ background: var(--surface-2);
713
+ border-radius: 999px;
714
+ overflow: hidden;
715
+ }
716
+
717
+ .stats-progress-bar-fill {
718
+ height: 100%;
719
+ border-radius: inherit;
720
+ transition: width 0.18s ease;
721
+ }
722
+
723
+ .stats-progress-bar-fill[data-variant="link"] {
724
+ background: var(--link);
725
+ }
726
+
727
+ .stats-progress-bar-fill[data-variant="success"] {
728
+ background: var(--success);
729
+ }
730
+
731
+ .stats-table-mobile-only {
732
+ display: none;
733
+ }
734
+
735
+ /* Match .stats-table-container so mobile-card tables also contain horizontal
736
+ overflow instead of spilling the page body on narrow desktops (768-1023px). */
737
+ .stats-table-desktop-only {
738
+ width: 100%;
739
+ overflow-x: auto;
740
+ }
741
+
742
+ @media (max-width: 767px) {
743
+ .stats-table-desktop-only {
744
+ display: none;
745
+ }
746
+ .stats-table-mobile-only {
747
+ display: block;
748
+ }
749
+ }
750
+
751
+ .stats-table-mobile-list {
752
+ display: flex;
753
+ flex-direction: column;
754
+ gap: 12px;
755
+ }
756
+
757
+ .stats-table-mobile-card-wrapper {
758
+ background: var(--surface);
759
+ border: 1px solid var(--border);
760
+ border-radius: var(--radius-lg);
761
+ padding: 14px;
762
+ }
763
+
764
+ /* Status Pill */
765
+ .stats-status-pill {
766
+ display: inline-flex;
767
+ align-items: center;
768
+ justify-content: center;
769
+ padding: 2px 8px;
770
+ font-size: 11px;
771
+ font-weight: 600;
772
+ border-radius: 100px;
773
+ line-height: 1.2;
774
+ }
775
+
776
+ .stats-status-pill[data-variant="success"] {
777
+ background: color-mix(in srgb, var(--success) 18%, transparent);
778
+ color: var(--success);
779
+ }
780
+
781
+ .stats-status-pill[data-variant="danger"] {
782
+ background: color-mix(in srgb, var(--danger) 18%, transparent);
783
+ color: var(--danger);
784
+ }
785
+
786
+ .stats-status-pill[data-variant="warning"] {
787
+ background: color-mix(in srgb, var(--warning) 18%, transparent);
788
+ color: var(--warning);
789
+ }
790
+
791
+ .stats-status-pill[data-variant="info"] {
792
+ background: color-mix(in srgb, var(--link) 18%, transparent);
793
+ color: var(--link);
794
+ }
795
+
796
+ .stats-status-pill[data-variant="default"] {
797
+ background: var(--surface-2);
798
+ color: var(--muted);
799
+ }
800
+
801
+ /* Segmented Control */
802
+ .stats-segmented-control {
803
+ display: flex;
804
+ background: var(--surface-2);
805
+ border: 1px solid var(--border);
806
+ border-radius: var(--radius-md);
807
+ padding: 2px;
808
+ gap: 2px;
809
+ }
810
+
811
+ .stats-segmented-control-btn {
812
+ padding: 6px 12px;
813
+ font-size: 12px;
814
+ font-weight: 500;
815
+ color: var(--muted);
816
+ background: transparent;
817
+ border: none;
818
+ border-radius: var(--radius-sm);
819
+ cursor: pointer;
820
+ transition: all 0.1s ease;
821
+ white-space: nowrap;
822
+ }
823
+
824
+ .stats-segmented-control-btn:hover {
825
+ color: var(--text);
826
+ }
827
+
828
+ .stats-segmented-control-btn[data-active="true"] {
829
+ color: var(--text);
830
+ background: var(--surface);
831
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
832
+ }
833
+
834
+ /* Drawer Detail Component */
835
+ .stats-drawer-overlay {
836
+ position: fixed;
837
+ inset: 0;
838
+ background: rgba(0, 0, 0, 0.5);
839
+ z-index: 100;
840
+ display: flex;
841
+ justify-content: flex-end;
842
+ }
843
+
844
+ .stats-drawer {
845
+ width: 600px;
846
+ max-width: 100%;
847
+ height: 100%;
848
+ background: var(--surface);
849
+ border-left: 1px solid var(--border);
850
+ display: flex;
851
+ flex-direction: column;
852
+ animation: drawerSlide 0.25s cubic-bezier(0.16, 1, 0.3, 1);
853
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.25);
854
+ }
855
+
856
+ @keyframes drawerSlide {
857
+ from { transform: translateX(100%); }
858
+ to { transform: translateX(0); }
859
+ }
860
+
861
+ .stats-drawer-header {
862
+ height: 56px;
863
+ border-bottom: 1px solid var(--border);
864
+ padding: 0 24px;
865
+ display: flex;
866
+ align-items: center;
867
+ justify-content: space-between;
868
+ background: var(--surface);
869
+ flex-shrink: 0;
870
+ }
871
+
872
+ .stats-drawer-header-left {
873
+ display: flex;
874
+ align-items: baseline;
875
+ gap: 12px;
876
+ }
877
+
878
+ .stats-drawer-title {
879
+ font-size: 15px;
880
+ font-weight: 600;
881
+ color: var(--text);
882
+ margin: 0;
883
+ }
884
+
885
+ .stats-drawer-id {
886
+ font-size: 11px;
887
+ color: var(--muted);
888
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
889
+ }
890
+
891
+ .stats-drawer-close-btn {
892
+ background: transparent;
893
+ border: none;
894
+ color: var(--muted);
895
+ cursor: pointer;
896
+ padding: 4px;
897
+ display: inline-flex;
898
+ align-items: center;
899
+ justify-content: center;
900
+ border-radius: var(--radius-sm);
901
+ transition: all 0.1s ease;
902
+ }
903
+
904
+ .stats-drawer-close-btn:hover {
905
+ color: var(--text);
906
+ background: var(--surface-2);
907
+ }
908
+
909
+ .stats-drawer-body {
910
+ flex: 1;
911
+ overflow-y: auto;
912
+ padding: 24px;
913
+ }
914
+
915
+ .stats-drawer-content {
916
+ display: flex;
917
+ flex-direction: column;
918
+ gap: 24px;
919
+ }
920
+
921
+ .stats-drawer-status-card {
922
+ background: var(--surface-2);
923
+ border: 1px solid var(--border);
924
+ border-radius: var(--radius-lg);
925
+ padding: 16px;
926
+ }
927
+
928
+ .stats-drawer-status-row {
929
+ display: flex;
930
+ justify-content: space-between;
931
+ align-items: flex-start;
932
+ gap: 16px;
933
+ }
934
+
935
+ .stats-drawer-model {
936
+ font-size: 16px;
937
+ font-weight: 700;
938
+ color: var(--text);
939
+ }
940
+
941
+ .stats-drawer-provider {
942
+ font-size: 12px;
943
+ color: var(--muted);
944
+ margin-top: 2px;
945
+ }
946
+
947
+ .stats-drawer-error-block {
948
+ margin-top: 14px;
949
+ border-top: 1px solid var(--border);
950
+ padding-top: 12px;
951
+ }
952
+
953
+ .stats-drawer-error-label {
954
+ font-size: 11px;
955
+ font-weight: 600;
956
+ text-transform: uppercase;
957
+ letter-spacing: 0.05em;
958
+ color: var(--danger);
959
+ margin-bottom: 4px;
960
+ }
961
+
962
+ .stats-drawer-error-text {
963
+ font-size: 12px;
964
+ color: var(--text);
965
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
966
+ white-space: pre-wrap;
967
+ word-break: break-all;
968
+ }
969
+
970
+ .stats-drawer-metrics-grid {
971
+ display: grid;
972
+ grid-template-columns: repeat(2, 1fr);
973
+ gap: 12px;
974
+ }
975
+
976
+ .stats-drawer-metric-card {
977
+ background: var(--surface);
978
+ border: 1px solid var(--border);
979
+ border-radius: var(--radius-lg);
980
+ padding: 14px;
981
+ display: flex;
982
+ flex-direction: column;
983
+ justify-content: center;
984
+ }
985
+
986
+ .stats-drawer-metric-label {
987
+ display: flex;
988
+ align-items: center;
989
+ gap: 6px;
990
+ font-size: 11px;
991
+ font-weight: 600;
992
+ text-transform: uppercase;
993
+ letter-spacing: 0.05em;
994
+ color: var(--muted);
995
+ margin-bottom: 6px;
996
+ }
997
+
998
+ .stats-drawer-metric-icon {
999
+ color: var(--dim);
1000
+ }
1001
+
1002
+ .stats-drawer-metric-value {
1003
+ font-size: 16px;
1004
+ font-weight: 700;
1005
+ color: var(--text);
1006
+ font-variant-numeric: tabular-nums;
1007
+ }
1008
+
1009
+ .stats-drawer-metric-sub {
1010
+ font-size: 11px;
1011
+ color: var(--muted);
1012
+ margin-top: 2px;
1013
+ font-variant-numeric: tabular-nums;
1014
+ }
1015
+
1016
+ .stats-drawer-json-blocks {
1017
+ display: flex;
1018
+ flex-direction: column;
1019
+ gap: 16px;
1020
+ }
1021
+
1022
+ /* Collapsible JSON Block */
1023
+ .stats-json-block {
1024
+ border: 1px solid var(--border);
1025
+ border-radius: var(--radius-lg);
1026
+ background: var(--surface);
1027
+ overflow: hidden;
1028
+ }
1029
+
1030
+ .stats-json-block-header {
1031
+ padding: 10px 16px;
1032
+ background: var(--surface-2);
1033
+ border-bottom: 1px solid var(--border);
1034
+ display: flex;
1035
+ justify-content: space-between;
1036
+ align-items: center;
1037
+ cursor: pointer;
1038
+ user-select: none;
1039
+ font-size: 12px;
1040
+ font-weight: 600;
1041
+ }
1042
+
1043
+ .stats-json-block-header:hover {
1044
+ background: var(--border);
1045
+ }
1046
+
1047
+ .stats-json-block-title {
1048
+ color: var(--text);
1049
+ }
1050
+
1051
+ .stats-json-block-toggle-indicator {
1052
+ color: var(--muted);
1053
+ }
1054
+
1055
+ .stats-json-block-content-wrapper {
1056
+ padding: 16px;
1057
+ background: var(--page);
1058
+ overflow-x: auto;
1059
+ }
1060
+
1061
+ .stats-json-block-content {
1062
+ margin: 0;
1063
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1064
+ font-size: 12px;
1065
+ color: var(--text);
1066
+ line-height: 1.5;
1067
+ }
1068
+
1069
+ /* Skeleton Loading Element */
1070
+ .stats-skeleton {
1071
+ background: var(--surface-2);
1072
+ position: relative;
1073
+ overflow: hidden;
1074
+ border-radius: var(--radius-sm);
1075
+ }
1076
+
1077
+ .stats-skeleton[data-variant="text"] {
1078
+ height: 14px;
1079
+ margin-top: 4px;
1080
+ margin-bottom: 4px;
1081
+ }
1082
+
1083
+ .stats-skeleton[data-variant="circle"] {
1084
+ border-radius: 50%;
1085
+ }
1086
+
1087
+ /* Subtle Shimmer effect, respecting media queries for reduced motion */
1088
+ .stats-skeleton::after {
1089
+ content: "";
1090
+ position: absolute;
1091
+ inset: 0;
1092
+ transform: translateX(-100%);
1093
+ background: linear-gradient(
1094
+ 90deg,
1095
+ transparent 0%,
1096
+ rgba(255, 255, 255, 0.05) 50%,
1097
+ transparent 100%
1098
+ );
1099
+ animation: shimmer 1.5s infinite;
1100
+ }
1101
+
1102
+ @media (prefers-color-scheme: light) {
1103
+ .stats-skeleton::after {
1104
+ background: linear-gradient(
1105
+ 90deg,
1106
+ transparent 0%,
1107
+ rgba(0, 0, 0, 0.03) 50%,
1108
+ transparent 100%
1109
+ );
1110
+ }
1111
+ }
1112
+
1113
+ @keyframes shimmer {
1114
+ 100% { transform: translateX(100%); }
1115
+ }
1116
+
1117
+ @media (prefers-reduced-motion: reduce) {
1118
+ .stats-skeleton::after {
1119
+ animation: none;
1120
+ }
1121
+ }
1122
+
1123
+ .stats-boundary-skeleton {
1124
+ padding: 20px;
1125
+ border: 1px solid var(--border);
1126
+ border-radius: var(--radius-lg);
1127
+ background: var(--surface);
1128
+ }
1129
+
1130
+ /* Empty State / Error State */
1131
+ .stats-empty-state,
1132
+ .stats-error-state {
1133
+ padding: 48px 24px;
1134
+ display: flex;
1135
+ flex-direction: column;
1136
+ align-items: center;
1137
+ justify-content: center;
1138
+ text-align: center;
1139
+ background: var(--surface);
1140
+ border: 1px dashed var(--border);
1141
+ border-radius: var(--radius-lg);
1142
+ }
1143
+
1144
+ .stats-empty-state-message {
1145
+ font-size: 13px;
1146
+ color: var(--muted);
1147
+ margin: 0;
1148
+ }
1149
+
1150
+ .stats-error-state-content {
1151
+ max-width: 360px;
1152
+ }
1153
+
1154
+ .stats-error-state-title {
1155
+ font-size: 14px;
1156
+ font-weight: 600;
1157
+ color: var(--text);
1158
+ margin: 0 0 8px 0;
1159
+ }
1160
+
1161
+ .stats-error-state-message {
1162
+ font-size: 12px;
1163
+ color: var(--danger);
1164
+ margin: 0 0 16px 0;
1165
+ word-break: break-word;
1166
+ }
1167
+
1168
+ .stats-error-state-btn {
1169
+ margin: 0 auto;
1170
+ }
1171
+
1172
+ /* ==========================================================================
1173
+ Token-vocabulary bridge utility classes (used across route components)
1174
+ ========================================================================== */
1175
+ .stats-text-primary { color: var(--text); }
1176
+ .stats-text-secondary { color: var(--muted); }
1177
+ .stats-text-muted { color: var(--dim); }
1178
+ .stats-text-xs { font-size: 12px; }
1179
+ .stats-font-medium { font-weight: 500; }
1180
+ .stats-font-semibold { font-weight: 600; }
1181
+
1182
+ /* ==========================================================================
1183
+ Owned scrollbars — thin, brand-tinted, app-wide (ComPress convention)
1184
+ ========================================================================== */
1185
+ * {
1186
+ scrollbar-width: thin;
1187
+ scrollbar-color: var(--border-strong) transparent;
1188
+ }
1189
+
1190
+ *::-webkit-scrollbar {
1191
+ width: 10px;
1192
+ height: 10px;
1193
+ }
1194
+
1195
+ *::-webkit-scrollbar-track {
1196
+ background: transparent;
1197
+ }
1198
+
1199
+ *::-webkit-scrollbar-thumb {
1200
+ background: var(--border-strong);
1201
+ border: 2px solid transparent;
1202
+ background-clip: padding-box;
1203
+ border-radius: 999px;
1204
+ }
1205
+
1206
+ *::-webkit-scrollbar-thumb:hover {
1207
+ background: var(--dim);
1208
+ border: 2px solid transparent;
1209
+ background-clip: padding-box;
1210
+ }
1211
+
1212
+ *::-webkit-scrollbar-corner {
1213
+ background: transparent;
1214
+ }
1215
+
1216
+ /* ==========================================================================
1217
+ Comprehensive reduced-motion fallback for all interaction motion
1218
+ ========================================================================== */
1219
+ @media (prefers-reduced-motion: reduce) {
1220
+ .stats-button,
1221
+ .stats-table-tr-clickable,
1222
+ .stats-nav-rail-item,
1223
+ .stats-range-control-btn,
1224
+ .stats-segmented-control-btn {
1225
+ transition: none;
1226
+ }
1227
+
1228
+ .stats-button:active:not(:disabled) {
1229
+ transform: none;
1230
+ }
1231
+
1232
+ .stats-mobile-drawer,
1233
+ .stats-drawer {
1234
+ animation: none;
1235
+ }
1236
+ }
1237
+
1238
+ /* ==========================================================================
1239
+ Brand mark on the OMP wordmark (both rail + mobile drawer headers)
1240
+ ========================================================================== */
1241
+ .stats-logo-container {
1242
+ display: grid;
1243
+ grid-template-columns: auto 1fr;
1244
+ align-items: center;
1245
+ column-gap: 10px;
1246
+ }
1247
+
1248
+ .stats-logo-container::before {
1249
+ content: "";
1250
+ grid-row: 1 / 3;
1251
+ width: 16px;
1252
+ height: 16px;
1253
+ border-radius: 4px;
1254
+ background: linear-gradient(135deg, #ed4abf 0%, #9b4dff 50%, #5ad8e6 100%); /* OMP brand gradient mark */
1255
+ }
1256
+
1257
+ .stats-logo-text,
1258
+ .stats-logo-subtext {
1259
+ grid-column: 2;
1260
+ }
1261
+
1262
+ /* ==========================================================================
1263
+ JSON block copy affordance
1264
+ ========================================================================== */
1265
+ .stats-json-actions {
1266
+ display: flex;
1267
+ align-items: center;
1268
+ gap: 12px;
1269
+ }
1270
+
1271
+ .stats-json-copy-btn {
1272
+ display: inline-flex;
1273
+ align-items: center;
1274
+ gap: 5px;
1275
+ font-size: 11px;
1276
+ font-weight: 600;
1277
+ color: var(--muted);
1278
+ background: transparent;
1279
+ border: 1px solid var(--border);
1280
+ border-radius: var(--radius-sm);
1281
+ padding: 3px 8px;
1282
+ cursor: pointer;
1283
+ font-family: inherit;
1284
+ transition: color 0.12s ease, background 0.12s ease;
1285
+ }
1286
+
1287
+ .stats-json-copy-btn:hover {
1288
+ color: var(--text);
1289
+ background: var(--surface-2);
1290
+ }
1291
+
1292
+ /* ==========================================================================
1293
+ Empty-state icon
1294
+ ========================================================================== */
1295
+ .stats-empty-state-icon {
1296
+ color: var(--dim);
1297
+ margin-bottom: 12px;
1298
+ }
1299
+
1300
+ @media (prefers-reduced-motion: reduce) {
1301
+ .stats-json-copy-btn {
1302
+ transition: none;
1303
+ }
1304
+ }
1305
+
1306
+ /* ==========================================================================
1307
+ Theme toggle (system / light / dark)
1308
+ ========================================================================== */
1309
+ .stats-theme-toggle {
1310
+ display: inline-flex;
1311
+ align-items: center;
1312
+ justify-content: center;
1313
+ width: 32px;
1314
+ height: 32px;
1315
+ flex-shrink: 0;
1316
+ border: 1px solid var(--border);
1317
+ border-radius: var(--radius-md);
1318
+ background: var(--surface);
1319
+ color: var(--muted);
1320
+ cursor: pointer;
1321
+ transition: color 0.14s ease, background 0.14s ease, border-color 0.14s ease;
1322
+ }
1323
+
1324
+ .stats-theme-toggle:hover {
1325
+ color: var(--text);
1326
+ border-color: var(--border-strong);
1327
+ }
1328
+
1329
+ @media (prefers-reduced-motion: reduce) {
1330
+ .stats-theme-toggle {
1331
+ transition: none;
1332
+ }
1333
+ }