@progen-labs/spectra 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -2
  3. package/bin/spectra.js +215 -0
  4. package/dist/assets/index-BvBsPok5.js +46 -0
  5. package/dist/assets/index-Da3Le5Px.css +1 -0
  6. package/dist/index.html +14 -0
  7. package/index.html +13 -0
  8. package/package.json +76 -4
  9. package/server/analysis/cpu.js +369 -0
  10. package/server/analysis/findings.js +551 -0
  11. package/server/analysis/locate.js +99 -0
  12. package/server/analysis/report.js +750 -0
  13. package/server/analysis/sources.js +249 -0
  14. package/server/analysis/trace.js +314 -0
  15. package/server/api.js +292 -0
  16. package/server/chrome.js +81 -0
  17. package/server/cli.js +94 -0
  18. package/server/config.js +59 -0
  19. package/server/dashboard.js +154 -0
  20. package/server/explore/boot.js +150 -0
  21. package/server/explore/classify.js +169 -0
  22. package/server/explore/discover.js +162 -0
  23. package/server/explore/evaluate-cli.js +101 -0
  24. package/server/explore/evaluate.js +165 -0
  25. package/server/explore/paths.js +105 -0
  26. package/server/explore/runner.js +94 -0
  27. package/server/explore/test-explore.js +139 -0
  28. package/server/explore/test-paths.js +75 -0
  29. package/server/framework.js +67 -0
  30. package/server/git.js +59 -0
  31. package/server/pdf/render.js +60 -0
  32. package/server/pdf/template.js +419 -0
  33. package/server/plugin.js +19 -0
  34. package/server/probe.browser.js +689 -0
  35. package/server/recorder.js +610 -0
  36. package/server/signin.js +27 -0
  37. package/server/test-analysis.js +424 -0
  38. package/server/test-compare.js +473 -0
  39. package/server/test-sources.js +136 -0
  40. package/shared/compare.js +977 -0
  41. package/shared/snapshot.js +77 -0
  42. package/src/App.jsx +361 -0
  43. package/src/api.js +51 -0
  44. package/src/components/AppearanceMenu.jsx +76 -0
  45. package/src/components/CodeRef.jsx +48 -0
  46. package/src/components/ComparePanel.jsx +143 -0
  47. package/src/components/CompareResults.jsx +473 -0
  48. package/src/components/ControlBar.jsx +336 -0
  49. package/src/components/CulpritSphere.jsx +218 -0
  50. package/src/components/Culprits.jsx +118 -0
  51. package/src/components/CulpritsHero.jsx +109 -0
  52. package/src/components/EvaluationPanel.jsx +160 -0
  53. package/src/components/FilePanel.jsx +225 -0
  54. package/src/components/FindingsList.jsx +381 -0
  55. package/src/components/HeatTrees.jsx +222 -0
  56. package/src/components/Icons.jsx +58 -0
  57. package/src/components/LongTasks.jsx +199 -0
  58. package/src/components/NetworkPanel.jsx +98 -0
  59. package/src/components/PageMap.jsx +329 -0
  60. package/src/components/Popover.jsx +51 -0
  61. package/src/components/ReactPanel.jsx +200 -0
  62. package/src/components/SidePicker.jsx +207 -0
  63. package/src/components/SnapshotButton.jsx +140 -0
  64. package/src/components/TimeBreakdown.jsx +45 -0
  65. package/src/components/TraceButton.jsx +76 -0
  66. package/src/components/TreeView.jsx +173 -0
  67. package/src/components/Vitals.jsx +163 -0
  68. package/src/culprits.js +127 -0
  69. package/src/download.js +18 -0
  70. package/src/format.js +26 -0
  71. package/src/heat.js +177 -0
  72. package/src/highlight.js +45 -0
  73. package/src/hooks/useCompare.js +187 -0
  74. package/src/hooks/useSession.js +73 -0
  75. package/src/layout.js +55 -0
  76. package/src/main.jsx +15 -0
  77. package/src/styles.css +2478 -0
  78. package/src/theme.js +75 -0
  79. package/src/themes.css +286 -0
  80. package/vite.config.js +8 -0
package/src/styles.css ADDED
@@ -0,0 +1,2478 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ scrollbar-width: thin;
4
+ scrollbar-color: var(--border) transparent;
5
+ }
6
+ html,
7
+ body,
8
+ #root {
9
+ height: 100%;
10
+ margin: 0;
11
+ }
12
+ body {
13
+ background: var(--bg);
14
+ background-size: var(--bg-size, auto);
15
+ background-attachment: fixed;
16
+ color: var(--text);
17
+ font: 0.8125rem/1.45 var(--font-ui);
18
+ -webkit-font-smoothing: antialiased;
19
+ }
20
+ button,
21
+ input,
22
+ select {
23
+ font: inherit;
24
+ color: inherit;
25
+ }
26
+ button,
27
+ select,
28
+ input:not([type='checkbox']):not([type='radio']) {
29
+ background: var(--control);
30
+ border: 1px solid var(--border);
31
+ border-radius: var(--radius-sm);
32
+ padding: 0.35rem 0.75rem;
33
+ min-height: 2rem;
34
+ transition:
35
+ background 0.15s,
36
+ border-color 0.15s,
37
+ box-shadow 0.15s;
38
+ }
39
+ button {
40
+ border-radius: var(--btn-radius);
41
+ cursor: pointer;
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ gap: 0.4rem;
46
+ font-weight: 500;
47
+ white-space: nowrap;
48
+ }
49
+ select {
50
+ cursor: pointer;
51
+ }
52
+ input:not([type='checkbox']):not([type='radio']):focus {
53
+ border-color: var(--accent);
54
+ box-shadow: 0 0 0 3px var(--accent-soft);
55
+ outline: none;
56
+ }
57
+ button:hover:not(:disabled),
58
+ select:hover:not(:disabled) {
59
+ background: var(--control-hover);
60
+ border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
61
+ }
62
+ button:disabled,
63
+ select:disabled,
64
+ input:disabled {
65
+ opacity: 0.5;
66
+ cursor: default;
67
+ }
68
+ button.primary {
69
+ background: var(--accent-strong);
70
+ border-color: transparent;
71
+ color: var(--on-accent);
72
+ box-shadow: var(--accent-glow);
73
+ }
74
+ button.primary:hover:not(:disabled) {
75
+ background: var(--accent-strong);
76
+ border-color: transparent;
77
+ filter: brightness(1.1);
78
+ }
79
+ button.danger {
80
+ background: var(--poor);
81
+ border-color: transparent;
82
+ color: #fff;
83
+ }
84
+ button.danger:hover:not(:disabled) {
85
+ background: var(--poor);
86
+ border-color: transparent;
87
+ filter: brightness(1.08);
88
+ }
89
+ button.ghost {
90
+ background: transparent;
91
+ border-color: transparent;
92
+ color: var(--text-2);
93
+ }
94
+ button.ghost:hover:not(:disabled) {
95
+ background: var(--control);
96
+ border-color: var(--border);
97
+ color: var(--text);
98
+ }
99
+ button.ghost.destructive:hover:not(:disabled) {
100
+ color: var(--poor);
101
+ }
102
+ button.link {
103
+ background: none;
104
+ border: 0;
105
+ padding: 0;
106
+ min-height: 0;
107
+ color: var(--accent);
108
+ text-align: left;
109
+ display: inline;
110
+ font-weight: inherit;
111
+ }
112
+ button.link:hover:not(:disabled) {
113
+ background: none;
114
+ text-decoration: underline;
115
+ }
116
+ :focus-visible {
117
+ outline: 2px solid var(--accent);
118
+ outline-offset: 2px;
119
+ }
120
+ code,
121
+ .mono {
122
+ font-family: var(--font-mono);
123
+ font-size: 12px;
124
+ }
125
+ .muted {
126
+ color: var(--text-muted);
127
+ font-weight: 400;
128
+ }
129
+ .warn {
130
+ color: var(--warn);
131
+ }
132
+ .spacer {
133
+ flex: 1;
134
+ }
135
+ .pad {
136
+ padding: 1rem;
137
+ }
138
+ .num {
139
+ text-align: right;
140
+ font-variant-numeric: tabular-nums;
141
+ }
142
+ .empty {
143
+ display: grid;
144
+ place-content: center;
145
+ text-align: center;
146
+ gap: 0.4rem;
147
+ padding: 2rem;
148
+ color: var(--text-muted);
149
+ flex: 1;
150
+ }
151
+ .icon {
152
+ flex: none;
153
+ }
154
+
155
+ /* ---- shell ---- */
156
+ .app {
157
+ display: flex;
158
+ flex-direction: column;
159
+ gap: 0.7rem;
160
+ height: 100%;
161
+ padding: 0.75rem;
162
+ }
163
+ .topbar,
164
+ .session,
165
+ .workspace {
166
+ background: var(--panel);
167
+ -webkit-backdrop-filter: var(--blur);
168
+ backdrop-filter: var(--blur);
169
+ border: 1px solid var(--panel-border);
170
+ border-radius: var(--radius);
171
+ box-shadow: var(--shadow), var(--panel-edge);
172
+ }
173
+ .topbar {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 0.8rem;
177
+ padding: 0.55rem 0.7rem 0.55rem 0.9rem;
178
+ position: relative;
179
+ z-index: 10;
180
+ }
181
+ .brand {
182
+ display: flex;
183
+ align-items: center;
184
+ gap: 0.7rem;
185
+ min-width: 0;
186
+ }
187
+ .brand-mark {
188
+ display: grid;
189
+ place-items: center;
190
+ width: 2rem;
191
+ height: 2rem;
192
+ border-radius: calc(var(--radius-sm) - 1px);
193
+ color: #fff;
194
+ box-shadow:
195
+ inset 0 1px 0 rgb(255 255 255 / 0.4),
196
+ 0 4px 14px rgb(40 80 220 / 0.35);
197
+ }
198
+ .brand h1 {
199
+ margin: 0;
200
+ font-size: 0.95rem;
201
+ line-height: 1.2;
202
+ font-weight: 650;
203
+ letter-spacing: -0.01em;
204
+ }
205
+ .brand small {
206
+ display: block;
207
+ color: var(--text-muted);
208
+ font-size: 0.72rem;
209
+ font-family: var(--font-mono);
210
+ }
211
+ .report-picker {
212
+ display: inline-flex;
213
+ align-items: center;
214
+ gap: 0.3rem;
215
+ }
216
+ .report-picker select {
217
+ max-width: 24rem;
218
+ }
219
+ .error {
220
+ background: color-mix(in srgb, var(--poor) 14%, var(--panel-solid));
221
+ border: 1px solid color-mix(in srgb, var(--poor) 55%, transparent);
222
+ border-radius: var(--radius-sm);
223
+ padding: 0.5rem 0.8rem;
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 0.8rem;
227
+ }
228
+ .error button {
229
+ margin-left: auto;
230
+ }
231
+
232
+ /* ---- popovers ---- */
233
+ .popover {
234
+ position: relative;
235
+ display: inline-block;
236
+ }
237
+ .caret {
238
+ font-size: 0.7em;
239
+ opacity: 0.7;
240
+ }
241
+ .popover-panel,
242
+ .trace-note {
243
+ background: color-mix(in srgb, var(--panel-solid) 98%, transparent);
244
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
245
+ backdrop-filter: blur(24px) saturate(160%);
246
+ border: 1px solid var(--panel-border);
247
+ border-radius: var(--radius);
248
+ box-shadow: var(--shadow-pop), var(--panel-edge);
249
+ }
250
+ .popover-panel {
251
+ position: absolute;
252
+ top: calc(100% + 8px);
253
+ z-index: 40;
254
+ padding: 0.9rem;
255
+ min-width: 15rem;
256
+ }
257
+ .popover-panel.align-right {
258
+ right: 0;
259
+ }
260
+ .popover-panel.align-left {
261
+ left: 0;
262
+ }
263
+ .appearance,
264
+ .options-list {
265
+ display: grid;
266
+ gap: 1rem;
267
+ width: 17rem;
268
+ }
269
+ .choice-group {
270
+ border: 0;
271
+ margin: 0;
272
+ padding: 0;
273
+ min-width: 0;
274
+ }
275
+ .choice-group legend,
276
+ .field-label {
277
+ padding: 0;
278
+ margin-bottom: 0.4rem;
279
+ font-size: 0.7rem;
280
+ font-weight: 600;
281
+ color: var(--text-muted);
282
+ text-transform: uppercase;
283
+ letter-spacing: 0.08em;
284
+ }
285
+ .choice input {
286
+ position: absolute;
287
+ opacity: 0;
288
+ pointer-events: none;
289
+ }
290
+ .choice:focus-within {
291
+ outline: 2px solid var(--accent);
292
+ outline-offset: 2px;
293
+ }
294
+ .theme-cards {
295
+ display: grid;
296
+ grid-template-columns: 1fr 1fr;
297
+ gap: 0.5rem;
298
+ }
299
+ .theme-cards .choice {
300
+ position: relative;
301
+ display: grid;
302
+ gap: 0.15rem;
303
+ padding: 0.45rem;
304
+ border: 1px solid var(--border);
305
+ border-radius: var(--radius-sm);
306
+ cursor: pointer;
307
+ background: var(--surface-2);
308
+ transition:
309
+ border-color 0.15s,
310
+ box-shadow 0.15s;
311
+ }
312
+ .theme-cards .choice:hover {
313
+ border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
314
+ }
315
+ .theme-cards .choice.selected {
316
+ border-color: var(--accent);
317
+ box-shadow: 0 0 0 3px var(--accent-soft);
318
+ }
319
+ .theme-cards .choice-label {
320
+ font-weight: 600;
321
+ margin-top: 0.25rem;
322
+ }
323
+ .theme-cards small {
324
+ font-size: 0.7rem;
325
+ line-height: 1.25;
326
+ }
327
+ .swatch {
328
+ position: relative;
329
+ display: block;
330
+ height: 2.9rem;
331
+ border-radius: calc(var(--radius-sm) - 3px);
332
+ overflow: hidden;
333
+ }
334
+ .swatch i {
335
+ position: absolute;
336
+ left: 0.45rem;
337
+ height: 0.5rem;
338
+ border-radius: 999px;
339
+ }
340
+ .swatch i:first-child {
341
+ top: 0.5rem;
342
+ width: 55%;
343
+ }
344
+ .swatch i:last-child {
345
+ top: 1.35rem;
346
+ width: 35%;
347
+ }
348
+ .swatch-glass {
349
+ background:
350
+ radial-gradient(circle at 20% 20%, #8ec5ff, transparent 60%),
351
+ radial-gradient(circle at 85% 30%, #ffb8df, transparent 55%),
352
+ linear-gradient(135deg, #c9bcff, #9fd6ff);
353
+ }
354
+ .swatch-glass i {
355
+ background: rgb(255 255 255 / 0.7);
356
+ box-shadow: inset 0 1px 0 #fff;
357
+ }
358
+ .swatch-space {
359
+ background:
360
+ radial-gradient(1px 1px at 70% 25%, #fff, transparent),
361
+ radial-gradient(1px 1px at 88% 70%, #bcd4ff, transparent),
362
+ radial-gradient(1px 1px at 45% 80%, #fff, transparent),
363
+ radial-gradient(circle at 90% 0%, rgb(124 92 255 / 0.6), transparent 60%),
364
+ #05071a;
365
+ }
366
+ .swatch-space i {
367
+ background: linear-gradient(90deg, #22d3ee, #6f6bff);
368
+ }
369
+ .segmented,
370
+ .segmented-control {
371
+ display: inline-flex;
372
+ gap: 2px;
373
+ padding: 3px;
374
+ background: var(--surface-2);
375
+ border: 1px solid var(--border);
376
+ border-radius: var(--btn-radius);
377
+ }
378
+ .segmented-control {
379
+ display: flex;
380
+ }
381
+ .segmented button,
382
+ .segmented-control .choice {
383
+ flex: 1;
384
+ background: transparent;
385
+ border: 0;
386
+ min-height: 1.7rem;
387
+ padding: 0.15rem 0.8rem;
388
+ border-radius: calc(var(--btn-radius) - 2px);
389
+ color: var(--text-2);
390
+ font-weight: 500;
391
+ text-align: center;
392
+ cursor: pointer;
393
+ position: relative;
394
+ }
395
+ .segmented-control .choice {
396
+ display: grid;
397
+ place-items: center;
398
+ }
399
+ .segmented button:hover:not(:disabled) {
400
+ background: var(--control);
401
+ border: 0;
402
+ }
403
+ .segmented button[aria-pressed='true'],
404
+ .segmented-control .choice.selected {
405
+ background: var(--accent-soft);
406
+ color: var(--accent);
407
+ font-weight: 600;
408
+ }
409
+
410
+ /* ---- switches ---- */
411
+ label.switch {
412
+ display: inline-flex;
413
+ align-items: center;
414
+ gap: 0.5rem;
415
+ cursor: pointer;
416
+ color: var(--text-2);
417
+ user-select: none;
418
+ }
419
+ .switch input {
420
+ appearance: none;
421
+ -webkit-appearance: none;
422
+ position: relative;
423
+ flex: none;
424
+ width: 2rem;
425
+ height: 1.15rem;
426
+ margin: 0;
427
+ border-radius: 999px;
428
+ background: var(--control);
429
+ border: 1px solid var(--border);
430
+ cursor: pointer;
431
+ transition: background 0.15s;
432
+ }
433
+ .switch input::after {
434
+ content: '';
435
+ position: absolute;
436
+ top: 1px;
437
+ left: 1px;
438
+ width: calc(1.15rem - 4px);
439
+ height: calc(1.15rem - 4px);
440
+ border-radius: 50%;
441
+ background: var(--text-muted);
442
+ transition:
443
+ transform 0.15s,
444
+ background 0.15s;
445
+ }
446
+ .switch input:checked {
447
+ background: var(--accent-strong);
448
+ border-color: transparent;
449
+ }
450
+ .switch input:checked::after {
451
+ background: #fff;
452
+ transform: translateX(0.85rem);
453
+ }
454
+ .switch input:disabled {
455
+ cursor: default;
456
+ }
457
+
458
+ /* ---- session (connect / record) ---- */
459
+ .session {
460
+ position: relative;
461
+ z-index: 5;
462
+ padding: 0.7rem 0.8rem;
463
+ display: grid;
464
+ gap: 0.6rem;
465
+ }
466
+ .session-row {
467
+ display: flex;
468
+ align-items: center;
469
+ gap: 0.5rem;
470
+ flex-wrap: wrap;
471
+ }
472
+ .session-row + .session-row {
473
+ padding-top: 0.6rem;
474
+ border-top: 1px solid var(--border);
475
+ }
476
+ .session label,
477
+ .connect label {
478
+ color: var(--text-2);
479
+ }
480
+ .address {
481
+ flex: 1;
482
+ min-width: 14rem;
483
+ font-family: var(--font-mono);
484
+ font-size: 12px;
485
+ }
486
+ .btn-group {
487
+ display: inline-flex;
488
+ align-items: center;
489
+ gap: 0.4rem;
490
+ }
491
+ .connect {
492
+ display: grid;
493
+ gap: 0.7rem;
494
+ }
495
+ .connect-head h2 {
496
+ margin: 0;
497
+ font-size: 1rem;
498
+ letter-spacing: -0.01em;
499
+ }
500
+ .connect-head p {
501
+ margin: 0.15rem 0 0;
502
+ }
503
+ .connect-main {
504
+ display: flex;
505
+ gap: 0.5rem;
506
+ align-items: flex-end;
507
+ flex-wrap: wrap;
508
+ }
509
+ .field {
510
+ display: grid;
511
+ gap: 0.25rem;
512
+ color: var(--text-2);
513
+ }
514
+ .connect-main .field {
515
+ flex: 1;
516
+ min-width: 16rem;
517
+ }
518
+ .field input {
519
+ width: 100%;
520
+ }
521
+ .field-row {
522
+ display: flex;
523
+ gap: 0.4rem;
524
+ }
525
+ .field-row input {
526
+ flex: 1;
527
+ min-width: 0;
528
+ }
529
+ .advanced {
530
+ display: grid;
531
+ gap: 0.8rem;
532
+ padding-top: 0.7rem;
533
+ border-top: 1px solid var(--border);
534
+ }
535
+ .advanced-section {
536
+ display: grid;
537
+ gap: 0.4rem;
538
+ }
539
+ .advanced-section + .advanced-section {
540
+ padding-top: 0.7rem;
541
+ border-top: 1px dashed var(--border);
542
+ }
543
+ .advanced-row {
544
+ display: flex;
545
+ gap: 0.5rem;
546
+ align-items: flex-end;
547
+ flex-wrap: wrap;
548
+ }
549
+ .advanced-row .field {
550
+ flex: 1;
551
+ min-width: 16rem;
552
+ }
553
+ .pill {
554
+ display: inline-flex;
555
+ align-items: center;
556
+ gap: 0.35rem;
557
+ border: 1px solid var(--border);
558
+ border-radius: 999px;
559
+ padding: 0.1rem 0.65rem;
560
+ background: var(--surface-2);
561
+ font-size: 0.75rem;
562
+ white-space: nowrap;
563
+ }
564
+ .pill.ok {
565
+ color: var(--good);
566
+ border-color: color-mix(in srgb, var(--good) 40%, transparent);
567
+ }
568
+ .pill.warn {
569
+ color: var(--warn);
570
+ border-color: color-mix(in srgb, var(--warn) 45%, transparent);
571
+ }
572
+ .rec-strip {
573
+ display: flex;
574
+ align-items: center;
575
+ gap: 0.8rem;
576
+ flex-wrap: wrap;
577
+ flex: 1;
578
+ }
579
+ .rec-strip.recording {
580
+ padding: 0.4rem 0.6rem;
581
+ border-radius: var(--radius-sm);
582
+ background: color-mix(in srgb, var(--poor) 10%, transparent);
583
+ border: 1px solid color-mix(in srgb, var(--poor) 35%, transparent);
584
+ }
585
+ .rec {
586
+ display: inline-flex;
587
+ align-items: center;
588
+ gap: 0.5rem;
589
+ font-weight: 600;
590
+ }
591
+ .rec i {
592
+ width: 10px;
593
+ height: 10px;
594
+ border-radius: 50%;
595
+ background: var(--poor);
596
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--poor) 25%, transparent);
597
+ animation: pulse 1s infinite;
598
+ }
599
+ .rec i.spin {
600
+ background: var(--accent);
601
+ box-shadow: 0 0 0 4px var(--accent-soft);
602
+ }
603
+ .elapsed {
604
+ font-family: var(--font-mono);
605
+ font-variant-numeric: tabular-nums;
606
+ color: var(--text-2);
607
+ font-weight: 400;
608
+ }
609
+ @keyframes pulse {
610
+ 50% {
611
+ opacity: 0.3;
612
+ }
613
+ }
614
+ @media (prefers-reduced-motion: reduce) {
615
+ .rec i {
616
+ animation: none;
617
+ }
618
+ * {
619
+ transition: none !important;
620
+ }
621
+ }
622
+
623
+ /* ---- workspace ---- */
624
+ .workspace {
625
+ flex: 1;
626
+ min-height: 0;
627
+ display: flex;
628
+ flex-direction: column;
629
+ overflow: hidden;
630
+ }
631
+ .report-bar {
632
+ display: flex;
633
+ align-items: center;
634
+ gap: 0.8rem;
635
+ flex-wrap: wrap;
636
+ padding: 0.6rem 0.9rem;
637
+ border-bottom: 1px solid var(--border);
638
+ }
639
+ .report-title {
640
+ font-weight: 600;
641
+ font-size: 0.9rem;
642
+ max-width: 28rem;
643
+ overflow: hidden;
644
+ text-overflow: ellipsis;
645
+ white-space: nowrap;
646
+ }
647
+ .report-meta {
648
+ display: flex;
649
+ gap: 0.35rem;
650
+ flex-wrap: wrap;
651
+ }
652
+ .tabs {
653
+ display: flex;
654
+ gap: 0.2rem;
655
+ overflow-x: auto;
656
+ }
657
+ .main-tabs {
658
+ align-items: center;
659
+ padding: 0.5rem 0.9rem;
660
+ border-bottom: 1px solid var(--border);
661
+ }
662
+ .main-tabs .tab-list {
663
+ display: flex;
664
+ gap: 0.2rem;
665
+ min-width: 0;
666
+ overflow-x: auto;
667
+ }
668
+ .tabs button {
669
+ background: transparent;
670
+ border: 0;
671
+ min-height: 1.9rem;
672
+ padding: 0.25rem 0.8rem;
673
+ color: var(--text-2);
674
+ }
675
+ .tabs button:hover:not(:disabled) {
676
+ background: var(--control);
677
+ border: 0;
678
+ color: var(--text);
679
+ }
680
+ .tabs button[aria-selected='true'] {
681
+ background: var(--accent-strong);
682
+ color: var(--on-accent);
683
+ box-shadow: var(--accent-glow);
684
+ }
685
+ .count {
686
+ margin-left: 0.1rem;
687
+ background: var(--accent-soft);
688
+ color: var(--accent);
689
+ border-radius: 999px;
690
+ padding: 0 0.45rem;
691
+ font-size: 11px;
692
+ font-weight: 600;
693
+ }
694
+ .tabs button[aria-selected='true'] .count {
695
+ background: rgb(255 255 255 / 0.25);
696
+ color: inherit;
697
+ }
698
+ .main {
699
+ flex: 1;
700
+ display: flex;
701
+ min-height: 0;
702
+ }
703
+ .content {
704
+ flex: 1;
705
+ min-width: 0;
706
+ overflow: auto;
707
+ display: flex;
708
+ flex-direction: column;
709
+ }
710
+
711
+ .welcome {
712
+ flex: 1;
713
+ overflow: auto;
714
+ display: grid;
715
+ align-content: center;
716
+ justify-items: center;
717
+ gap: 1.6rem;
718
+ padding: 2rem 1.5rem;
719
+ text-align: center;
720
+ }
721
+ .welcome h2 {
722
+ margin: 0;
723
+ font-size: 1.6rem;
724
+ letter-spacing: -0.02em;
725
+ }
726
+ .welcome-lead {
727
+ margin: 0.4rem 0 0;
728
+ font-size: 0.95rem;
729
+ color: var(--text-2);
730
+ }
731
+ .steps {
732
+ list-style: none;
733
+ margin: 0;
734
+ padding: 0;
735
+ display: grid;
736
+ grid-template-columns: repeat(3, minmax(0, 17rem));
737
+ gap: 0.9rem;
738
+ text-align: left;
739
+ }
740
+ .steps li {
741
+ display: grid;
742
+ gap: 0.35rem;
743
+ align-content: start;
744
+ padding: 1rem;
745
+ background: var(--surface-2);
746
+ border: 1px solid var(--border);
747
+ border-radius: var(--radius);
748
+ line-height: 1.55;
749
+ }
750
+ .step-num {
751
+ display: grid;
752
+ place-items: center;
753
+ width: 1.7rem;
754
+ height: 1.7rem;
755
+ border-radius: 50%;
756
+ background: var(--accent-soft);
757
+ color: var(--accent);
758
+ font-weight: 700;
759
+ }
760
+ .steps h3 {
761
+ margin: 0.2rem 0 0;
762
+ font-size: 0.9rem;
763
+ }
764
+ .steps p {
765
+ margin: 0;
766
+ color: var(--text-2);
767
+ }
768
+ .welcome-foot {
769
+ max-width: 44rem;
770
+ margin: 0;
771
+ color: var(--text-muted);
772
+ }
773
+ @media (max-width: 900px) {
774
+ .steps {
775
+ grid-template-columns: 1fr;
776
+ }
777
+ }
778
+
779
+ /* ---- culprits ---- */
780
+ .culprits {
781
+ padding: 1rem 1.2rem;
782
+ display: grid;
783
+ gap: 1.2rem;
784
+ align-content: start;
785
+ }
786
+ .culprits h2,
787
+ .network h2,
788
+ .long-tasks h2 {
789
+ font-size: 0.9rem;
790
+ margin: 0 0 0.5rem;
791
+ }
792
+ .vital-label {
793
+ color: var(--text-2);
794
+ font-size: 12px;
795
+ }
796
+ .vital-value {
797
+ font-size: 1.25rem;
798
+ font-weight: 600;
799
+ font-variant-numeric: tabular-nums;
800
+ }
801
+ .vital-rating {
802
+ font-size: 12px;
803
+ }
804
+ .rating-good {
805
+ border-left-color: var(--good);
806
+ }
807
+ .rating-good .vital-rating {
808
+ color: var(--good);
809
+ }
810
+ .rating-warn {
811
+ border-left-color: var(--warn);
812
+ }
813
+ .rating-warn .vital-rating {
814
+ color: var(--warn);
815
+ }
816
+ .rating-poor {
817
+ border-left-color: var(--poor);
818
+ }
819
+ .rating-poor .vital-rating {
820
+ color: var(--poor);
821
+ }
822
+ .rating-none {
823
+ border-left-color: var(--border);
824
+ }
825
+ .rating-none .vital-rating {
826
+ color: var(--text-muted);
827
+ }
828
+
829
+ .breakdown-bar {
830
+ display: flex;
831
+ height: 16px;
832
+ border-radius: 4px;
833
+ overflow: hidden;
834
+ gap: 2px;
835
+ background: var(--surface);
836
+ }
837
+ .breakdown-bar.small {
838
+ height: 8px;
839
+ margin: 0.35rem 0;
840
+ }
841
+ .breakdown-bar span {
842
+ min-width: 2px;
843
+ }
844
+ .breakdown-legend {
845
+ display: flex;
846
+ flex-wrap: wrap;
847
+ gap: 0.4rem 1.2rem;
848
+ list-style: none;
849
+ padding: 0;
850
+ margin: 0.5rem 0 0;
851
+ }
852
+ .breakdown-legend i {
853
+ display: inline-block;
854
+ width: 10px;
855
+ height: 10px;
856
+ border-radius: 2px;
857
+ margin-right: 0.35rem;
858
+ }
859
+
860
+ .sev-critical {
861
+ border-left-color: var(--poor);
862
+ }
863
+ .sev-warning {
864
+ border-left-color: var(--warn);
865
+ }
866
+ .sev-info {
867
+ border-left-color: var(--accent);
868
+ }
869
+ .badge-sev {
870
+ font-weight: 600;
871
+ font-size: 12px;
872
+ }
873
+ .sev-critical .badge-sev {
874
+ color: var(--poor);
875
+ }
876
+ .sev-warning .badge-sev {
877
+ color: var(--warn);
878
+ }
879
+ .sev-info .badge-sev {
880
+ color: var(--accent);
881
+ }
882
+ .chip {
883
+ border: 1px solid var(--border);
884
+ border-radius: 4px;
885
+ padding: 0 0.4rem;
886
+ font-size: 11px;
887
+ color: var(--text-2);
888
+ }
889
+ .chip-warn {
890
+ color: var(--warn);
891
+ border-color: var(--warn);
892
+ }
893
+
894
+ /* ---- trees ---- */
895
+ .tree-pane {
896
+ flex: 1;
897
+ display: flex;
898
+ flex-direction: column;
899
+ min-height: 0;
900
+ }
901
+ .tree-toolbar {
902
+ display: flex;
903
+ align-items: center;
904
+ gap: 0.8rem;
905
+ padding: 0.4rem 1rem;
906
+ border-bottom: 1px solid var(--border);
907
+ flex-wrap: wrap;
908
+ }
909
+ .tree-svg {
910
+ flex: 1;
911
+ min-height: 0;
912
+ width: 100%;
913
+ display: block;
914
+ cursor: grab;
915
+ background: radial-gradient(var(--grid) 1px, transparent 1px) 0 0 / 22px 22px;
916
+ }
917
+ .tree-svg:active {
918
+ cursor: grabbing;
919
+ }
920
+ .tree-links path {
921
+ fill: none;
922
+ stroke: var(--border);
923
+ stroke-width: 1.2;
924
+ }
925
+ .node-hit {
926
+ fill: transparent;
927
+ }
928
+ .node:hover .node-hit {
929
+ fill: var(--surface-2);
930
+ }
931
+ .node-dot {
932
+ fill: color-mix(in srgb, var(--heat) var(--heat-pct, 20%), var(--surface));
933
+ stroke: var(--heat);
934
+ stroke-width: 1.5;
935
+ cursor: pointer;
936
+ }
937
+ .node.vendor .node-dot {
938
+ fill: var(--surface);
939
+ stroke: var(--text-muted);
940
+ stroke-dasharray: 2 2;
941
+ }
942
+ .node.dir .node-dot {
943
+ stroke-width: 2;
944
+ }
945
+ .node.selected .node-hit {
946
+ fill: color-mix(in srgb, var(--accent) 22%, transparent);
947
+ }
948
+ .node.selected .node-label {
949
+ font-weight: 700;
950
+ }
951
+ .node-glyph {
952
+ font-size: 10px;
953
+ fill: var(--text);
954
+ pointer-events: none;
955
+ }
956
+ .node-label {
957
+ fill: var(--text);
958
+ font-size: 12px;
959
+ cursor: pointer;
960
+ }
961
+ .node.dir .node-label {
962
+ fill: var(--text-2);
963
+ font-weight: 600;
964
+ }
965
+ .legend {
966
+ display: flex;
967
+ gap: 1.2rem;
968
+ align-items: center;
969
+ padding: 0.4rem 1rem;
970
+ border-top: 1px solid var(--border);
971
+ color: var(--text-2);
972
+ flex-wrap: wrap;
973
+ }
974
+ .ramp {
975
+ display: inline-block;
976
+ width: 90px;
977
+ height: 10px;
978
+ border-radius: 2px;
979
+ vertical-align: middle;
980
+ margin-right: 0.4rem;
981
+ background: linear-gradient(
982
+ 90deg,
983
+ color-mix(in srgb, var(--heat) 12%, var(--surface)),
984
+ var(--heat)
985
+ );
986
+ border: 1px solid var(--heat);
987
+ }
988
+ .ring {
989
+ display: inline-block;
990
+ width: 10px;
991
+ height: 10px;
992
+ border-radius: 50%;
993
+ border: 1.5px dashed var(--text-muted);
994
+ margin-right: 0.35rem;
995
+ vertical-align: middle;
996
+ }
997
+
998
+ /* ---- side panel ---- */
999
+ .file-panel {
1000
+ width: 34rem;
1001
+ max-width: 45vw;
1002
+ flex: none;
1003
+ border-left: 1px solid var(--border);
1004
+ display: flex;
1005
+ flex-direction: column;
1006
+ min-height: 0;
1007
+ }
1008
+ .panel-head {
1009
+ display: flex;
1010
+ align-items: center;
1011
+ gap: 0.5rem;
1012
+ padding: 0.5rem 0.8rem;
1013
+ border-bottom: 1px solid var(--border);
1014
+ }
1015
+ .panel-head h2 {
1016
+ flex: 1;
1017
+ margin: 0;
1018
+ font-size: 0.8125rem;
1019
+ font-family: ui-monospace, Menlo, monospace;
1020
+ overflow: hidden;
1021
+ text-overflow: ellipsis;
1022
+ white-space: nowrap;
1023
+ }
1024
+ .metrics {
1025
+ display: grid;
1026
+ grid-template-columns: repeat(3, 1fr);
1027
+ gap: 0.4rem;
1028
+ padding: 0.6rem 0.8rem;
1029
+ }
1030
+ .metric {
1031
+ background: var(--surface-2);
1032
+ border: 1px solid var(--border);
1033
+ border-radius: 6px;
1034
+ padding: 0.3rem 0.5rem;
1035
+ }
1036
+ .metric-value {
1037
+ font-weight: 600;
1038
+ font-variant-numeric: tabular-nums;
1039
+ }
1040
+ .metric.warn .metric-value {
1041
+ color: var(--warn);
1042
+ }
1043
+ .metric-label {
1044
+ font-size: 11px;
1045
+ color: var(--text-muted);
1046
+ }
1047
+ .mini-findings {
1048
+ list-style: none;
1049
+ margin: 0 0.8rem 0.5rem;
1050
+ padding: 0;
1051
+ display: grid;
1052
+ gap: 0.3rem;
1053
+ }
1054
+ .mini-findings li {
1055
+ border-left: 3px solid;
1056
+ padding-left: 0.5rem;
1057
+ }
1058
+ .file-panel .tabs {
1059
+ padding: 0 0.8rem;
1060
+ border-bottom: 1px solid var(--border);
1061
+ }
1062
+ .panel-body {
1063
+ flex: 1;
1064
+ overflow: auto;
1065
+ display: flex;
1066
+ flex-direction: column;
1067
+ min-height: 0;
1068
+ }
1069
+
1070
+ /* ---- tables ---- */
1071
+ table.data {
1072
+ width: 100%;
1073
+ border-collapse: collapse;
1074
+ }
1075
+ table.data th {
1076
+ text-align: left;
1077
+ font-weight: 600;
1078
+ color: var(--text-2);
1079
+ position: sticky;
1080
+ top: 0;
1081
+ background: var(--panel-solid);
1082
+ border-bottom: 1px solid var(--border);
1083
+ padding: 0.4rem 0.6rem;
1084
+ }
1085
+ table.data th button {
1086
+ background: none;
1087
+ border: 0;
1088
+ padding: 0;
1089
+ font-weight: 600;
1090
+ color: inherit;
1091
+ }
1092
+ table.data td {
1093
+ padding: 0.25rem 0.6rem;
1094
+ border-bottom: 1px solid var(--grid);
1095
+ vertical-align: top;
1096
+ }
1097
+ tr.open td {
1098
+ background: var(--surface-2);
1099
+ }
1100
+ tr.detail td {
1101
+ background: var(--surface-2);
1102
+ }
1103
+ .detail-grid {
1104
+ display: grid;
1105
+ grid-template-columns: 1fr 2fr;
1106
+ gap: 1.5rem;
1107
+ }
1108
+ .detail-grid h4 {
1109
+ margin: 0 0 0.3rem;
1110
+ font-size: 12px;
1111
+ text-transform: uppercase;
1112
+ letter-spacing: 0.04em;
1113
+ color: var(--text-2);
1114
+ }
1115
+ .detail-grid ul {
1116
+ margin: 0;
1117
+ padding-left: 1.1rem;
1118
+ }
1119
+ .summary-row {
1120
+ display: flex;
1121
+ gap: 1.4rem;
1122
+ flex-wrap: wrap;
1123
+ padding: 0.6rem 1rem;
1124
+ color: var(--text-2);
1125
+ border-bottom: 1px solid var(--border);
1126
+ }
1127
+ .bar {
1128
+ height: 3px;
1129
+ background: var(--grid);
1130
+ border-radius: 2px;
1131
+ margin-top: 3px;
1132
+ }
1133
+ .bar span {
1134
+ display: block;
1135
+ height: 100%;
1136
+ background: var(--heat);
1137
+ border-radius: 2px;
1138
+ }
1139
+ .react-panel,
1140
+ .network {
1141
+ overflow: auto;
1142
+ }
1143
+ .network section {
1144
+ margin: 1.2rem 0;
1145
+ }
1146
+
1147
+ /* ---- long tasks ---- */
1148
+ .timeline {
1149
+ width: 100%;
1150
+ height: 130px;
1151
+ background: var(--surface-2);
1152
+ border-bottom: 1px solid var(--border);
1153
+ }
1154
+ .timeline .task {
1155
+ fill: var(--cat-other);
1156
+ }
1157
+ .timeline .task.long {
1158
+ fill: var(--poor);
1159
+ }
1160
+ .timeline .limit {
1161
+ stroke: var(--warn);
1162
+ stroke-dasharray: 4 4;
1163
+ vector-effect: non-scaling-stroke;
1164
+ }
1165
+ .timeline .axis {
1166
+ fill: var(--text-muted);
1167
+ font-size: 11px;
1168
+ }
1169
+ .task-list {
1170
+ list-style: none;
1171
+ margin: 0;
1172
+ padding: 0.8rem 1rem;
1173
+ display: grid;
1174
+ gap: 0.7rem;
1175
+ }
1176
+ .task-list > li {
1177
+ border: 1px solid var(--border);
1178
+ border-left-width: 4px;
1179
+ border-radius: var(--radius-sm);
1180
+ padding: 0.6rem 0.9rem;
1181
+ background: var(--surface-2);
1182
+ }
1183
+ .task-head {
1184
+ display: flex;
1185
+ gap: 0.7rem;
1186
+ align-items: center;
1187
+ flex-wrap: wrap;
1188
+ }
1189
+ .task-cols {
1190
+ display: grid;
1191
+ grid-template-columns: 1fr 1fr;
1192
+ gap: 1rem;
1193
+ }
1194
+ .task-cols h4 {
1195
+ margin: 0.3rem 0;
1196
+ font-size: 12px;
1197
+ text-transform: uppercase;
1198
+ letter-spacing: 0.04em;
1199
+ color: var(--text-2);
1200
+ }
1201
+ .row {
1202
+ display: flex;
1203
+ justify-content: space-between;
1204
+ gap: 0.6rem;
1205
+ width: 100%;
1206
+ }
1207
+ .row span {
1208
+ overflow: hidden;
1209
+ text-overflow: ellipsis;
1210
+ white-space: nowrap;
1211
+ }
1212
+
1213
+ /* ---- source view ---- */
1214
+ .code-scroll {
1215
+ flex: 1;
1216
+ overflow: auto;
1217
+ display: flex;
1218
+ align-items: flex-start;
1219
+ font:
1220
+ 12px/18px ui-monospace,
1221
+ Menlo,
1222
+ Consolas,
1223
+ monospace;
1224
+ }
1225
+ .gutter {
1226
+ position: sticky;
1227
+ left: 0;
1228
+ background: var(--surface-2);
1229
+ border-right: 1px solid var(--border);
1230
+ text-align: right;
1231
+ flex: none;
1232
+ }
1233
+ .gutter-line {
1234
+ height: 18px;
1235
+ padding: 0 0.5rem;
1236
+ color: var(--text-muted);
1237
+ white-space: nowrap;
1238
+ }
1239
+ .gutter-hot {
1240
+ background: color-mix(in srgb, var(--heat) 22%, transparent);
1241
+ color: var(--text);
1242
+ font-weight: 600;
1243
+ }
1244
+ .code {
1245
+ margin: 0;
1246
+ padding: 0 1rem;
1247
+ flex: 1;
1248
+ }
1249
+ .code code.hljs {
1250
+ background: none;
1251
+ padding: 0;
1252
+ font: inherit;
1253
+ display: block;
1254
+ white-space: pre;
1255
+ }
1256
+ .hljs-comment,
1257
+ .hljs-quote {
1258
+ color: var(--text-muted);
1259
+ font-style: italic;
1260
+ }
1261
+ .hljs-keyword,
1262
+ .hljs-selector-tag,
1263
+ .hljs-literal {
1264
+ color: var(--code-keyword);
1265
+ }
1266
+ .hljs-string,
1267
+ .hljs-regexp,
1268
+ .hljs-attr {
1269
+ color: var(--code-string);
1270
+ }
1271
+ .hljs-number,
1272
+ .hljs-built_in,
1273
+ .hljs-type {
1274
+ color: var(--code-number);
1275
+ }
1276
+ .hljs-title,
1277
+ .hljs-function,
1278
+ .hljs-name {
1279
+ color: var(--accent);
1280
+ }
1281
+
1282
+ /* ---- exact culprits ---- */
1283
+ .culprit-box {
1284
+ margin: 0.5rem 0 0.2rem;
1285
+ padding: 0.5rem 0.7rem;
1286
+ border: 1px dashed var(--border);
1287
+ border-radius: 6px;
1288
+ background: var(--surface);
1289
+ }
1290
+ .culprit-box h4 {
1291
+ margin: 0 0 0.3rem;
1292
+ font-size: 11px;
1293
+ text-transform: uppercase;
1294
+ letter-spacing: 0.05em;
1295
+ color: var(--text-2);
1296
+ }
1297
+ .culprit-box ul {
1298
+ margin: 0;
1299
+ padding: 0;
1300
+ list-style: none;
1301
+ display: grid;
1302
+ gap: 0.25rem;
1303
+ }
1304
+ code.fn {
1305
+ font-weight: 700;
1306
+ color: var(--text);
1307
+ }
1308
+ .code-ref {
1309
+ display: inline-flex;
1310
+ align-items: baseline;
1311
+ gap: 0.4rem;
1312
+ flex-wrap: wrap;
1313
+ }
1314
+ .code-ref b {
1315
+ font-weight: 700;
1316
+ }
1317
+ .editor-link {
1318
+ font-size: 11px;
1319
+ color: var(--accent);
1320
+ text-decoration: none;
1321
+ border: 1px solid var(--border);
1322
+ border-radius: 4px;
1323
+ padding: 0 0.3rem;
1324
+ }
1325
+ .editor-link:hover {
1326
+ border-color: var(--accent);
1327
+ }
1328
+ table.hot-functions td {
1329
+ vertical-align: middle;
1330
+ }
1331
+ tr.vendor-row code.fn {
1332
+ font-weight: 400;
1333
+ color: var(--text-2);
1334
+ }
1335
+
1336
+ /* ---- trace export ---- */
1337
+ .trace-button {
1338
+ position: relative;
1339
+ }
1340
+ .trace-note {
1341
+ position: absolute;
1342
+ right: 0;
1343
+ top: calc(100% + 8px);
1344
+ z-index: 40;
1345
+ width: 30rem;
1346
+ max-width: 80vw;
1347
+ padding: 0.8rem 2rem 0.8rem 1rem;
1348
+ line-height: 1.5;
1349
+ }
1350
+ .trace-note.error {
1351
+ border-color: var(--poor);
1352
+ }
1353
+ .trace-note ol {
1354
+ margin: 0.4rem 0 0;
1355
+ padding-left: 1.2rem;
1356
+ }
1357
+ .trace-note code {
1358
+ word-break: break-all;
1359
+ }
1360
+ .trace-note .close {
1361
+ position: absolute;
1362
+ top: 0.4rem;
1363
+ right: 0.4rem;
1364
+ background: none;
1365
+ border: 0;
1366
+ min-height: 0;
1367
+ padding: 0 0.3rem;
1368
+ font-size: 1.1rem;
1369
+ line-height: 1;
1370
+ }
1371
+ kbd {
1372
+ border: 1px solid var(--border);
1373
+ border-radius: 4px;
1374
+ padding: 0 0.3rem;
1375
+ font-size: 11px;
1376
+ }
1377
+
1378
+ /* ---- compare commits ---- */
1379
+ .compare-panel {
1380
+ padding: 1rem 1.2rem 2rem;
1381
+ display: grid;
1382
+ gap: 1rem;
1383
+ align-content: start;
1384
+ }
1385
+ .compare-intro h2 {
1386
+ margin: 0 0 0.3rem;
1387
+ font-size: 1rem;
1388
+ }
1389
+ .sides {
1390
+ display: grid;
1391
+ grid-template-columns: 1fr auto 1fr;
1392
+ gap: 0.8rem;
1393
+ align-items: start;
1394
+ }
1395
+ .swap {
1396
+ align-self: center;
1397
+ }
1398
+ .swap button {
1399
+ font-size: 1.1rem;
1400
+ padding: 0.3rem 0.6rem;
1401
+ }
1402
+ .side-picker {
1403
+ border: 1px solid var(--border);
1404
+ border-radius: var(--radius-sm);
1405
+ padding: 0.7rem 0.9rem;
1406
+ background: var(--surface-2);
1407
+ display: grid;
1408
+ gap: 0.5rem;
1409
+ }
1410
+ .side-picker h3 {
1411
+ margin: 0;
1412
+ font-size: 0.9rem;
1413
+ }
1414
+ .field {
1415
+ display: grid;
1416
+ gap: 0.2rem;
1417
+ color: var(--text-2);
1418
+ }
1419
+ .field-row {
1420
+ display: flex;
1421
+ gap: 0.4rem;
1422
+ }
1423
+ .field input {
1424
+ flex: 1;
1425
+ min-width: 0;
1426
+ }
1427
+ .git-facts {
1428
+ margin: 0;
1429
+ display: grid;
1430
+ grid-template-columns: 5.5rem 1fr;
1431
+ gap: 0.15rem 0.6rem;
1432
+ }
1433
+ .git-facts dt {
1434
+ color: var(--text-muted);
1435
+ }
1436
+ .git-facts dd {
1437
+ margin: 0;
1438
+ overflow-wrap: anywhere;
1439
+ }
1440
+ .dropzone {
1441
+ display: flex;
1442
+ flex-wrap: wrap;
1443
+ gap: 0.6rem;
1444
+ align-items: center;
1445
+ border: 1.5px dashed var(--border);
1446
+ border-radius: var(--radius-sm);
1447
+ padding: 0.6rem;
1448
+ }
1449
+ .dropzone.dragging {
1450
+ border-color: var(--accent);
1451
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
1452
+ }
1453
+ .run-chips {
1454
+ list-style: none;
1455
+ margin: 0;
1456
+ padding: 0;
1457
+ display: grid;
1458
+ gap: 0.3rem;
1459
+ }
1460
+ .run-chips li {
1461
+ display: flex;
1462
+ gap: 0.6rem;
1463
+ align-items: center;
1464
+ background: var(--surface);
1465
+ border: 1px solid var(--border);
1466
+ border-radius: 6px;
1467
+ padding: 0.2rem 0.5rem;
1468
+ }
1469
+ .run-chips li .mono {
1470
+ overflow: hidden;
1471
+ text-overflow: ellipsis;
1472
+ white-space: nowrap;
1473
+ flex: 1;
1474
+ }
1475
+ .run-chips button {
1476
+ border: 0;
1477
+ background: none;
1478
+ padding: 0 0.3rem;
1479
+ font-size: 1rem;
1480
+ }
1481
+ .compare-toolbar {
1482
+ display: flex;
1483
+ align-items: center;
1484
+ gap: 0.6rem;
1485
+ flex-wrap: wrap;
1486
+ padding: 0.5rem 0.7rem;
1487
+ border: 1px solid var(--border);
1488
+ border-radius: var(--radius-sm);
1489
+ position: sticky;
1490
+ top: 0;
1491
+ background: var(--surface);
1492
+ z-index: 5;
1493
+ }
1494
+ .pdf-buttons {
1495
+ display: inline-flex;
1496
+ gap: 0.5rem;
1497
+ align-items: center;
1498
+ }
1499
+ .compare-results {
1500
+ display: grid;
1501
+ gap: 1.2rem;
1502
+ }
1503
+ .compare-results h2 {
1504
+ font-size: 0.95rem;
1505
+ margin: 0 0 0.5rem;
1506
+ }
1507
+ .verdict-banner {
1508
+ border: 1.5px solid var(--c);
1509
+ border-left-width: 6px;
1510
+ border-radius: var(--radius-sm);
1511
+ padding: 0.7rem 1rem;
1512
+ }
1513
+ .verdict-banner .big {
1514
+ font-size: 1.4rem;
1515
+ font-weight: 700;
1516
+ color: var(--c);
1517
+ }
1518
+ .verdict-banner p {
1519
+ margin: 0.25rem 0;
1520
+ }
1521
+ .verdict-banner .counts span {
1522
+ margin-right: 1.2rem;
1523
+ font-weight: 600;
1524
+ }
1525
+ .good {
1526
+ --c: var(--good);
1527
+ }
1528
+ .bad {
1529
+ --c: var(--poor);
1530
+ }
1531
+ .warn {
1532
+ --c: var(--warn);
1533
+ }
1534
+ .same {
1535
+ --c: var(--text-2);
1536
+ }
1537
+ .na {
1538
+ --c: var(--text-muted);
1539
+ }
1540
+ .info {
1541
+ --c: var(--accent);
1542
+ }
1543
+ .verdict-banner .counts .good,
1544
+ .verdict-banner .counts .bad,
1545
+ .verdict-banner .counts .same,
1546
+ .verdict {
1547
+ color: var(--c);
1548
+ }
1549
+ .verdict {
1550
+ font-weight: 700;
1551
+ white-space: nowrap;
1552
+ }
1553
+ ul.icons {
1554
+ list-style: none;
1555
+ margin: 0;
1556
+ padding: 0;
1557
+ display: grid;
1558
+ gap: 0.25rem;
1559
+ }
1560
+ ul.icons li {
1561
+ color: var(--text);
1562
+ }
1563
+ ul.icons li .i,
1564
+ ul.checks li .i {
1565
+ display: inline-block;
1566
+ width: 1.4rem;
1567
+ font-weight: 700;
1568
+ color: var(--c);
1569
+ }
1570
+ ul.checks {
1571
+ list-style: none;
1572
+ margin: 0;
1573
+ padding: 0;
1574
+ display: grid;
1575
+ gap: 0.25rem;
1576
+ }
1577
+ ul.checks li {
1578
+ display: flex;
1579
+ align-items: baseline;
1580
+ }
1581
+ .compare-table td,
1582
+ .compare-table th {
1583
+ white-space: nowrap;
1584
+ }
1585
+ .compare-table td:first-child {
1586
+ white-space: normal;
1587
+ }
1588
+ .compare-table tr.dim td {
1589
+ color: var(--text-muted);
1590
+ }
1591
+ tr.group-row td {
1592
+ background: var(--surface-2);
1593
+ font-weight: 700;
1594
+ text-transform: uppercase;
1595
+ font-size: 11px;
1596
+ letter-spacing: 0.05em;
1597
+ color: var(--text-2);
1598
+ }
1599
+ .small {
1600
+ font-size: 11px;
1601
+ }
1602
+ svg.change-bar {
1603
+ display: block;
1604
+ }
1605
+ svg.change-bar .axis {
1606
+ stroke: var(--text-2);
1607
+ stroke-width: 1;
1608
+ }
1609
+ svg.change-bar rect {
1610
+ fill: var(--c);
1611
+ }
1612
+ .compare-results .segmented {
1613
+ margin-bottom: 0.5rem;
1614
+ }
1615
+ .detail-pane {
1616
+ padding-top: 0.6rem;
1617
+ display: grid;
1618
+ gap: 0.6rem;
1619
+ }
1620
+ .entity-group {
1621
+ border: 1px solid var(--border);
1622
+ border-left: 4px solid var(--c, var(--border));
1623
+ border-radius: 6px;
1624
+ padding: 0.3rem 0.7rem;
1625
+ }
1626
+ .entity-group summary {
1627
+ cursor: pointer;
1628
+ font-weight: 600;
1629
+ padding: 0.2rem 0;
1630
+ }
1631
+ .entity-name {
1632
+ width: 40%;
1633
+ }
1634
+ .entity-name > * {
1635
+ margin-right: 0.4rem;
1636
+ }
1637
+ .finding-cards {
1638
+ list-style: none;
1639
+ margin: 0 0 0.6rem;
1640
+ padding: 0;
1641
+ display: grid;
1642
+ gap: 0.5rem;
1643
+ }
1644
+ .finding-card {
1645
+ border: 1px solid var(--border);
1646
+ border-left: 4px solid var(--c);
1647
+ border-radius: 6px;
1648
+ padding: 0.5rem 0.8rem;
1649
+ }
1650
+ .finding-card h4 {
1651
+ margin: 0.2rem 0;
1652
+ }
1653
+ .finding-card .fix {
1654
+ margin: 0.3rem 0 0;
1655
+ }
1656
+ .sev {
1657
+ font-weight: 700;
1658
+ color: var(--c);
1659
+ }
1660
+ .trace-note.snapshot {
1661
+ width: 28rem;
1662
+ display: grid;
1663
+ gap: 0.5rem;
1664
+ }
1665
+ .trace-note.snapshot p {
1666
+ margin: 0;
1667
+ }
1668
+ @media (max-width: 1100px) {
1669
+ .sides {
1670
+ grid-template-columns: 1fr;
1671
+ }
1672
+ .swap {
1673
+ justify-self: center;
1674
+ }
1675
+ }
1676
+
1677
+ /* ---- page map ---- */
1678
+ .page-map {
1679
+ display: grid;
1680
+ grid-template-columns: minmax(0, 1fr) 20rem;
1681
+ gap: 1rem;
1682
+ padding: 1rem 1.2rem;
1683
+ align-items: start;
1684
+ }
1685
+ .page-caption {
1686
+ margin: 0 0 0.6rem;
1687
+ }
1688
+ .page-frame {
1689
+ position: relative;
1690
+ width: 100%;
1691
+ }
1692
+ .page-frame img {
1693
+ position: absolute;
1694
+ top: 0;
1695
+ height: 100%;
1696
+ display: block;
1697
+ border: 1px solid var(--border);
1698
+ border-radius: var(--radius-sm);
1699
+ box-shadow: var(--shadow);
1700
+ background: var(--surface);
1701
+ box-sizing: border-box;
1702
+ }
1703
+ .callout-svg {
1704
+ position: absolute;
1705
+ inset: 0;
1706
+ width: 100%;
1707
+ height: 100%;
1708
+ pointer-events: none;
1709
+ }
1710
+ .callout {
1711
+ --pin: var(--heat);
1712
+ }
1713
+ .callout.sev-warn {
1714
+ --pin: var(--warn);
1715
+ }
1716
+ .callout.sev-mild {
1717
+ --pin: var(--accent);
1718
+ }
1719
+ .callout rect {
1720
+ fill: none;
1721
+ stroke: var(--pin);
1722
+ stroke-width: 1;
1723
+ stroke-opacity: 0.4;
1724
+ vector-effect: non-scaling-stroke;
1725
+ }
1726
+ .callout line {
1727
+ stroke: var(--pin);
1728
+ stroke-width: 1;
1729
+ stroke-opacity: 0.5;
1730
+ vector-effect: non-scaling-stroke;
1731
+ }
1732
+ .callout circle {
1733
+ fill: var(--pin);
1734
+ fill-opacity: 0.8;
1735
+ }
1736
+ .callout.active rect {
1737
+ fill: var(--pin);
1738
+ fill-opacity: 0.16;
1739
+ stroke-opacity: 1;
1740
+ stroke-width: 2;
1741
+ }
1742
+ .callout.active line {
1743
+ stroke-opacity: 1;
1744
+ stroke-width: 2;
1745
+ }
1746
+ .callout.active circle {
1747
+ fill-opacity: 1;
1748
+ }
1749
+ button.marker {
1750
+ --pin: var(--heat);
1751
+ position: absolute;
1752
+ z-index: 5;
1753
+ display: grid;
1754
+ place-items: center;
1755
+ width: 1.7rem;
1756
+ height: 1.7rem;
1757
+ min-height: 0;
1758
+ padding: 0;
1759
+ transform: translate(-50%, -50%);
1760
+ border: 2px solid var(--panel-solid);
1761
+ border-radius: 50%;
1762
+ background: var(--pin);
1763
+ color: #fff;
1764
+ font-size: 11px;
1765
+ font-weight: 700;
1766
+ box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);
1767
+ }
1768
+ button.marker.sev-warn {
1769
+ --pin: var(--warn);
1770
+ }
1771
+ button.marker.sev-mild {
1772
+ --pin: var(--accent-strong);
1773
+ }
1774
+ button.marker:hover:not(:disabled),
1775
+ button.marker.active {
1776
+ background: var(--pin);
1777
+ border-color: var(--panel-solid);
1778
+ transform: translate(-50%, -50%) scale(1.2);
1779
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--pin) 40%, transparent);
1780
+ }
1781
+ .pin-num {
1782
+ display: inline-grid;
1783
+ place-items: center;
1784
+ min-width: 1.4rem;
1785
+ height: 1.4rem;
1786
+ padding: 0 0.3rem;
1787
+ border-radius: 999px;
1788
+ font-size: 11px;
1789
+ font-weight: 700;
1790
+ color: #fff;
1791
+ background: var(--accent-strong);
1792
+ }
1793
+ .pin-card {
1794
+ position: absolute;
1795
+ z-index: 20;
1796
+ width: 20rem;
1797
+ max-width: 90%;
1798
+ margin: 0.5rem 0;
1799
+ padding: 0.8rem 0.9rem;
1800
+ pointer-events: none;
1801
+ background: color-mix(in srgb, var(--panel-solid) 98%, transparent);
1802
+ border: 1px solid var(--panel-border);
1803
+ border-radius: var(--radius-sm);
1804
+ box-shadow: var(--shadow-pop);
1805
+ color: var(--text);
1806
+ }
1807
+ .pin-card-title {
1808
+ display: flex;
1809
+ align-items: center;
1810
+ gap: 0.5rem;
1811
+ margin-bottom: 0.2rem;
1812
+ }
1813
+ .pin-file {
1814
+ display: block;
1815
+ color: var(--text-muted);
1816
+ overflow-wrap: anywhere;
1817
+ margin-bottom: 0.5rem;
1818
+ }
1819
+ .pin-stats {
1820
+ display: grid;
1821
+ grid-template-columns: repeat(4, 1fr);
1822
+ gap: 0.4rem;
1823
+ margin: 0 0 0.5rem;
1824
+ }
1825
+ .pin-stats dt {
1826
+ font-size: 10px;
1827
+ text-transform: uppercase;
1828
+ letter-spacing: 0.06em;
1829
+ color: var(--text-muted);
1830
+ }
1831
+ .pin-stats dd {
1832
+ margin: 0;
1833
+ font-weight: 650;
1834
+ font-variant-numeric: tabular-nums;
1835
+ }
1836
+ .pin-card p {
1837
+ margin: 0.3rem 0 0;
1838
+ color: var(--text-2);
1839
+ }
1840
+ .pin-card .pin-fix {
1841
+ color: var(--text);
1842
+ }
1843
+ .page-list h2 {
1844
+ margin: 0 0 0.5rem;
1845
+ font-size: 0.9rem;
1846
+ }
1847
+ .page-list ol {
1848
+ list-style: none;
1849
+ margin: 0;
1850
+ padding: 0;
1851
+ display: grid;
1852
+ gap: 0.3rem;
1853
+ }
1854
+ button.hotspot {
1855
+ width: 100%;
1856
+ justify-content: flex-start;
1857
+ text-align: left;
1858
+ gap: 0.6rem;
1859
+ padding: 0.4rem 0.6rem;
1860
+ background: var(--surface-2);
1861
+ white-space: normal;
1862
+ }
1863
+ button.hotspot.active {
1864
+ border-color: var(--accent);
1865
+ background: var(--accent-soft);
1866
+ }
1867
+ .hotspot-main {
1868
+ display: grid;
1869
+ min-width: 0;
1870
+ flex: 1;
1871
+ }
1872
+ .hotspot-main .mono {
1873
+ overflow: hidden;
1874
+ text-overflow: ellipsis;
1875
+ white-space: nowrap;
1876
+ font-size: 11px;
1877
+ }
1878
+ .hotspot-time {
1879
+ font-variant-numeric: tabular-nums;
1880
+ font-weight: 600;
1881
+ color: var(--text-2);
1882
+ }
1883
+ @media (max-width: 1100px) {
1884
+ .page-map {
1885
+ grid-template-columns: 1fr;
1886
+ }
1887
+ }
1888
+
1889
+ .visually-hidden {
1890
+ position: absolute;
1891
+ width: 1px;
1892
+ height: 1px;
1893
+ overflow: hidden;
1894
+ clip: rect(0 0 0 0);
1895
+ white-space: nowrap;
1896
+ }
1897
+ .badge-sev {
1898
+ display: inline-flex;
1899
+ align-items: center;
1900
+ gap: 0.3rem;
1901
+ font-weight: 600;
1902
+ font-size: 12px;
1903
+ }
1904
+
1905
+ /* ---- top culprits sphere ---- */
1906
+ .culprits-hero {
1907
+ display: grid;
1908
+ grid-template-columns: minmax(18rem, 34rem) minmax(0, 1fr);
1909
+ gap: 1.4rem;
1910
+ align-items: start;
1911
+ }
1912
+ .hero-sphere h2,
1913
+ .hero-detail h2 {
1914
+ margin: 0 0 0.3rem;
1915
+ }
1916
+ .sphere {
1917
+ position: relative;
1918
+ width: 100%;
1919
+ aspect-ratio: 1;
1920
+ max-width: 34rem;
1921
+ margin: 0 auto;
1922
+ cursor: grab;
1923
+ touch-action: none;
1924
+ user-select: none;
1925
+ border-radius: 50%;
1926
+ }
1927
+ .sphere:active {
1928
+ cursor: grabbing;
1929
+ }
1930
+ .sphere-glow {
1931
+ position: absolute;
1932
+ inset: 12%;
1933
+ border-radius: 50%;
1934
+ background: radial-gradient(
1935
+ circle at 35% 30%,
1936
+ color-mix(in srgb, var(--accent) 26%, transparent),
1937
+ color-mix(in srgb, var(--accent) 6%, transparent) 55%,
1938
+ transparent 72%
1939
+ );
1940
+ box-shadow:
1941
+ inset 0 0 40px color-mix(in srgb, var(--accent) 18%, transparent),
1942
+ 0 0 60px color-mix(in srgb, var(--accent) 14%, transparent);
1943
+ }
1944
+ .sphere-wire {
1945
+ position: absolute;
1946
+ inset: 0;
1947
+ width: 100%;
1948
+ height: 100%;
1949
+ overflow: visible;
1950
+ }
1951
+ .sphere-wire path {
1952
+ fill: none;
1953
+ stroke: var(--accent);
1954
+ stroke-opacity: 0.22;
1955
+ stroke-width: 0.006;
1956
+ }
1957
+ button.orb {
1958
+ --orb: var(--accent);
1959
+ position: absolute;
1960
+ display: grid;
1961
+ gap: 0;
1962
+ justify-items: center;
1963
+ min-height: 0;
1964
+ padding: 0.2rem 0.6rem;
1965
+ border-radius: 999px;
1966
+ background: color-mix(in srgb, var(--panel-solid) 78%, transparent);
1967
+ border: 1px solid color-mix(in srgb, var(--orb) 55%, transparent);
1968
+ box-shadow: 0 2px 10px rgb(0 0 0 / 0.12);
1969
+ line-height: 1.2;
1970
+ will-change: transform, opacity;
1971
+ }
1972
+ button.orb.sev-critical {
1973
+ --orb: var(--poor);
1974
+ }
1975
+ button.orb.sev-warning {
1976
+ --orb: var(--warn);
1977
+ }
1978
+ button.orb .orb-name {
1979
+ font-weight: 650;
1980
+ color: var(--text);
1981
+ }
1982
+ button.orb .orb-ms {
1983
+ font-size: 0.78em;
1984
+ color: var(--orb);
1985
+ font-variant-numeric: tabular-nums;
1986
+ font-weight: 600;
1987
+ }
1988
+ button.orb:hover:not(:disabled),
1989
+ button.orb.active {
1990
+ background: color-mix(in srgb, var(--orb) 18%, var(--panel-solid));
1991
+ border-color: var(--orb);
1992
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--orb) 30%, transparent);
1993
+ }
1994
+ .insight {
1995
+ display: grid;
1996
+ gap: 0.35rem;
1997
+ padding: 0.8rem 1rem;
1998
+ border: 1px solid var(--border);
1999
+ border-left: 4px solid var(--accent);
2000
+ border-radius: var(--radius-sm);
2001
+ background: var(--surface-2);
2002
+ }
2003
+ .insight.sev-critical {
2004
+ border-left-color: var(--poor);
2005
+ }
2006
+ .insight.sev-warning {
2007
+ border-left-color: var(--warn);
2008
+ }
2009
+ .insight.sev-critical .badge-sev {
2010
+ color: var(--poor);
2011
+ }
2012
+ .insight.sev-warning .badge-sev {
2013
+ color: var(--warn);
2014
+ }
2015
+ .insight.sev-info .badge-sev {
2016
+ color: var(--accent);
2017
+ }
2018
+ .insight-head {
2019
+ display: flex;
2020
+ align-items: baseline;
2021
+ gap: 0.6rem;
2022
+ flex-wrap: wrap;
2023
+ }
2024
+ .insight-name {
2025
+ font-family: var(--font-mono);
2026
+ font-size: 0.95rem;
2027
+ }
2028
+ .insight-ms {
2029
+ margin-left: auto;
2030
+ font-weight: 700;
2031
+ font-variant-numeric: tabular-nums;
2032
+ }
2033
+ .insight p {
2034
+ margin: 0;
2035
+ }
2036
+ .insight button {
2037
+ justify-self: start;
2038
+ }
2039
+ .rank-list {
2040
+ list-style: none;
2041
+ margin: 0.8rem 0 0;
2042
+ padding: 0;
2043
+ display: grid;
2044
+ gap: 2px;
2045
+ }
2046
+ button.rank-row {
2047
+ --orb: var(--accent);
2048
+ width: 100%;
2049
+ display: grid;
2050
+ grid-template-columns: 1.4rem minmax(0, 1fr) 7rem 4.6rem;
2051
+ align-items: center;
2052
+ gap: 0.6rem;
2053
+ min-height: 1.9rem;
2054
+ padding: 0.15rem 0.5rem;
2055
+ background: transparent;
2056
+ border-color: transparent;
2057
+ border-radius: var(--radius-sm);
2058
+ text-align: left;
2059
+ font-weight: 400;
2060
+ }
2061
+ button.rank-row.sev-critical {
2062
+ --orb: var(--poor);
2063
+ }
2064
+ button.rank-row.sev-warning {
2065
+ --orb: var(--warn);
2066
+ }
2067
+ button.rank-row.active {
2068
+ background: var(--accent-soft);
2069
+ border-color: color-mix(in srgb, var(--accent) 40%, transparent);
2070
+ }
2071
+ .rank-n {
2072
+ color: var(--text-muted);
2073
+ font-variant-numeric: tabular-nums;
2074
+ }
2075
+ .rank-name {
2076
+ font-family: var(--font-mono);
2077
+ font-size: 12px;
2078
+ overflow: hidden;
2079
+ text-overflow: ellipsis;
2080
+ white-space: nowrap;
2081
+ }
2082
+ .rank-bar {
2083
+ height: 6px;
2084
+ border-radius: 3px;
2085
+ background: var(--grid);
2086
+ overflow: hidden;
2087
+ }
2088
+ .rank-bar span {
2089
+ display: block;
2090
+ height: 100%;
2091
+ border-radius: 3px;
2092
+ background: var(--orb);
2093
+ }
2094
+ .rank-ms {
2095
+ text-align: right;
2096
+ font-variant-numeric: tabular-nums;
2097
+ color: var(--text-2);
2098
+ }
2099
+ @media (max-width: 1000px) {
2100
+ .culprits-hero {
2101
+ grid-template-columns: 1fr;
2102
+ }
2103
+ }
2104
+
2105
+ /* ---- vitals ---- */
2106
+ .vitals-block {
2107
+ display: grid;
2108
+ gap: 0.7rem;
2109
+ }
2110
+ .vitals-hero {
2111
+ display: grid;
2112
+ grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
2113
+ gap: 0.7rem;
2114
+ }
2115
+ .vital-hero {
2116
+ display: grid;
2117
+ gap: 0.15rem;
2118
+ padding: 0.8rem 1rem;
2119
+ border: 1px solid var(--border);
2120
+ border-left-width: 4px;
2121
+ border-radius: var(--radius-sm);
2122
+ background: var(--surface-2);
2123
+ }
2124
+ .vital-hero .vital-label {
2125
+ color: var(--text-2);
2126
+ }
2127
+ .vital-hero .vital-value {
2128
+ font-size: 1.9rem;
2129
+ line-height: 1.15;
2130
+ font-weight: 650;
2131
+ letter-spacing: -0.02em;
2132
+ font-variant-numeric: tabular-nums;
2133
+ }
2134
+ .vital-hero .vital-rating {
2135
+ font-weight: 600;
2136
+ font-size: 12px;
2137
+ }
2138
+ .vital-target {
2139
+ font-size: 11px;
2140
+ }
2141
+ .rating-good {
2142
+ --rate: var(--good);
2143
+ border-left-color: var(--good);
2144
+ }
2145
+ .rating-warn {
2146
+ --rate: var(--warn);
2147
+ border-left-color: var(--warn);
2148
+ }
2149
+ .rating-poor {
2150
+ --rate: var(--poor);
2151
+ border-left-color: var(--poor);
2152
+ }
2153
+ .rating-none {
2154
+ --rate: var(--text-muted);
2155
+ border-left-color: var(--border);
2156
+ }
2157
+ .vital-rating {
2158
+ color: var(--rate);
2159
+ }
2160
+ .gauge {
2161
+ position: relative;
2162
+ height: 6px;
2163
+ margin: 0.45rem 0 0.2rem;
2164
+ border-radius: 3px;
2165
+ background: linear-gradient(
2166
+ 90deg,
2167
+ color-mix(in srgb, var(--good) 45%, transparent) 0 var(--g),
2168
+ color-mix(in srgb, var(--warn) 45%, transparent) var(--g) var(--p),
2169
+ color-mix(in srgb, var(--poor) 45%, transparent) var(--p) 100%
2170
+ );
2171
+ }
2172
+ .gauge-needle {
2173
+ position: absolute;
2174
+ top: -3px;
2175
+ width: 4px;
2176
+ height: 12px;
2177
+ margin-left: -2px;
2178
+ border-radius: 2px;
2179
+ background: var(--rate);
2180
+ box-shadow: 0 0 0 2px var(--panel-solid);
2181
+ }
2182
+ .vitals-secondary {
2183
+ display: grid;
2184
+ grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
2185
+ gap: 0.4rem;
2186
+ }
2187
+ .vital-chip {
2188
+ display: flex;
2189
+ align-items: center;
2190
+ gap: 0.5rem;
2191
+ padding: 0.35rem 0.7rem;
2192
+ border: 1px solid var(--border);
2193
+ border-radius: var(--radius-sm);
2194
+ background: var(--surface-2);
2195
+ border-left-width: 1px;
2196
+ }
2197
+ .vital-chip-icon {
2198
+ display: grid;
2199
+ place-items: center;
2200
+ width: 1.1rem;
2201
+ height: 1.1rem;
2202
+ border-radius: 50%;
2203
+ font-size: 10px;
2204
+ font-weight: 700;
2205
+ color: var(--panel-solid);
2206
+ background: var(--rate);
2207
+ }
2208
+ .vital-chip-label {
2209
+ flex: 1;
2210
+ color: var(--text-2);
2211
+ }
2212
+ .vital-chip b {
2213
+ font-variant-numeric: tabular-nums;
2214
+ }
2215
+
2216
+ /* ---- findings list ---- */
2217
+ .findings-toolbar {
2218
+ display: flex;
2219
+ align-items: center;
2220
+ gap: 0.5rem;
2221
+ flex-wrap: wrap;
2222
+ margin-bottom: 0.6rem;
2223
+ }
2224
+ .frows,
2225
+ .members {
2226
+ list-style: none;
2227
+ margin: 0;
2228
+ padding: 0;
2229
+ display: grid;
2230
+ gap: 0.4rem;
2231
+ }
2232
+ .frow {
2233
+ --sev: var(--accent);
2234
+ border: 1px solid var(--border);
2235
+ border-left: 4px solid var(--sev);
2236
+ border-radius: var(--radius-sm);
2237
+ background: var(--surface-2);
2238
+ overflow: hidden;
2239
+ }
2240
+ .frow.sev-critical {
2241
+ --sev: var(--poor);
2242
+ }
2243
+ .frow.sev-warning {
2244
+ --sev: var(--warn);
2245
+ }
2246
+ .frow.open {
2247
+ border-color: color-mix(in srgb, var(--sev) 45%, var(--border));
2248
+ }
2249
+ .frow.group {
2250
+ background: color-mix(in srgb, var(--sev) 6%, var(--surface-2));
2251
+ }
2252
+ button.frow-head {
2253
+ width: 100%;
2254
+ display: grid;
2255
+ grid-template-columns: 1.5rem minmax(0, 1fr) auto 9.5rem 1rem;
2256
+ align-items: center;
2257
+ gap: 0.7rem;
2258
+ padding: 0.55rem 0.8rem;
2259
+ border: 0;
2260
+ border-radius: 0;
2261
+ background: transparent;
2262
+ text-align: left;
2263
+ font-weight: 400;
2264
+ white-space: normal;
2265
+ }
2266
+ button.frow-head:hover:not(:disabled) {
2267
+ background: var(--control);
2268
+ border: 0;
2269
+ }
2270
+ .sev-badge {
2271
+ display: grid;
2272
+ place-items: center;
2273
+ width: 1.4rem;
2274
+ height: 1.4rem;
2275
+ border-radius: 50%;
2276
+ font-size: 11px;
2277
+ font-weight: 700;
2278
+ color: var(--panel-solid);
2279
+ background: var(--sev);
2280
+ }
2281
+ .frow-main {
2282
+ display: grid;
2283
+ min-width: 0;
2284
+ }
2285
+ .frow-title {
2286
+ font-weight: 600;
2287
+ overflow: hidden;
2288
+ text-overflow: ellipsis;
2289
+ white-space: nowrap;
2290
+ }
2291
+ .frow.open > .frow-head .frow-title {
2292
+ white-space: normal;
2293
+ }
2294
+ .frow-sub {
2295
+ font-family: var(--font-mono);
2296
+ font-size: 11px;
2297
+ }
2298
+ .frow-chips {
2299
+ display: flex;
2300
+ gap: 0.25rem;
2301
+ }
2302
+ .impact {
2303
+ display: grid;
2304
+ grid-template-columns: 1fr auto;
2305
+ align-items: center;
2306
+ gap: 0.5rem;
2307
+ }
2308
+ .impact-bar {
2309
+ height: 6px;
2310
+ border-radius: 3px;
2311
+ background: var(--grid);
2312
+ overflow: hidden;
2313
+ }
2314
+ .impact-bar span {
2315
+ display: block;
2316
+ height: 100%;
2317
+ background: var(--sev);
2318
+ border-radius: 3px;
2319
+ }
2320
+ .impact-ms {
2321
+ min-width: 3.4rem;
2322
+ text-align: right;
2323
+ font-variant-numeric: tabular-nums;
2324
+ font-weight: 600;
2325
+ }
2326
+ .fbody {
2327
+ display: grid;
2328
+ gap: 0.8rem;
2329
+ padding: 0.2rem 1rem 1rem 3.1rem;
2330
+ }
2331
+ .fix-callout {
2332
+ padding: 0.6rem 0.9rem;
2333
+ border-radius: var(--radius-sm);
2334
+ background: var(--accent-soft);
2335
+ border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
2336
+ }
2337
+ .fix-callout p {
2338
+ margin: 0.15rem 0 0;
2339
+ }
2340
+ .fix-label {
2341
+ font-size: 0.7rem;
2342
+ font-weight: 700;
2343
+ text-transform: uppercase;
2344
+ letter-spacing: 0.08em;
2345
+ color: var(--accent);
2346
+ }
2347
+ .fdetail {
2348
+ margin: 0;
2349
+ color: var(--text-2);
2350
+ }
2351
+ .fdetail b {
2352
+ display: block;
2353
+ font-size: 0.7rem;
2354
+ text-transform: uppercase;
2355
+ letter-spacing: 0.08em;
2356
+ color: var(--text-muted);
2357
+ }
2358
+ .culprit-table th {
2359
+ position: static;
2360
+ background: transparent;
2361
+ }
2362
+ .culprit-table td {
2363
+ vertical-align: middle;
2364
+ }
2365
+ .members-title {
2366
+ margin: 0.2rem 0 0;
2367
+ font-size: 0.7rem;
2368
+ text-transform: uppercase;
2369
+ letter-spacing: 0.08em;
2370
+ color: var(--text-muted);
2371
+ }
2372
+ .members .frow {
2373
+ background: var(--surface);
2374
+ }
2375
+ @media (max-width: 900px) {
2376
+ button.frow-head {
2377
+ grid-template-columns: 1.5rem minmax(0, 1fr) 1rem;
2378
+ }
2379
+ .frow-chips,
2380
+ .impact {
2381
+ display: none;
2382
+ }
2383
+ .fbody {
2384
+ padding-left: 1rem;
2385
+ }
2386
+ }
2387
+
2388
+ /* ---- golden-path evaluation matrix ---- */
2389
+ .eval-panel {
2390
+ padding: 16px 20px 28px;
2391
+ overflow: auto;
2392
+ }
2393
+ .eval-head {
2394
+ display: flex;
2395
+ flex-wrap: wrap;
2396
+ align-items: center;
2397
+ gap: 12px;
2398
+ margin-bottom: 12px;
2399
+ }
2400
+ .eval-summary {
2401
+ display: inline-flex;
2402
+ flex-wrap: wrap;
2403
+ gap: 6px;
2404
+ }
2405
+ .eval-target {
2406
+ font-size: 13px;
2407
+ margin-bottom: 14px;
2408
+ color: var(--text);
2409
+ }
2410
+ .eval-table-wrap {
2411
+ overflow-x: auto;
2412
+ border: 1px solid var(--border);
2413
+ border-radius: var(--radius-sm);
2414
+ }
2415
+ table.eval-matrix {
2416
+ width: 100%;
2417
+ border-collapse: collapse;
2418
+ font-size: 13px;
2419
+ }
2420
+ table.eval-matrix th,
2421
+ table.eval-matrix td {
2422
+ text-align: left;
2423
+ padding: 9px 12px;
2424
+ border-bottom: 1px solid var(--border);
2425
+ vertical-align: top;
2426
+ }
2427
+ table.eval-matrix th {
2428
+ font: inherit;
2429
+ font-size: 11px;
2430
+ letter-spacing: var(--label-tracking);
2431
+ text-transform: var(--label-case);
2432
+ color: var(--text-muted);
2433
+ background: var(--surface);
2434
+ }
2435
+ table.eval-matrix td.num,
2436
+ table.eval-matrix th.num {
2437
+ text-align: right;
2438
+ font-family: var(--font-mono);
2439
+ white-space: nowrap;
2440
+ }
2441
+ table.eval-matrix tr:last-child td {
2442
+ border-bottom: none;
2443
+ }
2444
+ table.eval-matrix tr.row-fail td {
2445
+ background: color-mix(in srgb, var(--poor) 8%, transparent);
2446
+ }
2447
+ .eval-path-title {
2448
+ font-weight: 600;
2449
+ }
2450
+ .eval-path-error {
2451
+ margin-top: 3px;
2452
+ font-size: 12px;
2453
+ color: var(--poor);
2454
+ font-family: var(--font-mono);
2455
+ }
2456
+ .badge {
2457
+ display: inline-block;
2458
+ padding: 2px 8px;
2459
+ border-radius: 999px;
2460
+ font-size: 11px;
2461
+ font-weight: 700;
2462
+ letter-spacing: 0.04em;
2463
+ }
2464
+ .badge-pass {
2465
+ background: color-mix(in srgb, var(--good) 20%, transparent);
2466
+ color: var(--good);
2467
+ }
2468
+ .badge-fail {
2469
+ background: color-mix(in srgb, var(--poor) 20%, transparent);
2470
+ color: var(--poor);
2471
+ }
2472
+ .top-finding {
2473
+ font-size: 12px;
2474
+ }
2475
+ .eval-foot {
2476
+ margin-top: 12px;
2477
+ font-size: 12px;
2478
+ }