@polderlabs/bizar 5.1.0 → 5.3.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 (72) hide show
  1. package/bizar-dash/dist/assets/{MobileChat-BVojK0n5.js → MobileChat-OidrxXR9.js} +1 -1
  2. package/bizar-dash/dist/assets/{MobileChat-BVojK0n5.js.map → MobileChat-OidrxXR9.js.map} +1 -1
  3. package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js → MobileSettings-B91gVFii.js} +1 -1
  4. package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js.map → MobileSettings-B91gVFii.js.map} +1 -1
  5. package/bizar-dash/dist/assets/{icons-Clz0NR6Y.js → icons-Bd8piSb2.js} +159 -134
  6. package/bizar-dash/dist/assets/icons-Bd8piSb2.js.map +1 -0
  7. package/bizar-dash/dist/assets/{main-DTkNlLrw.css → main-3G6mOhiK.css} +1 -1
  8. package/bizar-dash/dist/assets/main-6YUZY968.js +19 -0
  9. package/bizar-dash/dist/assets/main-6YUZY968.js.map +1 -0
  10. package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js → mobile-CSsp30lr.js} +2 -2
  11. package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js.map → mobile-CSsp30lr.js.map} +1 -1
  12. package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js → mobile-DtwK7DOV.js} +1 -1
  13. package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js.map → mobile-DtwK7DOV.js.map} +1 -1
  14. package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js → useSlashCommands-DEkxEvOO.js} +2 -2
  15. package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js.map → useSlashCommands-DEkxEvOO.js.map} +1 -1
  16. package/bizar-dash/dist/index.html +5 -5
  17. package/bizar-dash/dist/mobile.html +3 -3
  18. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  19. package/bizar-dash/src/server/api.mjs +4 -0
  20. package/bizar-dash/src/server/eval-store.mjs +55 -0
  21. package/bizar-dash/src/server/eval.mjs +63 -0
  22. package/bizar-dash/src/server/plugins/permission-audit.mjs +109 -0
  23. package/bizar-dash/src/server/plugins/sandbox.mjs +76 -2
  24. package/bizar-dash/src/server/plugins/store.mjs +4 -0
  25. package/bizar-dash/src/server/routes/eval.mjs +42 -2
  26. package/bizar-dash/src/server/routes/plugins.mjs +55 -7
  27. package/bizar-dash/src/server/routes/tailscale.mjs +46 -0
  28. package/bizar-dash/src/server/routes/voice.mjs +42 -6
  29. package/bizar-dash/src/server/schedules-runner.mjs +61 -0
  30. package/bizar-dash/src/server/server.mjs +13 -0
  31. package/bizar-dash/src/server/voice-store.mjs +27 -0
  32. package/bizar-dash/src/server/workers/transcription-worker.mjs +213 -0
  33. package/bizar-dash/src/web/App.tsx +9 -0
  34. package/bizar-dash/src/web/components/EvalDiff.tsx +157 -0
  35. package/bizar-dash/src/web/components/EvalExportButton.tsx +27 -0
  36. package/bizar-dash/src/web/components/EvalRunCard.tsx +78 -0
  37. package/bizar-dash/src/web/components/InstallConfirmDialog.tsx +62 -0
  38. package/bizar-dash/src/web/components/MarketplacePluginCard.tsx +51 -0
  39. package/bizar-dash/src/web/components/PluginCard.tsx +64 -0
  40. package/bizar-dash/src/web/components/PluginPermissions.tsx +36 -0
  41. package/bizar-dash/src/web/components/ScheduleTemplateCard.tsx +1 -0
  42. package/bizar-dash/src/web/components/TailscaleSettings.tsx +161 -0
  43. package/bizar-dash/src/web/components/Toggle.tsx +31 -0
  44. package/bizar-dash/src/web/components/Topbar.tsx +9 -0
  45. package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +28 -0
  46. package/bizar-dash/src/web/styles/main.css +684 -0
  47. package/bizar-dash/src/web/views/Doctor.tsx +3 -3
  48. package/bizar-dash/src/web/views/Eval.tsx +372 -0
  49. package/bizar-dash/src/web/views/EvalReport.tsx +353 -0
  50. package/bizar-dash/src/web/views/Marketplace.tsx +135 -0
  51. package/bizar-dash/src/web/views/Plugins.tsx +134 -0
  52. package/bizar-dash/src/web/views/Schedules.tsx +1 -1
  53. package/bizar-dash/src/web/views/Settings.tsx +2 -0
  54. package/bizar-dash/tests/a11y/components.test.tsx +149 -0
  55. package/bizar-dash/tests/a11y/forms.test.tsx +191 -0
  56. package/bizar-dash/tests/a11y/navigation.test.tsx +112 -0
  57. package/bizar-dash/tests/cli-tailscale.test.mjs +113 -0
  58. package/bizar-dash/tests/components/marketplace-plugin-card.test.tsx +144 -0
  59. package/bizar-dash/tests/components/plugin-permissions.test.tsx +95 -0
  60. package/bizar-dash/tests/eval-csv.test.mjs +67 -0
  61. package/bizar-dash/tests/eval-scheduled.test.mjs +84 -0
  62. package/bizar-dash/tests/eval-web-ui.test.tsx +220 -0
  63. package/bizar-dash/tests/plugins-permissions.test.mjs +525 -0
  64. package/bizar-dash/tests/voice-transcribe-worker.test.mjs +343 -0
  65. package/cli/bin.mjs +14 -0
  66. package/cli/commands/dash.mjs +19 -1
  67. package/cli/commands/service.mjs +7 -0
  68. package/cli/commands/tailscale.mjs +251 -0
  69. package/package.json +1 -1
  70. package/bizar-dash/dist/assets/icons-Clz0NR6Y.js.map +0 -1
  71. package/bizar-dash/dist/assets/main-8feQWXiF.js +0 -19
  72. package/bizar-dash/dist/assets/main-8feQWXiF.js.map +0 -1
@@ -9065,3 +9065,687 @@ select:focus-visible,
9065
9065
  color: var(--accent, #8b5cf6);
9066
9066
  margin-left: 6px;
9067
9067
  }
9068
+
9069
+ /* ─── Toggle switch ──────────────────────────────────────────────── */
9070
+ .toggle {
9071
+ position: relative;
9072
+ display: inline-flex;
9073
+ align-items: center;
9074
+ cursor: pointer;
9075
+ width: 40px;
9076
+ height: 22px;
9077
+ flex-shrink: 0;
9078
+ }
9079
+
9080
+ .toggle input {
9081
+ opacity: 0;
9082
+ width: 0;
9083
+ height: 0;
9084
+ position: absolute;
9085
+ }
9086
+
9087
+ .toggle-slider {
9088
+ position: absolute;
9089
+ inset: 0;
9090
+ background: var(--bg-elev-3);
9091
+ border-radius: 11px;
9092
+ transition: background 200ms ease;
9093
+ }
9094
+
9095
+ .toggle-slider::before {
9096
+ content: '';
9097
+ position: absolute;
9098
+ left: 2px;
9099
+ top: 2px;
9100
+ width: 18px;
9101
+ height: 18px;
9102
+ background: white;
9103
+ border-radius: 50%;
9104
+ transition: transform 200ms ease;
9105
+ }
9106
+
9107
+ .toggle input:checked + .toggle-slider {
9108
+ background: var(--accent);
9109
+ }
9110
+
9111
+ .toggle input:checked + .toggle-slider::before {
9112
+ transform: translateX(18px);
9113
+ }
9114
+
9115
+ .toggle input:focus-visible + .toggle-slider {
9116
+ outline: 2px solid var(--accent);
9117
+ outline-offset: 2px;
9118
+ }
9119
+
9120
+ /* ─── Plugins view ────────────────────────────────────────────────── */
9121
+ .view-plugins {
9122
+ max-width: 1400px;
9123
+ }
9124
+
9125
+ .plugins-toolbar {
9126
+ display: flex;
9127
+ align-items: center;
9128
+ gap: var(--space-3);
9129
+ margin-bottom: var(--space-4);
9130
+ }
9131
+
9132
+ .plugin-grid {
9133
+ display: grid;
9134
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
9135
+ gap: var(--space-4);
9136
+ }
9137
+
9138
+ .plugin-description {
9139
+ font-size: 13px;
9140
+ color: var(--text-dim);
9141
+ margin: 0;
9142
+ line-height: 1.5;
9143
+ }
9144
+
9145
+ .plugin-card-head {
9146
+ display: flex;
9147
+ align-items: flex-start;
9148
+ justify-content: space-between;
9149
+ gap: var(--space-3);
9150
+ }
9151
+
9152
+ .plugin-card-actions {
9153
+ display: flex;
9154
+ gap: var(--space-2);
9155
+ flex-wrap: wrap;
9156
+ margin-top: var(--space-2);
9157
+ }
9158
+
9159
+ .plugin-card-stats {
9160
+ font-size: 11px;
9161
+ color: var(--text-dim);
9162
+ margin-top: var(--space-2);
9163
+ }
9164
+
9165
+ /* ─── v5.3.0 — Plugin marketplace ──────────────────────────────── */
9166
+ .marketplace-grid {
9167
+ display: grid;
9168
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
9169
+ gap: 16px;
9170
+ margin-top: 24px;
9171
+ }
9172
+
9173
+ .marketplace-plugin-card {
9174
+ display: flex;
9175
+ flex-direction: column;
9176
+ }
9177
+
9178
+ .marketplace-plugin-head {
9179
+ display: flex;
9180
+ align-items: flex-start;
9181
+ justify-content: space-between;
9182
+ gap: var(--space-3);
9183
+ }
9184
+
9185
+ .marketplace-plugin-description {
9186
+ color: var(--text-dim);
9187
+ margin: var(--space-2) 0;
9188
+ font-size: 13px;
9189
+ line-height: 1.5;
9190
+ }
9191
+
9192
+ .marketplace-plugin-tags {
9193
+ display: flex;
9194
+ flex-wrap: wrap;
9195
+ gap: 4px;
9196
+ margin: 8px 0;
9197
+ }
9198
+
9199
+ .marketplace-plugin-tags .tag {
9200
+ background: var(--surface-3, rgba(255, 255, 255, 0.05));
9201
+ color: var(--text-dim, #8b949e);
9202
+ padding: 2px 6px;
9203
+ border-radius: 8px;
9204
+ font-size: 10px;
9205
+ }
9206
+
9207
+ .marketplace-plugin-actions {
9208
+ display: flex;
9209
+ gap: var(--space-2);
9210
+ flex-wrap: wrap;
9211
+ margin-top: auto;
9212
+ padding-top: var(--space-2);
9213
+ }
9214
+
9215
+ .marketplace-plugin-author {
9216
+ font-size: 12px;
9217
+ color: var(--text-dim);
9218
+ }
9219
+
9220
+ .marketplace-search-row {
9221
+ margin-bottom: 16px;
9222
+ }
9223
+
9224
+ .category-chips {
9225
+ display: flex;
9226
+ gap: 8px;
9227
+ margin: 16px 0;
9228
+ flex-wrap: wrap;
9229
+ }
9230
+
9231
+ .category-chips button {
9232
+ padding: 6px 12px;
9233
+ border: 1px solid var(--border, #30363d);
9234
+ border-radius: 14px;
9235
+ background: transparent;
9236
+ color: var(--text-dim);
9237
+ cursor: pointer;
9238
+ font-size: 13px;
9239
+ }
9240
+
9241
+ .category-chips button:hover {
9242
+ background: var(--surface-2);
9243
+ }
9244
+
9245
+ .category-chips button.active {
9246
+ background: var(--accent, #8b5cf6);
9247
+ color: white;
9248
+ border-color: var(--accent, #8b5cf6);
9249
+ }
9250
+
9251
+ .install-confirm h3 {
9252
+ margin: 0 0 var(--space-2);
9253
+ font-size: 16px;
9254
+ }
9255
+
9256
+ .install-warning {
9257
+ display: flex;
9258
+ gap: 8px;
9259
+ background: rgba(245, 158, 11, 0.1);
9260
+ padding: 12px;
9261
+ border-radius: 6px;
9262
+ margin: 12px 0;
9263
+ color: #d29922;
9264
+ font-size: 13px;
9265
+ }
9266
+
9267
+ .install-warning p {
9268
+ margin: 0;
9269
+ color: var(--text);
9270
+ }
9271
+
9272
+ .install-confirm-actions {
9273
+ display: flex;
9274
+ gap: var(--space-2);
9275
+ justify-content: flex-end;
9276
+ margin-top: var(--space-4);
9277
+ }
9278
+
9279
+ /* ─── Plugin permissions ──────────────────────────────────────────── */
9280
+ .plugin-permissions {
9281
+ display: flex;
9282
+ flex-wrap: wrap;
9283
+ gap: 6px;
9284
+ margin: var(--space-3) 0;
9285
+ }
9286
+
9287
+ .permission-chip {
9288
+ display: inline-flex;
9289
+ align-items: center;
9290
+ gap: 4px;
9291
+ padding: 3px 8px;
9292
+ border-radius: 12px;
9293
+ font-size: 11px;
9294
+ font-weight: 500;
9295
+ }
9296
+
9297
+ .permission-chip.is-blue {
9298
+ background: rgba(59, 130, 246, 0.15);
9299
+ color: #58a6ff;
9300
+ }
9301
+
9302
+ .permission-chip.is-yellow {
9303
+ background: rgba(245, 158, 11, 0.15);
9304
+ color: #d29922;
9305
+ }
9306
+
9307
+ .permission-chip.is-red {
9308
+ background: rgba(239, 68, 68, 0.15);
9309
+ color: #f85149;
9310
+ }
9311
+
9312
+ .permission-chip.is-gray {
9313
+ background: rgba(139, 148, 158, 0.15);
9314
+ color: #8b949e;
9315
+ }
9316
+
9317
+ /* ─── v5.2.0 — Eval framework web UI ─────────────────────────────────────── */
9318
+
9319
+ .eval-grid {
9320
+ display: grid;
9321
+ grid-template-columns: minmax(280px, 1fr) minmax(0, 2fr);
9322
+ gap: var(--space-6, 24px);
9323
+ margin-top: var(--space-6, 24px);
9324
+ align-items: flex-start;
9325
+ }
9326
+
9327
+ @media (max-width: 900px) {
9328
+ .eval-grid {
9329
+ grid-template-columns: minmax(0, 1fr);
9330
+ }
9331
+ }
9332
+
9333
+ .eval-runs-list {
9334
+ display: flex;
9335
+ flex-direction: column;
9336
+ gap: var(--space-3, 12px);
9337
+ min-width: 0;
9338
+ }
9339
+
9340
+ .eval-run-card-wrap {
9341
+ position: relative;
9342
+ display: flex;
9343
+ flex-direction: column;
9344
+ gap: 4px;
9345
+ }
9346
+
9347
+ .eval-run-card {
9348
+ cursor: pointer;
9349
+ }
9350
+
9351
+ .eval-run-card-selected {
9352
+ border-color: var(--accent, #8b5cf6);
9353
+ box-shadow: 0 0 0 1px var(--accent, #8b5cf6);
9354
+ }
9355
+
9356
+ .eval-run-head {
9357
+ display: flex;
9358
+ align-items: center;
9359
+ justify-content: space-between;
9360
+ gap: var(--space-3, 12px);
9361
+ min-width: 0;
9362
+ }
9363
+
9364
+ .eval-run-title {
9365
+ font-family: ui-monospace, 'JetBrains Mono', monospace;
9366
+ font-size: 13px;
9367
+ font-weight: 600;
9368
+ color: var(--text-strong);
9369
+ margin: 0;
9370
+ overflow: hidden;
9371
+ text-overflow: ellipsis;
9372
+ white-space: nowrap;
9373
+ min-width: 0;
9374
+ }
9375
+
9376
+ .eval-run-meta {
9377
+ font-size: 12px;
9378
+ color: var(--text-dim);
9379
+ }
9380
+
9381
+ .eval-status {
9382
+ display: inline-flex;
9383
+ align-items: center;
9384
+ padding: 4px 10px;
9385
+ border-radius: 12px;
9386
+ font-size: 12px;
9387
+ font-weight: 600;
9388
+ white-space: nowrap;
9389
+ flex-shrink: 0;
9390
+ }
9391
+
9392
+ .eval-status.is-pass { background: rgba(63, 185, 80, 0.15); color: #3fb950; }
9393
+ .eval-status.is-warn { background: rgba(210, 153, 34, 0.15); color: #d29922; }
9394
+ .eval-status.is-fail { background: rgba(239, 68, 68, 0.15); color: #f85149; }
9395
+
9396
+ .eval-baseline-btn {
9397
+ align-self: flex-end;
9398
+ display: inline-flex;
9399
+ align-items: center;
9400
+ gap: 4px;
9401
+ background: transparent;
9402
+ border: 1px solid var(--border);
9403
+ border-radius: 999px;
9404
+ padding: 2px 8px;
9405
+ font-size: 11px;
9406
+ color: var(--text-dim);
9407
+ cursor: pointer;
9408
+ transition: border-color var(--motion-base) var(--ease), color var(--motion-base) var(--ease);
9409
+ }
9410
+
9411
+ .eval-baseline-btn:hover {
9412
+ border-color: var(--accent, #8b5cf6);
9413
+ color: var(--accent, #8b5cf6);
9414
+ }
9415
+
9416
+ .eval-detail {
9417
+ min-width: 0;
9418
+ display: flex;
9419
+ flex-direction: column;
9420
+ gap: var(--space-4, 16px);
9421
+ }
9422
+
9423
+ .eval-detail-loading,
9424
+ .eval-runs-loading {
9425
+ display: inline-flex;
9426
+ align-items: center;
9427
+ gap: var(--space-2, 8px);
9428
+ padding: var(--space-4, 16px);
9429
+ color: var(--text-dim);
9430
+ }
9431
+
9432
+ .eval-detail-empty {
9433
+ padding: var(--space-6, 24px);
9434
+ }
9435
+
9436
+ .eval-run-detail {
9437
+ display: flex;
9438
+ flex-direction: column;
9439
+ gap: var(--space-4, 16px);
9440
+ }
9441
+
9442
+ .eval-run-detail-header-row {
9443
+ display: flex;
9444
+ align-items: center;
9445
+ justify-content: space-between;
9446
+ gap: var(--space-3, 12px);
9447
+ }
9448
+
9449
+ .eval-run-detail-summary {
9450
+ display: flex;
9451
+ align-items: center;
9452
+ gap: var(--space-3, 12px);
9453
+ flex-wrap: wrap;
9454
+ }
9455
+
9456
+ .eval-rate {
9457
+ font-weight: 600;
9458
+ }
9459
+
9460
+ .eval-rate.is-pass { color: #3fb950; }
9461
+ .eval-rate.is-fail { color: #f85149; }
9462
+
9463
+ .eval-fixtures-list {
9464
+ list-style: none;
9465
+ margin: 0;
9466
+ padding: 0;
9467
+ display: flex;
9468
+ flex-direction: column;
9469
+ gap: 4px;
9470
+ }
9471
+
9472
+ .eval-fixture-row {
9473
+ display: grid;
9474
+ grid-template-columns: 24px 1fr auto;
9475
+ align-items: center;
9476
+ gap: var(--space-3, 12px);
9477
+ padding: 6px 0;
9478
+ border-bottom: 1px solid var(--border);
9479
+ font-size: 13px;
9480
+ }
9481
+
9482
+ .eval-fixture-row:last-child {
9483
+ border-bottom: none;
9484
+ }
9485
+
9486
+ .eval-fixture-pill {
9487
+ display: inline-flex;
9488
+ align-items: center;
9489
+ justify-content: center;
9490
+ width: 22px;
9491
+ height: 22px;
9492
+ border-radius: 50%;
9493
+ font-weight: 700;
9494
+ font-size: 12px;
9495
+ }
9496
+
9497
+ .eval-fixture-pill.is-pass {
9498
+ background: rgba(63, 185, 80, 0.15);
9499
+ color: #3fb950;
9500
+ }
9501
+
9502
+ .eval-fixture-pill.is-fail {
9503
+ background: rgba(239, 68, 68, 0.15);
9504
+ color: #f85149;
9505
+ }
9506
+
9507
+ .eval-fixture-id {
9508
+ font-size: 12px;
9509
+ overflow: hidden;
9510
+ text-overflow: ellipsis;
9511
+ white-space: nowrap;
9512
+ }
9513
+
9514
+ .eval-fixture-fails {
9515
+ font-size: 12px;
9516
+ grid-column: 2 / 3;
9517
+ }
9518
+
9519
+ .eval-fixture-latency {
9520
+ font-size: 11px;
9521
+ font-family: ui-monospace, 'JetBrains Mono', monospace;
9522
+ white-space: nowrap;
9523
+ }
9524
+
9525
+ .eval-diff {
9526
+ display: flex;
9527
+ flex-direction: column;
9528
+ gap: var(--space-3, 12px);
9529
+ }
9530
+
9531
+ .eval-diff-title {
9532
+ margin: 0;
9533
+ font-size: 14px;
9534
+ font-weight: 600;
9535
+ }
9536
+
9537
+ .eval-diff-subtitle {
9538
+ font-size: 12px;
9539
+ }
9540
+
9541
+ .eval-diff-stats {
9542
+ display: flex;
9543
+ align-items: center;
9544
+ gap: var(--space-4, 16px);
9545
+ flex-wrap: wrap;
9546
+ margin: 4px 0 8px;
9547
+ }
9548
+
9549
+ .eval-diff-stat {
9550
+ display: inline-flex;
9551
+ align-items: center;
9552
+ gap: 4px;
9553
+ font-size: 13px;
9554
+ font-weight: 600;
9555
+ }
9556
+
9557
+ .eval-diff-stat-icon {
9558
+ display: inline-flex;
9559
+ align-items: center;
9560
+ justify-content: center;
9561
+ width: 18px;
9562
+ height: 18px;
9563
+ border-radius: 50%;
9564
+ font-size: 12px;
9565
+ font-weight: 700;
9566
+ }
9567
+
9568
+ .eval-diff-stat.improved { color: #3fb950; }
9569
+ .eval-diff-stat.improved .eval-diff-stat-icon { background: rgba(63, 185, 80, 0.15); }
9570
+ .eval-diff-stat.regressed { color: #f85149; }
9571
+ .eval-diff-stat.regressed .eval-diff-stat-icon { background: rgba(239, 68, 68, 0.15); }
9572
+ .eval-diff-stat.unchanged { color: var(--text-dim); }
9573
+
9574
+ .eval-diff-list {
9575
+ list-style: none;
9576
+ margin: 0;
9577
+ padding: 0;
9578
+ display: flex;
9579
+ flex-direction: column;
9580
+ gap: 4px;
9581
+ }
9582
+
9583
+ .eval-diff-row {
9584
+ display: grid;
9585
+ grid-template-columns: minmax(0, 1.4fr) auto minmax(0, 1fr);
9586
+ align-items: center;
9587
+ gap: var(--space-3, 12px);
9588
+ padding: 4px 0;
9589
+ border-bottom: 1px solid var(--border);
9590
+ font-size: 13px;
9591
+ }
9592
+
9593
+ .eval-diff-row:last-child {
9594
+ border-bottom: none;
9595
+ }
9596
+
9597
+ .eval-diff-row.is-improved { color: var(--text); }
9598
+ .eval-diff-row.is-improved .eval-diff-fixture { color: #3fb950; }
9599
+ .eval-diff-row.is-regressed { color: var(--text); }
9600
+ .eval-diff-row.is-regressed .eval-diff-fixture { color: #f85149; }
9601
+
9602
+ .eval-diff-fixture {
9603
+ font-size: 12px;
9604
+ font-weight: 600;
9605
+ overflow: hidden;
9606
+ text-overflow: ellipsis;
9607
+ white-space: nowrap;
9608
+ }
9609
+
9610
+ .eval-diff-arrow {
9611
+ color: var(--text-dim);
9612
+ }
9613
+
9614
+ .eval-diff-arrow-small {
9615
+ color: var(--text-dim);
9616
+ margin: 0 4px;
9617
+ }
9618
+
9619
+ .eval-diff-states {
9620
+ display: inline-flex;
9621
+ align-items: center;
9622
+ gap: 4px;
9623
+ font-size: 12px;
9624
+ }
9625
+
9626
+ .eval-diff-state {
9627
+ display: inline-flex;
9628
+ align-items: center;
9629
+ padding: 2px 6px;
9630
+ border-radius: 4px;
9631
+ font-family: ui-monospace, 'JetBrains Mono', monospace;
9632
+ font-size: 11px;
9633
+ }
9634
+
9635
+ .eval-diff-state.is-pass {
9636
+ background: rgba(63, 185, 80, 0.15);
9637
+ color: #3fb950;
9638
+ }
9639
+
9640
+ .eval-diff-state.is-fail {
9641
+ background: rgba(239, 68, 68, 0.15);
9642
+ color: #f85149;
9643
+ }
9644
+
9645
+ .eval-diff-empty {
9646
+ margin: 0;
9647
+ font-size: 13px;
9648
+ }
9649
+
9650
+ .eval-diff-card {
9651
+ display: flex;
9652
+ flex-direction: column;
9653
+ gap: var(--space-3, 12px);
9654
+ }
9655
+
9656
+ .eval-diff-card-head {
9657
+ display: flex;
9658
+ align-items: center;
9659
+ justify-content: space-between;
9660
+ gap: var(--space-3, 12px);
9661
+ }
9662
+
9663
+ .eval-diff-card-title {
9664
+ display: inline-flex;
9665
+ align-items: center;
9666
+ gap: 6px;
9667
+ font-weight: 600;
9668
+ font-size: 13px;
9669
+ }
9670
+
9671
+ .eval-diff-missing {
9672
+ font-size: 13px;
9673
+ padding: var(--space-3, 12px);
9674
+ }
9675
+
9676
+ .eval-baseline-hint {
9677
+ background: rgba(139, 92, 246, 0.05);
9678
+ border-color: rgba(139, 92, 246, 0.25);
9679
+ }
9680
+
9681
+ /* ─── Eval overview (Eval.tsx) ──────────────────────────────────────────── */
9682
+
9683
+ .eval-summary-grid {
9684
+ display: grid;
9685
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
9686
+ gap: var(--space-4, 16px);
9687
+ margin-bottom: var(--space-6, 24px);
9688
+ }
9689
+
9690
+ .eval-summary-card {
9691
+ min-height: 120px;
9692
+ }
9693
+
9694
+ .eval-summary-value {
9695
+ font-size: 28px;
9696
+ font-weight: 700;
9697
+ letter-spacing: -0.01em;
9698
+ color: var(--text-strong);
9699
+ line-height: 1;
9700
+ margin: 4px 0;
9701
+ }
9702
+
9703
+ .eval-summary-cta {
9704
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.08), rgba(59, 130, 246, 0.04));
9705
+ border-color: rgba(139, 92, 246, 0.25);
9706
+ justify-content: space-between;
9707
+ }
9708
+
9709
+ .eval-summary-cta-btn {
9710
+ align-self: flex-start;
9711
+ margin-top: auto;
9712
+ }
9713
+
9714
+ .eval-section-title {
9715
+ font-size: 14px;
9716
+ font-weight: 600;
9717
+ text-transform: uppercase;
9718
+ letter-spacing: 0.04em;
9719
+ color: var(--text-dim);
9720
+ margin: 0 0 var(--space-3, 12px);
9721
+ }
9722
+
9723
+ .eval-recent-section {
9724
+ display: flex;
9725
+ flex-direction: column;
9726
+ }
9727
+
9728
+ /* ─── High-contrast mode (prefers-contrast: more) ──────────────────── */
9729
+ @media (prefers-contrast: more) {
9730
+ :root {
9731
+ --text-dim: var(--text);
9732
+ --text-muted: var(--text);
9733
+ }
9734
+
9735
+ [data-theme='light'] {
9736
+ --text-dim: #374151;
9737
+ --text-muted: #374151;
9738
+ }
9739
+
9740
+ :focus-visible,
9741
+ button:focus-visible,
9742
+ [role='button']:focus-visible,
9743
+ a:focus-visible,
9744
+ input:focus-visible,
9745
+ textarea:focus-visible,
9746
+ select:focus-visible,
9747
+ [tabindex]:focus-visible {
9748
+ outline-width: 3px;
9749
+ outline-style: solid;
9750
+ }
9751
+ }
@@ -208,7 +208,7 @@ function DoctorInner({
208
208
  </div>
209
209
  <div className="doctor-header-meta muted">
210
210
  <Clock size={12} aria-hidden />
211
- <span>
211
+ <span aria-live="polite" aria-atomic="true">
212
212
  v{data.bizarVersion} · {data.platform}/{data.arch} ·{' '}
213
213
  up {Math.floor(data.uptime)}s
214
214
  {lastFetched ? ` · refreshed ${formatTime(new Date(lastFetched))}` : ''}
@@ -266,7 +266,7 @@ function DoctorInner({
266
266
  </CardTitle>
267
267
  <CardMeta>Run a check, view logs, or export the snapshot.</CardMeta>
268
268
  <div className="doctor-actions-row">
269
- <Button variant="primary" onClick={fetchSnapshot} disabled={refreshing}>
269
+ <Button variant="primary" onClick={fetchSnapshot} disabled={refreshing} aria-label="Refresh diagnostics">
270
270
  {refreshing ? <Spinner size="sm" /> : <RefreshCw size={12} />}
271
271
  {refreshing ? 'Running…' : 'Run Health Check Now'}
272
272
  </Button>
@@ -279,7 +279,7 @@ function DoctorInner({
279
279
  <Button
280
280
  variant="ghost"
281
281
  onClick={() => setAutoRefresh((v) => !v)}
282
- title="Toggle auto-refresh"
282
+ aria-label={autoRefresh ? 'Pause auto-refresh' : 'Resume auto-refresh'}
283
283
  >
284
284
  {autoRefresh ? 'Pause auto-refresh' : 'Resume auto-refresh'}
285
285
  </Button>