great-cto 3.3.0 → 3.4.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.
@@ -160,12 +160,24 @@ html, body { height: 100%; }
160
160
  body {
161
161
  font-family: var(--sans);
162
162
  color: var(--text);
163
- font-size: 14px;
163
+ font-size: var(--fs-base);
164
164
  line-height: 1.5;
165
165
  -webkit-font-smoothing: antialiased;
166
166
  background: var(--bg-card);
167
167
  }
168
- button { font-family: inherit; cursor: pointer; }
168
+ /* Form controls do not inherit type from the page — they start at the browser's
169
+ own font, and `font-family: inherit` alone fixed only half of that. The size
170
+ stayed at the UA default 13.333px: off every step of the scale, on nine inputs
171
+ and four buttons, including the fields the CTO actually types into. A scale
172
+ that the text you enter does not follow is not the page's scale.
173
+
174
+ Bare-element specificity (0,0,1), so every component rule below still wins —
175
+ this only supplies a floor where nothing else speaks. */
176
+ button, input, select, textarea {
177
+ font-family: inherit;
178
+ font-size: var(--fs-body);
179
+ }
180
+ button { cursor: pointer; }
169
181
 
170
182
  /* Headings had no base rule, so every unstyled h1/h2/h3 fell to the browser's
171
183
  bold 700. Three weights exist in this design — 400 body, 500 value, 600 title
@@ -236,16 +248,16 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
236
248
  background: rgba(180, 83, 9, 0.10);
237
249
  border: 1px solid var(--status-progress);
238
250
  border-radius: 8px;
239
- font-size: 13px; color: var(--text);
251
+ font-size: var(--fs-body); color: var(--text);
240
252
  }
241
- .stale-board code { font-family: var(--mono); font-size: 12px; }
253
+ .stale-board code { font-family: var(--mono); font-size: var(--fs-small); }
242
254
 
243
255
  /* Per-agent budget. Four states, and they must not look alike: `exceeded` holds
244
256
  a dispatch, `unmeasured` deliberately does not. A shared grey chip for
245
257
  "under the cap" and "we never measured it" would be the same silent zero this
246
258
  board spends its time refusing to render. */
247
259
  .budget-chip {
248
- font-family: var(--mono); font-size: 10px;
260
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
249
261
  padding: 1px 6px; border-radius: 3px;
250
262
  border: 1px solid var(--border);
251
263
  white-space: nowrap;
@@ -264,11 +276,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
264
276
  .ab-row:first-of-type { border-top: 0; }
265
277
  .ab-name { font-family: var(--mono); flex: 1 1 auto; min-width: 0; }
266
278
  .ab-cap { font-family: var(--mono); color: var(--text2); }
267
- .ab-spent { font-family: var(--mono); font-size: 11px; color: var(--text3); }
268
- .ab-note { margin-top: 8px; font-size: 11px; color: var(--status-progress); }
279
+ .ab-spent { font-family: var(--mono); font-size: var(--fs-caption); color: var(--text3); }
280
+ .ab-note { margin-top: 8px; font-size: var(--fs-caption); color: var(--status-progress); }
269
281
  .ab-note.ab-bad { color: var(--status-blocked); }
270
282
  .ab-btn {
271
- font-family: inherit; font-size: 11px;
283
+ font-family: inherit; font-size: var(--fs-caption);
272
284
  min-height: 24px; padding: 0 8px;
273
285
  border: 1px solid var(--border); border-radius: 5px;
274
286
  background: var(--bg-card); color: var(--text2);
@@ -280,7 +292,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
280
292
 
281
293
  .budget-line {
282
294
  margin: -18px 0 24px; padding: 8px 12px;
283
- border-radius: 8px; font-size: 12px;
295
+ border-radius: 8px; font-size: var(--fs-small);
284
296
  border: 1px solid var(--border);
285
297
  }
286
298
  .budget-line.exceeded { color: var(--status-blocked); border-color: var(--status-blocked); background: var(--p0-bg); font-weight: 500; }
@@ -340,7 +352,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
340
352
  .sidebar-brand .brand-mark svg { width: 22px; height: 22px; }
341
353
  .sidebar-brand .brand-name {
342
354
  font-family: var(--serif); font-weight: 600;
343
- font-size: 15px; letter-spacing: -0.01em;
355
+ font-size: var(--fs-title-s); letter-spacing: -0.01em;
344
356
  color: var(--text);
345
357
  }
346
358
  .proj-switch {
@@ -353,9 +365,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
353
365
  }
354
366
  .proj-switch:hover { background: var(--bg-muted); }
355
367
  .proj-switch .star { color: var(--accent); font-size: 16px; }
356
- .proj-switch .name { font-weight: 600; font-size: 14px; }
368
+ .proj-switch .name { font-weight: 600; font-size: var(--fs-base); }
357
369
  .proj-switch .sw-meta { flex: 1; display: flex; flex-direction: column; gap: 1px; }
358
- .proj-switch .sw-arch { font-size: 10px; color: var(--text3); font-weight: 400; letter-spacing: 0.03em; }
370
+ .proj-switch .sw-arch { font-size: var(--fs-eyebrow); color: var(--text3); font-weight: 400; letter-spacing: 0.03em; }
359
371
  .proj-switch .chev {
360
372
  color: var(--text3); width: 12px; height: 12px;
361
373
  transition: transform 0.15s;
@@ -383,7 +395,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
383
395
  }
384
396
  .proj-menu-search input {
385
397
  flex: 1; border: none; outline: none;
386
- font-family: inherit; font-size: 12px;
398
+ font-family: inherit; font-size: var(--fs-small);
387
399
  color: var(--text); background: transparent;
388
400
  }
389
401
  .proj-item {
@@ -391,11 +403,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
391
403
  padding: 8px 10px;
392
404
  border-radius: 5px;
393
405
  cursor: pointer;
394
- font-size: 13px;
406
+ font-size: var(--fs-body);
395
407
  }
396
408
  .proj-item:hover { background: var(--bg-muted); }
397
409
  .proj-item.active { background: var(--bg-strong); font-weight: 500; }
398
- .proj-item .star { color: var(--accent); font-size: 14px; }
410
+ .proj-item .star { color: var(--accent); font-size: var(--fs-base); }
399
411
  .proj-icon, .proj-switch .proj-icon, .crumbs .proj-icon {
400
412
  display: inline-flex; align-items: center; justify-content: center;
401
413
  flex-shrink: 0;
@@ -404,9 +416,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
404
416
  .proj-item .proj-icon { width: 22px; height: 22px; }
405
417
  .crumbs .proj-icon { width: 14px; height: 14px; margin-right: 2px; }
406
418
  .proj-item-info { flex: 1; min-width: 0; }
407
- .proj-item-info .name { font-weight: 500; font-size: 13px; }
419
+ .proj-item-info .name { font-weight: 500; font-size: var(--fs-body); }
408
420
  .proj-item-info .arch {
409
- font-family: var(--mono); font-size: 10px;
421
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
410
422
  color: var(--text3);
411
423
  letter-spacing: 0.04em;
412
424
  }
@@ -418,7 +430,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
418
430
  .proj-menu-empty {
419
431
  padding: 14px 10px;
420
432
  text-align: center;
421
- font-size: 12px;
433
+ font-size: var(--fs-small);
422
434
  color: var(--text3);
423
435
  }
424
436
 
@@ -427,7 +439,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
427
439
  display: flex; align-items: center; gap: 10px;
428
440
  height: 32px; padding: 0 10px;
429
441
  border-radius: 6px;
430
- font-size: 13px;
442
+ font-size: var(--fs-body);
431
443
  color: var(--text);
432
444
  cursor: pointer;
433
445
  position: relative;
@@ -445,14 +457,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
445
457
  }
446
458
  .nav-item .icon { width: 16px; height: 16px; color: var(--text2); flex-shrink: 0; }
447
459
  .nav-item.active .icon { color: var(--accent); }
448
- .nav-item .count { margin-left: auto; font-size: 11px; color: var(--text3); font-family: var(--mono); }
460
+ .nav-item .count { margin-left: auto; font-size: var(--fs-caption); color: var(--text3); font-family: var(--mono); }
449
461
  .nav-divider { height: 1px; background: var(--border); margin: 12px 8px; }
450
462
 
451
463
  .sidebar-footer {
452
464
  padding: 12px 16px;
453
465
  border-top: 1px solid var(--border);
454
466
  font-family: var(--mono);
455
- font-size: 11px;
467
+ font-size: var(--fs-caption);
456
468
  color: var(--text3);
457
469
  letter-spacing: 0.04em;
458
470
  display: flex; align-items: center; gap: 6px;
@@ -472,7 +484,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
472
484
  border-bottom: 1px solid var(--border);
473
485
  display: flex; align-items: center; justify-content: space-between;
474
486
  padding: 0 20px;
475
- font-size: 13px;
487
+ font-size: var(--fs-body);
476
488
  }
477
489
  .crumbs { display: flex; align-items: center; gap: 8px; color: var(--text2); }
478
490
  .crumbs .star { color: var(--accent); }
@@ -493,12 +505,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
493
505
  .search-box svg { color: var(--text3); flex-shrink: 0; }
494
506
  .search-box input {
495
507
  flex: 1; border: none; outline: none; background: transparent;
496
- font-family: inherit; font-size: 13px;
508
+ font-family: inherit; font-size: var(--fs-body);
497
509
  color: var(--text); padding: 3px 0;
498
510
  }
499
511
  .search-box input::placeholder { color: var(--text3); }
500
512
  .search-box kbd {
501
- font-family: var(--mono); font-size: 10px;
513
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
502
514
  color: var(--text3);
503
515
  background: var(--bg-strong);
504
516
  border-radius: 3px; padding: 1px 5px;
@@ -515,7 +527,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
515
527
  .btn-bell-badge {
516
528
  position: absolute; top: -5px; right: -5px;
517
529
  background: var(--accent); color: #04140d;
518
- font-size: 10px; font-weight: 700; line-height: 1;
530
+ font-size: var(--fs-eyebrow); font-weight: 700; line-height: 1;
519
531
  min-width: 16px; height: 16px; border-radius: 8px;
520
532
  display: none; align-items: center; justify-content: center;
521
533
  padding: 0 3px;
@@ -525,7 +537,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
525
537
  .tab-btn {
526
538
  background: transparent; border: none;
527
539
  padding: 6px 12px; border-radius: 6px;
528
- font-size: 13px; color: var(--text2);
540
+ font-size: var(--fs-body); color: var(--text2);
529
541
  }
530
542
  .tab-btn:hover { background: var(--bg-muted); color: var(--text); }
531
543
  .tab-btn.active { background: var(--bg-strong); color: var(--text); font-weight: 500; }
@@ -546,13 +558,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
546
558
  background: #0f1115; color: #fff; border: none;
547
559
  height: 30px; padding: 0 14px;
548
560
  border-radius: 999px;
549
- font-size: 13px; font-weight: 500;
561
+ font-size: var(--fs-body); font-weight: 500;
550
562
  display: inline-flex; align-items: center; gap: 6px;
551
563
  }
552
564
  .btn-new:hover { background: #2a2d33; }
553
565
 
554
566
  .task-count {
555
- font-family: var(--mono); font-size: 11px;
567
+ font-family: var(--mono); font-size: var(--fs-caption);
556
568
  color: var(--text3); letter-spacing: 0.04em;
557
569
  margin-right: 6px;
558
570
  }
@@ -582,7 +594,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
582
594
  .col-head {
583
595
  display: flex; align-items: center; gap: 8px;
584
596
  padding: 4px 6px 8px;
585
- font-size: 13px; font-weight: 600;
597
+ font-size: var(--fs-body); font-weight: 600;
586
598
  }
587
599
  .col-dot {
588
600
  width: 10px; height: 10px; border-radius: 50%;
@@ -592,7 +604,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
592
604
  .col-dot.filled { background: currentColor; }
593
605
  .col-dot.half { background: conic-gradient(currentColor 50%, transparent 50%); }
594
606
  .col-count {
595
- font-family: var(--mono); font-size: 11px;
607
+ font-family: var(--mono); font-size: var(--fs-caption);
596
608
  color: var(--text3); margin-left: 2px; font-weight: 500;
597
609
  }
598
610
  .col-title { color: var(--text); }
@@ -614,7 +626,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
614
626
  overflow: hidden;
615
627
  }
616
628
  .card-desc {
617
- font-size: 12px;
629
+ font-size: var(--fs-small);
618
630
  line-height: 1.4;
619
631
  color: var(--text2);
620
632
  display: -webkit-box;
@@ -626,7 +638,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
626
638
  .card:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-1px); }
627
639
  .card.selected { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
628
640
  .card .id {
629
- font-family: var(--mono); font-size: 10px;
641
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
630
642
  letter-spacing: 0.04em; color: var(--text3);
631
643
  flex-shrink: 0;
632
644
  }
@@ -654,7 +666,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
654
666
  .card-footer > * { flex-shrink: 0; }
655
667
  .card-time {
656
668
  margin-left: auto;
657
- font-family: var(--mono); font-size: 11px;
669
+ font-family: var(--mono); font-size: var(--fs-caption);
658
670
  color: var(--text3);
659
671
  }
660
672
 
@@ -662,7 +674,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
662
674
  display: inline-flex; align-items: center; gap: 4px;
663
675
  height: 20px; padding: 0 8px;
664
676
  border-radius: 999px;
665
- font-size: 11px; font-weight: 500;
677
+ font-size: var(--fs-caption); font-weight: 500;
666
678
  background: var(--bg-strong); color: var(--text2);
667
679
  }
668
680
  .chip-p0 { background: var(--p0-bg); color: var(--p0-fg); }
@@ -686,7 +698,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
686
698
  height: 24px;
687
699
  border: 1px solid var(--border);
688
700
  border-radius: 5px;
689
- font-family: inherit; font-size: 11px; font-weight: 500;
701
+ font-family: inherit; font-size: var(--fs-caption); font-weight: 500;
690
702
  cursor: pointer;
691
703
  background: var(--bg-card);
692
704
  transition: background 0.12s, border-color 0.12s;
@@ -704,13 +716,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
704
716
  #agent-panel { position: fixed; right: 16px; bottom: 16px; width: min(560px, 94vw); max-height: 70vh; display: none; flex-direction: column; background: var(--bg-card, #11161a); border: 1px solid var(--border, #1f2a26); border-radius: 12px; box-shadow: 0 24px 70px rgba(0,0,0,.5); z-index: 60; overflow: hidden; }
705
717
  #agent-panel.open { display: flex; }
706
718
  #agent-panel header { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border, #1f2a26); background: transparent; }
707
- #agent-panel header b { font-size: 13px; }
719
+ #agent-panel header b { font-size: var(--fs-body); }
708
720
  #agent-panel .ap-x { margin-left: auto; cursor: pointer; border: 0; background: none; color: var(--text3); font-size: 16px; }
709
721
  #agent-log { flex: 1; overflow: auto; padding: 10px 14px; font: 12px/1.55 ui-monospace, Menlo, monospace; }
710
722
  #agent-log .al { margin: 2px 0; white-space: pre-wrap; word-break: break-word; }
711
723
  #agent-log .al.tool { color: var(--status-review); } #agent-log .al.system { color: var(--text3); } #agent-log .al.error { color: var(--status-blocked, #ff5a5a); } #agent-log .al.done { color: var(--text3); font-weight: 600; }
712
724
  #agent-panel .ap-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--border, #1f2a26); }
713
- #agent-panel input { flex: 1; background: var(--bg-muted); color: var(--text, #ecf2ee); border: 1px solid var(--border, #1f2a26); border-radius: 8px; padding: 7px 10px; font: inherit; font-size: 13px; }
725
+ #agent-panel input { flex: 1; background: var(--bg-muted); color: var(--text, #ecf2ee); border: 1px solid var(--border, #1f2a26); border-radius: 8px; padding: 7px 10px; font: inherit; font-size: var(--fs-body); }
714
726
  #agent-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text3); }
715
727
  #agent-dot.live { background: var(--status-review); animation: apulse 1.2s infinite; }
716
728
  @keyframes apulse { 0%,100%{opacity:1} 50%{opacity:.4} }
@@ -734,7 +746,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
734
746
 
735
747
  .empty {
736
748
  padding: 20px 8px;
737
- font-size: 12px;
749
+ font-size: var(--fs-small);
738
750
  color: var(--text3);
739
751
  text-align: center;
740
752
  }
@@ -751,7 +763,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
751
763
  border-left-width: 3px;
752
764
  border-radius: 6px;
753
765
  background: rgba(240, 180, 41, 0.08);
754
- font-size: 12px;
766
+ font-size: var(--fs-small);
755
767
  line-height: 1.45;
756
768
  }
757
769
  .degraded-banner strong { color: var(--text); font-weight: 600; }
@@ -795,7 +807,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
795
807
  border-bottom: 1px solid var(--border);
796
808
  }
797
809
  .side-id {
798
- font-family: var(--mono); font-size: 11px; font-weight: 500;
810
+ font-family: var(--mono); font-size: var(--fs-caption); font-weight: 500;
799
811
  background: var(--bg-strong); color: var(--text);
800
812
  padding: 4px 8px; border-radius: 4px;
801
813
  letter-spacing: 0.04em;
@@ -817,7 +829,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
817
829
  color: var(--text);
818
830
  }
819
831
  .side-desc {
820
- font-size: 14px; line-height: 1.6;
832
+ font-size: var(--fs-base); line-height: 1.6;
821
833
  color: var(--text2);
822
834
  margin-bottom: 16px;
823
835
  padding-bottom: 14px;
@@ -828,30 +840,30 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
828
840
  .side-desc-empty {
829
841
  color: var(--text3, #9ca3af);
830
842
  font-style: italic;
831
- font-size: 13px;
843
+ font-size: var(--fs-body);
832
844
  }
833
845
  .side-desc-empty code {
834
846
  font-style: normal;
835
847
  background: var(--bg-muted);
836
848
  padding: 1px 5px;
837
849
  border-radius: 3px;
838
- font-size: 12px;
850
+ font-size: var(--fs-small);
839
851
  }
840
852
  .side-desc.md { white-space: normal; }
841
853
  .side-desc.md h1 { font-size: 16px; margin: 8px 0 6px; font-weight: 600; }
842
- .side-desc.md h2 { font-size: 14px; margin: 10px 0 4px; font-weight: 600; color: var(--text); }
843
- .side-desc.md h3 { font-size: 13px; margin: 8px 0 3px; font-weight: 600; color: var(--text); }
844
- .side-desc.md h4 { font-size: 12px; margin: 6px 0 2px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text2); }
854
+ .side-desc.md h2 { font-size: var(--fs-base); margin: 10px 0 4px; font-weight: 600; color: var(--text); }
855
+ .side-desc.md h3 { font-size: var(--fs-body); margin: 8px 0 3px; font-weight: 600; color: var(--text); }
856
+ .side-desc.md h4 { font-size: var(--fs-small); margin: 6px 0 2px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text2); }
845
857
  .side-desc.md p { margin: 0 0 8px; }
846
858
  .side-desc.md ul { padding-left: 18px; margin: 4px 0 8px; }
847
859
  .side-desc.md li { margin: 2px 0; }
848
860
  .side-desc.md code {
849
861
  background: var(--bg-muted); padding: 1px 4px;
850
- border-radius: 3px; font-size: 12px; font-family: ui-monospace, monospace;
862
+ border-radius: 3px; font-size: var(--fs-small); font-family: ui-monospace, monospace;
851
863
  }
852
864
  .side-desc.md pre.md-pre {
853
865
  background: var(--bg-muted); padding: 8px 10px;
854
- border-radius: 4px; overflow-x: auto; font-size: 12px;
866
+ border-radius: 4px; overflow-x: auto; font-size: var(--fs-small);
855
867
  margin: 6px 0;
856
868
  }
857
869
  .side-desc.md pre.md-pre code { background: none; padding: 0; }
@@ -873,7 +885,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
873
885
  .toast {
874
886
  background: #1f2937; color: #fff;
875
887
  padding: 8px 14px; border-radius: 6px;
876
- font-size: 13px; min-width: 200px; max-width: 360px;
888
+ font-size: var(--fs-body); min-width: 200px; max-width: 360px;
877
889
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
878
890
  opacity: 0; transform: translateY(8px);
879
891
  transition: opacity 0.2s, transform 0.2s;
@@ -903,7 +915,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
903
915
  #cmdk-input {
904
916
  border: 0; border-bottom: 1px solid var(--border);
905
917
  background: transparent; color: var(--text);
906
- font-family: inherit; font-size: 15px; padding: 14px 16px; outline: none;
918
+ font-family: inherit; font-size: var(--fs-title-s); padding: 14px 16px; outline: none;
907
919
  }
908
920
  #cmdk-list { overflow-y: auto; padding: 6px; }
909
921
  .cmdk-item {
@@ -913,14 +925,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
913
925
  }
914
926
  .cmdk-item.on { background: var(--bg-muted); }
915
927
  .cmdk-group {
916
- font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em;
928
+ font-family: var(--mono); font-size: var(--fs-eyebrow); letter-spacing: 0.06em;
917
929
  text-transform: uppercase; color: var(--text3);
918
930
  min-width: 34px; flex-shrink: 0;
919
931
  }
920
932
  .cmdk-label { font-size: var(--fs-body); color: var(--text); flex: 1; min-width: 0;
921
933
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
922
- .cmdk-hint { font-family: var(--mono); font-size: 11px; color: var(--text3); flex-shrink: 0; }
923
- .cmdk-empty { padding: 18px 14px; font-size: 13px; color: var(--text2); }
934
+ .cmdk-hint { font-family: var(--mono); font-size: var(--fs-caption); color: var(--text3); flex-shrink: 0; }
935
+ .cmdk-empty { padding: 18px 14px; font-size: var(--fs-body); color: var(--text2); }
924
936
 
925
937
  #hotkey-modal {
926
938
  position: fixed; inset: 0; z-index: 10000;
@@ -935,16 +947,16 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
935
947
  border-radius: 8px; padding: 20px 24px; min-width: 360px;
936
948
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
937
949
  }
938
- .hk-title { font-size: 15px; font-weight: 600; margin-bottom: 12px; }
950
+ .hk-title { font-size: var(--fs-title-s); font-weight: 600; margin-bottom: 12px; }
939
951
  .hk-table { border-collapse: collapse; width: 100%; }
940
- .hk-table td { padding: 4px 8px; font-size: 13px; }
952
+ .hk-table td { padding: 4px 8px; font-size: var(--fs-body); }
941
953
  .hk-table td:first-child { text-align: right; white-space: nowrap; color: var(--text2); }
942
954
  .hk-table kbd {
943
955
  background: var(--bg-muted); border: 1px solid var(--border);
944
- border-radius: 3px; padding: 1px 5px; font-size: 11px;
956
+ border-radius: 3px; padding: 1px 5px; font-size: var(--fs-caption);
945
957
  font-family: ui-monospace, monospace; color: var(--text);
946
958
  }
947
- .hk-foot { margin-top: 12px; font-size: 11px; color: var(--text3, #9ca3af); text-align: center; }
959
+ .hk-foot { margin-top: 12px; font-size: var(--fs-caption); color: var(--text3, #9ca3af); text-align: center; }
948
960
 
949
961
  /* ── Resume detail modal (clickable verdicts + decisions) ───────────── */
950
962
  #resume-detail-modal {
@@ -962,30 +974,30 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
962
974
  .rdm-head .rdm-agent { font-weight: 600; }
963
975
  .rdm-head .rdm-verdict.ok { color: #4ade80; }
964
976
  .rdm-head .rdm-verdict.fail { color: #f87171; }
965
- .rdm-head .rdm-ts { color: var(--text2); font-size: 11px; margin-left: auto; }
977
+ .rdm-head .rdm-ts { color: var(--text2); font-size: var(--fs-caption); margin-left: auto; }
966
978
  .rdm-raw {
967
979
  background: var(--bg-muted); padding: 8px 10px; border-radius: 4px;
968
- font-family: ui-monospace, monospace; font-size: 12px;
980
+ font-family: ui-monospace, monospace; font-size: var(--fs-small);
969
981
  white-space: pre-wrap; word-break: break-all;
970
982
  border: 1px solid var(--border);
971
983
  max-height: 200px; overflow-y: auto;
972
984
  }
973
985
  .rdm-artefacts { margin-top: 12px; }
974
- .rdm-artefacts-title { font-size: 11px; color: var(--text2); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
986
+ .rdm-artefacts-title { font-size: var(--fs-caption); color: var(--text2); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
975
987
  .rdm-artefact {
976
988
  display: inline-flex; align-items: center; gap: 4px;
977
989
  margin: 2px 4px 2px 0; padding: 4px 8px;
978
990
  background: var(--bg-muted); border: 1px solid var(--border);
979
- border-radius: 4px; font-size: 12px; cursor: pointer;
991
+ border-radius: 4px; font-size: var(--fs-small); cursor: pointer;
980
992
  text-decoration: none; color: var(--text);
981
993
  font-family: ui-monospace, monospace;
982
994
  }
983
995
  .rdm-artefact:hover { background: var(--bg-muted); }
984
996
  .rdm-artefact .rdm-art-kind {
985
- font-size: 10px; padding: 1px 4px; border-radius: 3px;
997
+ font-size: var(--fs-eyebrow); padding: 1px 4px; border-radius: 3px;
986
998
  background: var(--accent-soft); color: var(--accent-text);
987
999
  }
988
- .rdm-foot { margin-top: 12px; font-size: 11px; color: var(--text3, #9ca3af); }
1000
+ .rdm-foot { margin-top: 12px; font-size: var(--fs-caption); color: var(--text3, #9ca3af); }
989
1001
  .resume-item { cursor: pointer; }
990
1002
  .resume-item:hover { background: var(--bg-muted); border-radius: 4px; }
991
1003
 
@@ -1039,7 +1051,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1039
1051
  border: 1px solid var(--border, #2a2d33);
1040
1052
  border-radius: 6px;
1041
1053
  color: var(--text);
1042
- font: inherit; font-size: 13px;
1054
+ font: inherit; font-size: var(--fs-body);
1043
1055
  padding: 8px 10px;
1044
1056
  outline: none;
1045
1057
  transition: border-color 120ms;
@@ -1055,7 +1067,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1055
1067
  .ni-row { display: flex; gap: 12px; }
1056
1068
  .ni-checkbox {
1057
1069
  flex-direction: row; align-items: center; gap: 8px;
1058
- font-size: 13px; cursor: pointer;
1070
+ font-size: var(--fs-body); cursor: pointer;
1059
1071
  }
1060
1072
  .ni-checkbox input { margin: 0; cursor: pointer; }
1061
1073
  .ni-checkbox span { color: var(--text2); }
@@ -1065,7 +1077,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1065
1077
  }
1066
1078
  .ni-btn {
1067
1079
  border: 1px solid transparent; border-radius: 6px;
1068
- padding: 7px 14px; font: inherit; font-size: 13px;
1080
+ padding: 7px 14px; font: inherit; font-size: var(--fs-body);
1069
1081
  cursor: pointer; transition: all 120ms;
1070
1082
  }
1071
1083
  .ni-btn-ghost {
@@ -1089,14 +1101,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1089
1101
  .side-gate-actions .gate-btn {
1090
1102
  flex: 1;
1091
1103
  height: 36px;
1092
- font-size: 13px; font-weight: 500;
1104
+ font-size: var(--fs-body); font-weight: 500;
1093
1105
  padding: 0 14px;
1094
1106
  }
1095
1107
  .side-section {
1096
1108
  margin-top: 18px;
1097
1109
  }
1098
1110
  .side-section-head {
1099
- font-family: var(--mono); font-size: 10px;
1111
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1100
1112
  letter-spacing: 0.1em; text-transform: uppercase;
1101
1113
  color: var(--text3); margin-bottom: 6px;
1102
1114
  }
@@ -1113,12 +1125,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1113
1125
  border-top: 1px solid var(--border);
1114
1126
  padding-top: 14px;
1115
1127
  }
1116
- .prop-key { color: var(--text3); font-size: 12px; padding: 6px 0; font-family: var(--mono); letter-spacing: 0.04em; }
1117
- .prop-val { font-size: 13px; padding: 6px 0; }
1128
+ .prop-key { color: var(--text3); font-size: var(--fs-small); padding: 6px 0; font-family: var(--mono); letter-spacing: 0.04em; }
1129
+ .prop-val { font-size: var(--fs-body); padding: 6px 0; }
1118
1130
  .pill {
1119
1131
  display: inline-flex; align-items: center; gap: 6px;
1120
1132
  padding: 4px 10px; border-radius: 999px;
1121
- font-size: 12px; font-weight: 500;
1133
+ font-size: var(--fs-small); font-weight: 500;
1122
1134
  background: var(--bg-strong); color: var(--text2);
1123
1135
  }
1124
1136
  .pill-gate { background: rgba(124, 58, 237, 0.12); color: var(--status-gate); }
@@ -1132,7 +1144,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1132
1144
  border: 1px solid var(--border);
1133
1145
  border-radius: 8px;
1134
1146
  padding: 12px 14px;
1135
- font-size: 13px;
1147
+ font-size: var(--fs-body);
1136
1148
  color: var(--text2);
1137
1149
  white-space: pre-wrap;
1138
1150
  line-height: 1.55;
@@ -1153,7 +1165,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1153
1165
  overflow-y: auto;
1154
1166
  }
1155
1167
  .memory-side-head {
1156
- font-family: var(--mono); font-size: 10px;
1168
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1157
1169
  letter-spacing: 0.1em; text-transform: uppercase;
1158
1170
  color: var(--text3);
1159
1171
  padding: 0 10px 8px;
@@ -1169,12 +1181,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1169
1181
  .mem-item:hover { background: var(--bg-muted); }
1170
1182
  .mem-item.active { background: var(--accent-soft); border-color: rgba(37, 64, 212, 0.16); }
1171
1183
  .mem-item .layer-tag {
1172
- font-family: var(--mono); font-size: 10px;
1184
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1173
1185
  color: var(--accent); letter-spacing: 0.06em;
1174
1186
  font-weight: 600;
1175
1187
  }
1176
1188
  .mem-item .name {
1177
- font-family: var(--mono); font-size: 12px;
1189
+ font-family: var(--mono); font-size: var(--fs-small);
1178
1190
  color: var(--text);
1179
1191
  }
1180
1192
  .mem-item .desc {
@@ -1182,14 +1194,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1182
1194
  margin-top: 2px;
1183
1195
  }
1184
1196
  .mem-item .meta {
1185
- font-family: var(--mono); font-size: 10px;
1197
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1186
1198
  color: var(--text3); margin-top: 2px;
1187
1199
  }
1188
1200
  .mem-item.missing { opacity: 0.55; }
1189
1201
  .mem-item.missing .layer-tag { color: var(--text3); }
1190
1202
  .mem-group-label {
1191
1203
  font-family: var(--mono);
1192
- font-size: 10px;
1204
+ font-size: var(--fs-eyebrow);
1193
1205
  letter-spacing: 0.08em;
1194
1206
  color: var(--text3);
1195
1207
  padding: 14px 12px 6px;
@@ -1209,18 +1221,18 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1209
1221
  }
1210
1222
  .memory-doc h1 { font-family: var(--serif); font-weight: 600; font-size: 28px; letter-spacing: -0.02em; margin: 0 0 14px; color: var(--text); }
1211
1223
  .memory-doc h2 { font-family: var(--serif); font-weight: 600; font-size: 20px; margin: 28px 0 10px; color: var(--text); }
1212
- .memory-doc h3 { font-family: var(--sans); font-weight: 600; font-size: 15px; margin: 18px 0 8px; color: var(--text); }
1213
- .memory-doc p { font-size: 14px; line-height: 1.65; color: var(--text2); margin: 0 0 10px; }
1224
+ .memory-doc h3 { font-family: var(--sans); font-weight: 600; font-size: var(--fs-title-s); margin: 18px 0 8px; color: var(--text); }
1225
+ .memory-doc p { font-size: var(--fs-base); line-height: 1.65; color: var(--text2); margin: 0 0 10px; }
1214
1226
  .memory-doc ul, .memory-doc ol { padding-left: 22px; margin: 0 0 12px; color: var(--text2); }
1215
- .memory-doc li { line-height: 1.6; font-size: 14px; }
1227
+ .memory-doc li { line-height: 1.6; font-size: var(--fs-base); }
1216
1228
  .memory-doc code {
1217
- font-family: var(--mono); font-size: 12px;
1229
+ font-family: var(--mono); font-size: var(--fs-small);
1218
1230
  background: var(--bg-muted);
1219
1231
  padding: 1px 6px; border-radius: 3px;
1220
1232
  color: var(--text);
1221
1233
  }
1222
1234
  .memory-doc pre {
1223
- font-family: var(--mono); font-size: 12px;
1235
+ font-family: var(--mono); font-size: var(--fs-small);
1224
1236
  background: var(--bg-strong);
1225
1237
  padding: 14px 16px;
1226
1238
  border-radius: 8px;
@@ -1233,7 +1245,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1233
1245
  .memory-doc table {
1234
1246
  border-collapse: collapse;
1235
1247
  margin: 12px 0;
1236
- font-size: 13px;
1248
+ font-size: var(--fs-body);
1237
1249
  width: 100%;
1238
1250
  }
1239
1251
  .memory-doc th, .memory-doc td {
@@ -1249,27 +1261,27 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1249
1261
  border-bottom: 1px solid var(--border);
1250
1262
  }
1251
1263
  .memory-doc-head .layer-tag {
1252
- font-family: var(--mono); font-size: 11px;
1264
+ font-family: var(--mono); font-size: var(--fs-caption);
1253
1265
  background: var(--accent-soft); color: var(--accent);
1254
1266
  padding: 4px 10px; border-radius: 4px;
1255
1267
  letter-spacing: 0.06em; font-weight: 600;
1256
1268
  }
1257
1269
  .memory-doc-head .filename {
1258
- font-family: var(--mono); font-size: 13px;
1270
+ font-family: var(--mono); font-size: var(--fs-body);
1259
1271
  color: var(--text);
1260
1272
  }
1261
1273
  .memory-doc-head .filesize {
1262
1274
  margin-left: auto;
1263
- font-family: var(--mono); font-size: 11px;
1275
+ font-family: var(--mono); font-size: var(--fs-caption);
1264
1276
  color: var(--text3);
1265
1277
  }
1266
1278
  .memory-empty {
1267
1279
  text-align: center; padding: 60px 24px;
1268
- color: var(--text3); font-size: 13px;
1280
+ color: var(--text3); font-size: var(--fs-body);
1269
1281
  }
1270
1282
 
1271
1283
  /* ── change_tier badge ────────────────────────────────────────────────────── */
1272
- .tier-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; margin-left: 6px;
1284
+ .tier-badge { font-size: var(--fs-caption); padding: 1px 8px; border-radius: 999px; margin-left: 6px;
1273
1285
  font-family: var(--mono); white-space: nowrap; }
1274
1286
  .tier-badge:empty { display: none; }
1275
1287
  .tier-badge.tier-T0 { background: var(--status-review); color: #fff; }
@@ -1306,12 +1318,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1306
1318
  min-height: 26px; padding: 0 8px;
1307
1319
  border: 1px solid var(--border); border-radius: 5px;
1308
1320
  background: var(--bg-card); color: var(--text2);
1309
- font-family: var(--mono); font-size: 10px; cursor: pointer;
1321
+ font-family: var(--mono); font-size: var(--fs-eyebrow); cursor: pointer;
1310
1322
  }
1311
1323
  .pl-recover-btn:hover { border-color: var(--status-blocked); color: var(--status-blocked); }
1312
1324
  .pl-recover-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
1313
1325
  .pl-stage .pl-name {
1314
- font-family: var(--mono); font-size: 11px;
1326
+ font-family: var(--mono); font-size: var(--fs-caption);
1315
1327
  letter-spacing: 0.04em; color: var(--text);
1316
1328
  display: flex; align-items: center; gap: 6px;
1317
1329
  }
@@ -1325,7 +1337,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1325
1337
  .pl-stage.failed .pl-name .dot { background: var(--status-blocked); }
1326
1338
  @keyframes pl-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); } 50% { box-shadow: 0 0 0 5px rgba(245, 158, 11, 0); } }
1327
1339
  .pl-stage .pl-msg {
1328
- font-size: 11px; color: var(--text2);
1340
+ font-size: var(--fs-caption); color: var(--text2);
1329
1341
  line-height: 1.35;
1330
1342
  display: -webkit-box;
1331
1343
  -webkit-line-clamp: 2;
@@ -1334,7 +1346,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1334
1346
  text-overflow: ellipsis;
1335
1347
  }
1336
1348
  .pl-stage .pl-meta {
1337
- font-family: var(--mono); font-size: 10px;
1349
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1338
1350
  color: var(--text3);
1339
1351
  }
1340
1352
  .pl-stage.idle .pl-msg, .pl-stage.idle .pl-meta { opacity: 0.55; }
@@ -1355,11 +1367,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1355
1367
  border-radius: 50%; background: rgba(0, 217, 126, 0.12);
1356
1368
  color: var(--accent, #00d97e); font-size: 16px; font-weight: 600;
1357
1369
  }
1358
- .inbox-allclear .ac-text { flex: 1; font-size: 14px; color: var(--text2); }
1370
+ .inbox-allclear .ac-text { flex: 1; font-size: var(--fs-base); color: var(--text2); }
1359
1371
  .inbox-allclear .ac-text b { color: var(--text); font-weight: 600; }
1360
1372
  .inbox-allclear .ac-actions { display: flex; gap: 8px; }
1361
1373
  .inbox-allclear .ac-btn {
1362
- font-size: 13px; padding: 7px 14px; border-radius: 8px;
1374
+ font-size: var(--fs-body); padding: 7px 14px; border-radius: 8px;
1363
1375
  border: 1px solid var(--border-strong); background: transparent;
1364
1376
  color: var(--text); cursor: pointer; transition: background 0.12s, border-color 0.12s;
1365
1377
  }
@@ -1376,7 +1388,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1376
1388
  margin: -4px 2px;
1377
1389
  padding: 12px 12px 10px;
1378
1390
  }
1379
- .pl-gate .gate-glyph { font-size: 12px; line-height: 1; flex-shrink: 0; filter: grayscale(1); opacity: 0.6; }
1391
+ .pl-gate .gate-glyph { font-size: var(--fs-small); line-height: 1; flex-shrink: 0; filter: grayscale(1); opacity: 0.6; }
1380
1392
  .pl-gate .pl-name { font-weight: 600; }
1381
1393
  .pl-gate.active {
1382
1394
  border-left-color: var(--status-blocked);
@@ -1387,7 +1399,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1387
1399
  .pl-gate .gate-badge {
1388
1400
  margin-left: auto;
1389
1401
  background: var(--status-blocked); color: #fff;
1390
- font-size: 10px; font-weight: 700;
1402
+ font-size: var(--fs-eyebrow); font-weight: 700;
1391
1403
  min-width: 16px; height: 16px; padding: 0 4px;
1392
1404
  border-radius: 8px;
1393
1405
  display: inline-flex; align-items: center; justify-content: center;
@@ -1408,7 +1420,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1408
1420
  display: flex; flex-direction: column; gap: 2px;
1409
1421
  }
1410
1422
  .cost-cell .lbl {
1411
- font-family: var(--mono); font-size: 10px;
1423
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1412
1424
  letter-spacing: 0.08em; text-transform: uppercase;
1413
1425
  color: var(--text2);
1414
1426
  }
@@ -1418,7 +1430,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1418
1430
  font-size: var(--fs-num-s); letter-spacing: -0.02em;
1419
1431
  color: var(--text); line-height: 1.1;
1420
1432
  }
1421
- .cost-cell .v small { font-size: 14px; color: var(--text2); margin-left: 2px; font-weight: 400; }
1433
+ .cost-cell .v small { font-size: var(--fs-base); color: var(--text2); margin-left: 2px; font-weight: 400; }
1422
1434
  .cost-cell .sub { font-size: var(--fs-caption); color: var(--text2); }
1423
1435
  .cost-cell.warn .v { color: var(--status-blocked); }
1424
1436
  .cost-cell.good .v { color: var(--status-review); }
@@ -1451,7 +1463,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1451
1463
  transform: translateX(-50%);
1452
1464
  background: #0f1115; color: #fff;
1453
1465
  padding: 4px 8px; border-radius: 4px;
1454
- font-family: var(--mono); font-size: 10px;
1466
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1455
1467
  white-space: nowrap;
1456
1468
  display: none;
1457
1469
  pointer-events: none;
@@ -1464,7 +1476,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1464
1476
  chart is the primary reading and this is the exact one. */
1465
1477
  .cost-table { margin-top: 10px; }
1466
1478
  .cost-table summary {
1467
- cursor: pointer; font-size: 12px; color: var(--text2);
1479
+ cursor: pointer; font-size: var(--fs-small); color: var(--text2);
1468
1480
  padding: 4px 0; list-style-position: inside;
1469
1481
  }
1470
1482
  .cost-table summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 4px; }
@@ -1472,13 +1484,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1472
1484
  .cost-table th, .cost-table td {
1473
1485
  text-align: left; padding: 5px 10px 5px 0;
1474
1486
  border-bottom: 1px solid var(--border);
1475
- font-size: 12px; white-space: nowrap;
1487
+ font-size: var(--fs-small); white-space: nowrap;
1476
1488
  }
1477
- .cost-table th { font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text2); }
1489
+ .cost-table th { font-family: var(--mono); font-size: var(--fs-eyebrow); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text2); }
1478
1490
  .cost-table td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; }
1479
1491
  .cost-axis {
1480
1492
  display: flex; justify-content: space-between;
1481
- font-family: var(--mono); font-size: 10px;
1493
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1482
1494
  color: var(--text3);
1483
1495
  margin-top: 4px;
1484
1496
  }
@@ -1491,13 +1503,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1491
1503
  }
1492
1504
  .cost-budget-line .label {
1493
1505
  position: absolute; right: 4px; top: -16px;
1494
- font-family: var(--mono); font-size: 10px;
1506
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1495
1507
  color: var(--status-progress);
1496
1508
  background: var(--bg-card); padding: 0 4px;
1497
1509
  }
1498
1510
  .cost-empty {
1499
1511
  text-align: center; padding: 30px 16px;
1500
- font-size: 13px; color: var(--text3);
1512
+ font-size: var(--fs-body); color: var(--text3);
1501
1513
  }
1502
1514
 
1503
1515
  /* ── Cost per feature ────────────────────────────────────────────────────── */
@@ -1507,7 +1519,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1507
1519
  padding-top: 16px;
1508
1520
  }
1509
1521
  .feat-title {
1510
- font-size: 11px; font-weight: 600; text-transform: uppercase;
1522
+ font-size: var(--fs-caption); font-weight: 600; text-transform: uppercase;
1511
1523
  letter-spacing: .06em; color: var(--text3);
1512
1524
  margin-bottom: 10px;
1513
1525
  }
@@ -1517,7 +1529,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1517
1529
  grid-template-columns: 160px 1fr 56px 32px;
1518
1530
  align-items: center;
1519
1531
  gap: 8px;
1520
- font-size: 12px;
1532
+ font-size: var(--fs-small);
1521
1533
  }
1522
1534
  .feat-name {
1523
1535
  font-family: var(--mono); color: var(--text);
@@ -1526,7 +1538,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1526
1538
  .feat-bar-wrap { background: var(--bg-strong); border-radius: 3px; height: 6px; }
1527
1539
  .feat-bar { height: 6px; border-radius: 3px; background: var(--accent); transition: width .3s; }
1528
1540
  .feat-cost { font-family: var(--mono); color: var(--text); text-align: right; }
1529
- .feat-runs { font-size: 11px; color: var(--text3); text-align: right; }
1541
+ .feat-runs { font-size: var(--fs-caption); color: var(--text3); text-align: right; }
1530
1542
 
1531
1543
  /* ── Inbox page ───────────────────────────────────────────────────────────── */
1532
1544
  .inbox-page {
@@ -1559,7 +1571,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1559
1571
  color: var(--text);
1560
1572
  }
1561
1573
  .inbox-summary .pill-stat .lbl {
1562
- font-family: var(--mono); font-size: 10px;
1574
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1563
1575
  letter-spacing: 0.08em; text-transform: uppercase;
1564
1576
  color: var(--text2);
1565
1577
  }
@@ -1586,11 +1598,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1586
1598
  }
1587
1599
  .resume-title {
1588
1600
  font-family: var(--sans); font-weight: 600;
1589
- font-size: 13px; color: var(--text);
1601
+ font-size: var(--fs-body); color: var(--text);
1590
1602
  }
1591
1603
  .resume-meta {
1592
1604
  margin-left: auto;
1593
- font-family: var(--mono); font-size: 10px;
1605
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1594
1606
  letter-spacing: 0.08em; text-transform: uppercase;
1595
1607
  color: var(--text3);
1596
1608
  }
@@ -1599,7 +1611,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1599
1611
  gap: 16px;
1600
1612
  }
1601
1613
  .resume-col-head {
1602
- font-family: var(--mono); font-size: 10px;
1614
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1603
1615
  letter-spacing: 0.08em; text-transform: uppercase;
1604
1616
  color: var(--text3);
1605
1617
  margin-bottom: 8px;
@@ -1610,7 +1622,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1610
1622
  display: flex; flex-direction: column; gap: 8px;
1611
1623
  }
1612
1624
  .resume-list .resume-item {
1613
- font-size: 12px;
1625
+ font-size: var(--fs-small);
1614
1626
  line-height: 1.4;
1615
1627
  color: var(--text2);
1616
1628
  cursor: pointer;
@@ -1635,7 +1647,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1635
1647
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1636
1648
  }
1637
1649
  .resume-list .empty {
1638
- font-size: 12px; color: var(--text3); font-style: italic;
1650
+ font-size: var(--fs-small); color: var(--text3); font-style: italic;
1639
1651
  }
1640
1652
  @media (max-width: 880px) {
1641
1653
  .resume-grid { grid-template-columns: 1fr; gap: 12px; }
@@ -1654,7 +1666,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1654
1666
  font-size: var(--fs-title-s); margin: 0; color: var(--text);
1655
1667
  }
1656
1668
  .inbox-section-head .inbox-count {
1657
- font-family: var(--mono); font-size: 11px;
1669
+ font-family: var(--mono); font-size: var(--fs-caption);
1658
1670
  color: var(--text3); margin-left: 2px;
1659
1671
  }
1660
1672
 
@@ -1680,7 +1692,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1680
1692
  .inbox-row:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-1px); }
1681
1693
  .inbox-row .id {
1682
1694
  padding-top: 2px;
1683
- font-family: var(--mono); font-size: 11px;
1695
+ font-family: var(--mono); font-size: var(--fs-caption);
1684
1696
  color: var(--text3); letter-spacing: 0.04em;
1685
1697
  /* keep a long project-prefixed id inside its own column — never overlap the title */
1686
1698
  min-width: 0; overflow-wrap: anywhere; word-break: break-word;
@@ -1712,16 +1724,20 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1712
1724
  margin-top: 2px;
1713
1725
  }
1714
1726
  .inbox-row .ttl .meta {
1715
- font-family: var(--mono); font-size: 11px;
1727
+ font-family: var(--mono); font-size: var(--fs-caption);
1716
1728
  color: var(--text3); margin-top: 4px;
1717
1729
  }
1718
1730
  .inbox-row .actions {
1719
1731
  display: flex; gap: 4px; align-items: center;
1720
1732
  }
1721
- .inbox-row .actions .gate-btn { height: 26px; padding: 0 10px; font-size: 11px; }
1722
- .budgets-table { width: 100%; border-collapse: collapse; font-size: 13px; }
1733
+ .inbox-row .actions .gate-btn { height: 26px; padding: 0 10px; font-size: var(--fs-caption); }
1734
+ .budgets-failed { padding: 20px 0; max-width: 560px; }
1735
+ .budgets-failed .bf-head { font-size: var(--fs-title-s); font-weight: 600; margin-bottom: 6px; }
1736
+ .budgets-failed .bf-why { font-family: var(--mono); font-size: var(--fs-small); color: var(--status-blocked); margin-bottom: 10px; word-break: break-word; }
1737
+ .budgets-failed .bf-note { font-size: var(--fs-body); color: var(--text2); line-height: 1.5; margin-bottom: 14px; }
1738
+ .budgets-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
1723
1739
  .budgets-table th {
1724
- text-align: left; font-family: var(--mono); font-size: 10px;
1740
+ text-align: left; font-family: var(--mono); font-size: var(--fs-eyebrow);
1725
1741
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--text2);
1726
1742
  padding: 0 12px 8px 0; border-bottom: 1px solid var(--border);
1727
1743
  }
@@ -1730,21 +1746,21 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1730
1746
  over what period. The slug alone answered none of them. */
1731
1747
  .budgets-table .bt-slug { font-family: var(--mono); font-size: var(--fs-small); }
1732
1748
  .budgets-table .bt-desc {
1733
- font-size: 12px; color: var(--text2); margin-top: 2px;
1749
+ font-size: var(--fs-small); color: var(--text2); margin-top: 2px;
1734
1750
  max-width: 52ch; text-wrap: pretty;
1735
1751
  }
1736
1752
  .budgets-table .bt-runs,
1737
1753
  .budgets-table .bt-cap { font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; }
1738
- .budgets-table .bt-spend { font-family: var(--mono); font-size: 12px; white-space: nowrap; }
1754
+ .budgets-table .bt-spend { font-family: var(--mono); font-size: var(--fs-small); white-space: nowrap; }
1739
1755
  .budgets-table .bt-usd { font-variant-numeric: tabular-nums; color: var(--text); }
1740
1756
  .budgets-table th[title] { cursor: help; }
1741
- .budgets-idle summary { cursor: pointer; font-size: 13px; color: var(--text2); }
1757
+ .budgets-idle summary { cursor: pointer; font-size: var(--fs-body); color: var(--text2); }
1742
1758
  .budgets-idle summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
1743
1759
  .budgets-table .bt-actions { text-align: right; white-space: nowrap; }
1744
1760
  .budgets-table .bt-actions .ab-btn + .ab-btn { margin-left: 6px; }
1745
1761
  .budgets-empty {
1746
1762
  padding: 16px; border: 1px dashed var(--border-strong); border-radius: 8px;
1747
- color: var(--text2); font-size: 13px;
1763
+ color: var(--text2); font-size: var(--fs-body);
1748
1764
  }
1749
1765
  .budgets-uncapped { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
1750
1766
  .docs-search { display: flex; align-items: center; gap: 8px; margin: 10px 0 16px; }
@@ -1752,18 +1768,18 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1752
1768
  flex: 1 1 auto; min-width: 0; max-width: 460px;
1753
1769
  height: 32px; padding: 0 10px;
1754
1770
  border: 1px solid var(--border); border-radius: 8px;
1755
- background: var(--bg-card); color: var(--text); font: inherit; font-size: 13px;
1771
+ background: var(--bg-card); color: var(--text); font: inherit; font-size: var(--fs-body);
1756
1772
  }
1757
1773
  .docs-search input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
1758
- .docs-search-hint { font-family: var(--mono); font-size: 10px; color: var(--text3); }
1759
- .docs-search-meta { font-size: 12px; color: var(--text2); margin-bottom: 12px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
1774
+ .docs-search-hint { font-family: var(--mono); font-size: var(--fs-eyebrow); color: var(--text3); }
1775
+ .docs-search-meta { font-size: var(--fs-small); color: var(--text2); margin-bottom: 12px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
1760
1776
  .docs-hit { padding: 10px 0; border-top: 1px solid var(--border); }
1761
- .docs-hit-line { display: flex; gap: 10px; font-family: var(--mono); font-size: 11px; color: var(--text2); margin-top: 4px; }
1777
+ .docs-hit-line { display: flex; gap: 10px; font-family: var(--mono); font-size: var(--fs-caption); color: var(--text2); margin-top: 4px; }
1762
1778
  .docs-hit-no { color: var(--text3); min-width: 34px; text-align: right; }
1763
1779
  .col-more {
1764
1780
  display: block; width: 100%;
1765
1781
  margin-top: 6px; padding: 8px;
1766
- font-family: var(--mono); font-size: 11px;
1782
+ font-family: var(--mono); font-size: var(--fs-caption);
1767
1783
  border: 1px dashed var(--border-strong); border-radius: 8px;
1768
1784
  background: transparent; color: var(--text2);
1769
1785
  }
@@ -1773,14 +1789,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1773
1789
  padding: 14px 16px;
1774
1790
  background: var(--bg-muted);
1775
1791
  border-radius: 8px;
1776
- font-size: 12px; color: var(--text3);
1792
+ font-size: var(--fs-small); color: var(--text3);
1777
1793
  text-align: center;
1778
1794
  }
1779
1795
 
1780
1796
  /* ── Metrics page ─────────────────────────────────────────────────────────── */
1781
1797
  .metrics-page { padding: 24px 32px; }
1782
1798
  .mp-section-label {
1783
- font-family: var(--mono); font-size: 11px;
1799
+ font-family: var(--mono); font-size: var(--fs-caption);
1784
1800
  letter-spacing: 0.1em; text-transform: uppercase;
1785
1801
  color: var(--text3); margin: 0 0 12px;
1786
1802
  }
@@ -1793,7 +1809,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1793
1809
  margin-bottom: 16px;
1794
1810
  }
1795
1811
  .period-label {
1796
- font-family: var(--mono); font-size: 10px;
1812
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1797
1813
  letter-spacing: 0.12em; color: var(--text3);
1798
1814
  }
1799
1815
  .period-chips {
@@ -1804,7 +1820,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1804
1820
  .period-chip {
1805
1821
  background: transparent; border: none; color: var(--text2);
1806
1822
  padding: 5px 12px; border-radius: 5px;
1807
- font-family: var(--mono); font-size: 11px; font-weight: 500;
1823
+ font-family: var(--mono); font-size: var(--fs-caption); font-weight: 500;
1808
1824
  letter-spacing: 0.05em; cursor: pointer;
1809
1825
  transition: background .12s, color .12s;
1810
1826
  }
@@ -1845,23 +1861,23 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1845
1861
  }
1846
1862
  .delta {
1847
1863
  margin-left: 6px;
1848
- font-family: var(--mono); font-size: 10px; font-weight: 600;
1864
+ font-family: var(--mono); font-size: var(--fs-eyebrow); font-weight: 600;
1849
1865
  letter-spacing: 0.02em; white-space: nowrap;
1850
1866
  color: var(--text3); cursor: help;
1851
1867
  }
1852
1868
  .delta.up { color: var(--status-review); }
1853
1869
  .delta.down { color: var(--status-progress); }
1854
1870
  .metric-num small {
1855
- font-size: 22px; color: var(--text2);
1871
+ font-size: var(--fs-title-l); color: var(--text2);
1856
1872
  margin-left: 4px; font-weight: 400;
1857
1873
  }
1858
1874
  .metric-label {
1859
- font-family: var(--mono); font-size: 11px;
1875
+ font-family: var(--mono); font-size: var(--fs-caption);
1860
1876
  letter-spacing: 0.08em; text-transform: uppercase;
1861
1877
  color: var(--text2); margin-top: 14px;
1862
1878
  }
1863
1879
  .metric-trend {
1864
- margin-top: 6px; font-size: 12px; color: var(--text2);
1880
+ margin-top: 6px; font-size: var(--fs-small); color: var(--text2);
1865
1881
  }
1866
1882
 
1867
1883
  .mp-secondary {
@@ -1885,12 +1901,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1885
1901
  .mp-card-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 16px; }
1886
1902
  .mp-card-head h3 { margin: 0; }
1887
1903
  .mp-card-meta {
1888
- font-family: var(--mono); font-size: 10px;
1904
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1889
1905
  color: var(--text3); letter-spacing: 0.06em;
1890
1906
  text-transform: uppercase;
1891
1907
  }
1892
1908
  .feed-cost {
1893
- font-family: var(--mono); font-size: 10px;
1909
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
1894
1910
  color: var(--accent);
1895
1911
  background: var(--accent-soft);
1896
1912
  padding: 2px 6px; border-radius: 4px;
@@ -1915,7 +1931,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1915
1931
  }
1916
1932
  .bar-row .pct {
1917
1933
  text-align: right; color: var(--text2);
1918
- font-family: var(--mono); font-size: 11px;
1934
+ font-family: var(--mono); font-size: var(--fs-caption);
1919
1935
  }
1920
1936
 
1921
1937
  .feed-row {
@@ -1924,7 +1940,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1924
1940
  gap: 12px; align-items: baseline;
1925
1941
  padding: 10px 0;
1926
1942
  border-top: 1px solid var(--border);
1927
- font-size: 13px;
1943
+ font-size: var(--fs-body);
1928
1944
  }
1929
1945
  .feed-row:first-child { border-top: none; }
1930
1946
  .feed-dot {
@@ -1934,16 +1950,16 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1934
1950
  .feed-dot.ok { background: var(--status-review); }
1935
1951
  .feed-dot.fail { background: var(--status-blocked); }
1936
1952
  .feed-dot.def { background: var(--text3); }
1937
- .feed-row .agent { font-family: var(--mono); color: var(--text2); font-size: 12px; }
1953
+ .feed-row .agent { font-family: var(--mono); color: var(--text2); font-size: var(--fs-small); }
1938
1954
  .feed-row .msg { color: var(--text); }
1939
- .feed-row .time { font-family: var(--mono); font-size: 11px; color: var(--text3); }
1955
+ .feed-row .time { font-family: var(--mono); font-size: var(--fs-caption); color: var(--text3); }
1940
1956
 
1941
1957
  /* ── Share page ───────────────────────────────────────────────────────────── */
1942
1958
  /* ── Logs tab ───────────────────────────────────────────── */
1943
1959
  .logs-page { display: flex; flex-direction: column; height: 100%; }
1944
1960
  .logs-header { padding: 20px 24px 12px; border-bottom: 1px solid var(--border); display: flex; align-items: baseline; gap: 16px; }
1945
1961
  .logs-header h2 { margin: 0; font-size: var(--fs-title-l); letter-spacing: -0.02em; }
1946
- .logs-hint { font-size: 12px; color: var(--text3); }
1962
+ .logs-hint { font-size: var(--fs-small); color: var(--text3); }
1947
1963
  .logs-hint code { font-family: var(--mono); background: var(--bg-strong); padding: 1px 5px; border-radius: 3px; }
1948
1964
  .logs-layout { display: flex; flex: 1; overflow: hidden; }
1949
1965
  .logs-list { width: 280px; min-width: 220px; border-right: 1px solid var(--border); overflow-y: auto; padding: 8px 0; flex-shrink: 0; }
@@ -1956,7 +1972,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1956
1972
  min-height: 28px; padding: 0 10px;
1957
1973
  border: 1px solid var(--border); border-radius: 999px;
1958
1974
  background: transparent; color: var(--text2);
1959
- font-family: var(--mono); font-size: 11px; cursor: pointer;
1975
+ font-family: var(--mono); font-size: var(--fs-caption); cursor: pointer;
1960
1976
  }
1961
1977
  .log-facet:hover { border-color: var(--border-strong); }
1962
1978
  .log-facet.on { background: var(--accent-soft); border-color: var(--accent-2); color: var(--accent-text); }
@@ -1965,18 +1981,18 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1965
1981
  .log-item { padding: 10px 16px; cursor: pointer; border-bottom: 1px solid var(--border); }
1966
1982
  .log-item:hover { background: var(--bg-muted); }
1967
1983
  .log-item.active { background: var(--bg-strong); border-left: 2px solid var(--accent); }
1968
- .log-item .log-date { font-size: 11px; color: var(--text3); font-family: var(--mono); }
1969
- .log-item .log-title { font-size: 13px; font-weight: 500; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1970
- .log-item .log-meta { font-size: 11px; color: var(--text3); margin-top: 2px; }
1984
+ .log-item .log-date { font-size: var(--fs-caption); color: var(--text3); font-family: var(--mono); }
1985
+ .log-item .log-title { font-size: var(--fs-body); font-weight: 500; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1986
+ .log-item .log-meta { font-size: var(--fs-caption); color: var(--text3); margin-top: 2px; }
1971
1987
  .log-detail-head { margin-bottom: 20px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
1972
1988
  .log-detail-head h3 { margin: 0 0 4px; font-size: var(--fs-title-m); letter-spacing: -0.01em; }
1973
- .log-detail-head .log-detail-meta { font-size: 12px; color: var(--text3); }
1989
+ .log-detail-head .log-detail-meta { font-size: var(--fs-small); color: var(--text3); }
1974
1990
  .log-section { margin-bottom: 18px; }
1975
- .log-section-label { font-size: 11px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; }
1991
+ .log-section-label { font-size: var(--fs-caption); color: var(--text3); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; }
1976
1992
  .log-bullets { list-style: none; padding: 0; margin: 0; }
1977
- .log-bullets li { padding: 3px 0 3px 12px; position: relative; font-size: 13px; color: var(--text2); }
1993
+ .log-bullets li { padding: 3px 0 3px 12px; position: relative; font-size: var(--fs-body); color: var(--text2); }
1978
1994
  .log-bullets li::before { content: '·'; position: absolute; left: 0; color: var(--accent); }
1979
- .log-empty { font-size: 13px; color: var(--text3); font-style: italic; }
1995
+ .log-empty { font-size: var(--fs-body); color: var(--text3); font-style: italic; }
1980
1996
 
1981
1997
  /* ── Agents installed list (legacy — kept for fallback) ───────────────── */
1982
1998
  .agents-installed { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; margin-top: 4px; }
@@ -1986,9 +2002,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
1986
2002
  .ab-name { font-family: var(--mono, monospace); color: var(--accent); }
1987
2003
  .ab-cap { color: var(--text3); }
1988
2004
  .agent-card { background: var(--bg-muted); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; }
1989
- .agent-card .ac-name { font-size: 12px; font-weight: 600; font-family: var(--mono); color: var(--accent); }
1990
- .agent-card .ac-desc { font-size: 11px; color: var(--text3); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1991
- .agent-card .ac-meta { font-size: 10px; color: var(--text3); margin-top: 5px; display: flex; gap: 8px; }
2005
+ .agent-card .ac-name { font-size: var(--fs-small); font-weight: 600; font-family: var(--mono); color: var(--accent); }
2006
+ .agent-card .ac-desc { font-size: var(--fs-caption); color: var(--text3); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2007
+ .agent-card .ac-meta { font-size: var(--fs-eyebrow); color: var(--text3); margin-top: 5px; display: flex; gap: 8px; }
1992
2008
  .agent-card .ac-model { background: var(--bg-strong); padding: 1px 5px; border-radius: 3px; }
1993
2009
  .agent-card .ac-used { color: var(--status-review); }
1994
2010
  .agent-card .ac-never { color: var(--text3); font-style: italic; }
@@ -2018,17 +2034,17 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2018
2034
  .fleet-search input[type="search"] {
2019
2035
  flex: 1; max-width: 320px;
2020
2036
  background: var(--bg-muted); border: 1px solid var(--border);
2021
- color: var(--text); font-family: var(--mono); font-size: 13px;
2037
+ color: var(--text); font-family: var(--mono); font-size: var(--fs-body);
2022
2038
  padding: 8px 12px; border-radius: 6px;
2023
2039
  }
2024
2040
  .fleet-search input[type="search"]:focus {
2025
2041
  outline: 2px solid var(--focus-ring); outline-offset: 1px;
2026
2042
  border-color: transparent;
2027
2043
  }
2028
- .fleet-search .search-hint { font-size: 11px; color: var(--text3); font-family: var(--mono); }
2044
+ .fleet-search .search-hint { font-size: var(--fs-caption); color: var(--text3); font-family: var(--mono); }
2029
2045
  .fleet-search .search-hint kbd {
2030
2046
  background: var(--bg-muted); border: 1px solid var(--border);
2031
- border-radius: 3px; padding: 1px 5px; font-size: 10px;
2047
+ border-radius: 3px; padding: 1px 5px; font-size: var(--fs-eyebrow);
2032
2048
  }
2033
2049
 
2034
2050
  .fleet-facets { display: flex; flex-direction: column; gap: 6px; }
@@ -2037,12 +2053,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2037
2053
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
2038
2054
  }
2039
2055
  .fleet-facets legend {
2040
- font-size: 11px; color: var(--text3); text-transform: uppercase;
2056
+ font-size: var(--fs-caption); color: var(--text3); text-transform: uppercase;
2041
2057
  letter-spacing: 0.05em; margin-right: 6px; min-width: 60px;
2042
2058
  }
2043
2059
  .facet-chip {
2044
2060
  background: var(--bg-muted); border: 1px solid var(--border);
2045
- color: var(--text2); font-size: 12px; font-family: var(--mono);
2061
+ color: var(--text2); font-size: var(--fs-small); font-family: var(--mono);
2046
2062
  padding: 4px 10px; border-radius: 12px; cursor: pointer;
2047
2063
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
2048
2064
  }
@@ -2056,16 +2072,16 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2056
2072
 
2057
2073
  .fleet-sort {
2058
2074
  display: flex; gap: 8px; align-items: center;
2059
- font-size: 11px; color: var(--text3);
2075
+ font-size: var(--fs-caption); color: var(--text3);
2060
2076
  }
2061
2077
  .fleet-sort select {
2062
2078
  background: var(--bg-muted); border: 1px solid var(--border);
2063
- color: var(--text); font-family: var(--mono); font-size: 12px;
2079
+ color: var(--text); font-family: var(--mono); font-size: var(--fs-small);
2064
2080
  padding: 4px 8px; border-radius: 6px;
2065
2081
  }
2066
2082
  .fleet-sort button {
2067
2083
  background: transparent; border: 1px solid var(--border);
2068
- color: var(--text2); font-size: 11px; font-family: var(--mono);
2084
+ color: var(--text2); font-size: var(--fs-caption); font-family: var(--mono);
2069
2085
  padding: 4px 8px; border-radius: 6px;
2070
2086
  }
2071
2087
  .fleet-sort button:hover { color: var(--text); border-color: var(--border-strong); }
@@ -2102,11 +2118,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2102
2118
  .agent-row.status-unused .ar-status { background: var(--agent-unused); }
2103
2119
  .agent-row.status-retired .ar-status { background: var(--agent-retired); }
2104
2120
  .agent-row .ar-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
2105
- .agent-row .ar-name { font-family: var(--mono); font-size: 13px; color: var(--text); }
2106
- .agent-row .ar-meta { font-size: 11px; color: var(--text3); display: flex; gap: 10px; flex-wrap: wrap; }
2121
+ .agent-row .ar-name { font-family: var(--mono); font-size: var(--fs-body); color: var(--text); }
2122
+ .agent-row .ar-meta { font-size: var(--fs-caption); color: var(--text3); display: flex; gap: 10px; flex-wrap: wrap; }
2107
2123
  .agent-row .ar-meta .ar-domain {
2108
2124
  background: var(--bg-muted); padding: 1px 6px; border-radius: 3px;
2109
- color: var(--text2); font-family: var(--mono); font-size: 10px;
2125
+ color: var(--text2); font-family: var(--mono); font-size: var(--fs-eyebrow);
2110
2126
  }
2111
2127
  .agent-row .ar-rail {
2112
2128
  width: 80px; height: 4px;
@@ -2119,13 +2135,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2119
2135
  }
2120
2136
  .agent-row.status-failing .ar-rail-fill { background: var(--agent-failing); }
2121
2137
  .agent-row .ar-chev {
2122
- color: var(--text3); font-size: 14px; padding: 0 4px;
2138
+ color: var(--text3); font-size: var(--fs-base); padding: 0 4px;
2123
2139
  }
2124
2140
 
2125
2141
  /* Status pill — used in drawer header */
2126
2142
  .status-pill {
2127
2143
  display: inline-flex; align-items: center; gap: 6px;
2128
- font-size: 11px; font-family: var(--mono);
2144
+ font-size: var(--fs-caption); font-family: var(--mono);
2129
2145
  padding: 2px 8px; border-radius: 10px;
2130
2146
  background: var(--bg-muted); border: 1px solid var(--border);
2131
2147
  }
@@ -2171,7 +2187,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2171
2187
  }
2172
2188
  .agent-drawer-close {
2173
2189
  background: transparent; border: 1px solid var(--border);
2174
- color: var(--text2); font-size: 18px; line-height: 1;
2190
+ color: var(--text2); font-size: var(--fs-title-m); line-height: 1;
2175
2191
  width: 28px; height: 28px; border-radius: 6px;
2176
2192
  display: flex; align-items: center; justify-content: center;
2177
2193
  }
@@ -2183,20 +2199,20 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2183
2199
  }
2184
2200
  .agent-drawer-body .mp-card { padding: 12px 14px; }
2185
2201
  .agent-drawer-body h4 {
2186
- font-size: 11px; text-transform: uppercase;
2202
+ font-size: var(--fs-caption); text-transform: uppercase;
2187
2203
  color: var(--text3); letter-spacing: 0.05em;
2188
2204
  margin: 0 0 8px;
2189
2205
  }
2190
2206
  .agent-summary-grid {
2191
2207
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px;
2192
- font-size: 12px;
2208
+ font-size: var(--fs-small);
2193
2209
  }
2194
2210
  .agent-summary-grid dt { color: var(--text3); font-family: var(--mono); }
2195
2211
  .agent-summary-grid dd { color: var(--text); font-family: var(--mono); margin: 0; }
2196
2212
  .run-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
2197
2213
  .run-row {
2198
2214
  display: grid; grid-template-columns: auto auto 1fr auto; gap: 10px;
2199
- font-size: 11px; font-family: var(--mono); color: var(--text2);
2215
+ font-size: var(--fs-caption); font-family: var(--mono); color: var(--text2);
2200
2216
  padding: 4px 0;
2201
2217
  }
2202
2218
  .run-row .rr-verdict.ok { color: var(--agent-ok); }
@@ -2206,15 +2222,15 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2206
2222
  .failure-list { list-style: none; margin: 0; padding: 0; }
2207
2223
  .failure-list li {
2208
2224
  display: flex; gap: 10px; padding: 4px 0;
2209
- font-family: var(--mono); font-size: 12px;
2225
+ font-family: var(--mono); font-size: var(--fs-small);
2210
2226
  }
2211
2227
  .failure-list .fm-key { flex: 1; color: var(--text); }
2212
2228
  .failure-list .fm-count { color: var(--agent-failing); }
2213
- .failure-list .fm-last { color: var(--text3); font-size: 11px; }
2229
+ .failure-list .fm-last { color: var(--text3); font-size: var(--fs-caption); }
2214
2230
  .agent-actions { display: flex; gap: 8px; flex-wrap: wrap; }
2215
2231
  .agent-actions button {
2216
2232
  background: var(--bg-muted); border: 1px solid var(--border);
2217
- color: var(--text); font-family: var(--mono); font-size: 12px;
2233
+ color: var(--text); font-family: var(--mono); font-size: var(--fs-small);
2218
2234
  padding: 6px 12px; border-radius: 6px;
2219
2235
  }
2220
2236
  .agent-actions button:hover { border-color: var(--border-strong); }
@@ -2264,8 +2280,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2264
2280
  margin-bottom: 16px;
2265
2281
  }
2266
2282
  .share-toggle-row .label-grp { flex: 1; }
2267
- .share-toggle-row .label-grp .l { font-weight: 600; font-size: 14px; }
2268
- .share-toggle-row .label-grp .h { color: var(--text2); font-size: 12px; }
2283
+ .share-toggle-row .label-grp .l { font-weight: 600; font-size: var(--fs-base); }
2284
+ .share-toggle-row .label-grp .h { color: var(--text2); font-size: var(--fs-small); }
2269
2285
 
2270
2286
  .notif-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--bg-muted); border: 1px solid var(--border); border-radius: 8px; cursor: pointer; transition: border-color 0.15s; }
2271
2287
  .notif-row:hover { border-color: var(--accent); }
@@ -2311,17 +2327,17 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2311
2327
  .btn-black {
2312
2328
  background: #0f1115; color: #fff; border: none;
2313
2329
  padding: 8px 14px; border-radius: 999px;
2314
- font-size: 12px; font-weight: 500;
2330
+ font-size: var(--fs-small); font-weight: 500;
2315
2331
  }
2316
2332
  .btn-ghost-sm {
2317
2333
  background: transparent;
2318
2334
  border: 1px solid var(--border-strong);
2319
2335
  color: var(--text);
2320
2336
  padding: 8px 12px; border-radius: 999px;
2321
- font-size: 12px;
2337
+ font-size: var(--fs-small);
2322
2338
  }
2323
2339
  .share-note {
2324
- font-size: 13px; color: var(--text2);
2340
+ font-size: var(--fs-body); color: var(--text2);
2325
2341
  margin-top: 16px; line-height: 1.55;
2326
2342
  }
2327
2343
  .share-note strong { color: var(--text); }
@@ -2331,7 +2347,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2331
2347
  padding: 12px 16px;
2332
2348
  background: var(--bg-muted);
2333
2349
  border-radius: 10px;
2334
- font-size: 13px; color: var(--text2);
2350
+ font-size: var(--fs-body); color: var(--text2);
2335
2351
  margin-top: 12px;
2336
2352
  }
2337
2353
  .spinner {
@@ -2345,7 +2361,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2345
2361
 
2346
2362
  .share-badge {
2347
2363
  display: inline-flex; align-items: center; gap: 4px;
2348
- font-size: 10px;
2364
+ font-size: var(--fs-eyebrow);
2349
2365
  padding: 2px 6px; border-radius: 999px;
2350
2366
  font-family: var(--mono);
2351
2367
  text-transform: uppercase;
@@ -2372,7 +2388,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2372
2388
  display: inline-flex; align-items: center; gap: 4px;
2373
2389
  height: 22px; padding: 0 9px;
2374
2390
  border-radius: 999px;
2375
- font-size: 11px; font-weight: 500;
2391
+ font-size: var(--fs-caption); font-weight: 500;
2376
2392
  background: var(--bg-strong); color: var(--text2);
2377
2393
  border: 1px solid var(--border);
2378
2394
  cursor: pointer;
@@ -2386,7 +2402,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2386
2402
  .fchip-clear { background: transparent; color: var(--text3); border-color: transparent; }
2387
2403
  .fchip-clear:hover { background: var(--bg-strong); color: var(--status-blocked); }
2388
2404
  .filter-section-label {
2389
- font-family: var(--mono); font-size: 10px;
2405
+ font-family: var(--mono); font-size: var(--fs-eyebrow);
2390
2406
  letter-spacing: 0.06em; text-transform: uppercase;
2391
2407
  color: var(--text3); padding: 0 4px;
2392
2408
  }
@@ -2397,7 +2413,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2397
2413
  cursor: pointer;
2398
2414
  position: relative;
2399
2415
  }
2400
- .card-status-edit .pill { font-size: 10px; height: 18px; padding: 0 7px; }
2416
+ .card-status-edit .pill { font-size: var(--fs-eyebrow); height: 18px; padding: 0 7px; }
2401
2417
  .card-status-chev {
2402
2418
  color: var(--text3); transition: color 0.12s;
2403
2419
  display: inline-flex; align-items: center;
@@ -2418,7 +2434,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2418
2434
  display: flex; align-items: center; gap: 8px;
2419
2435
  padding: 6px 10px;
2420
2436
  border-radius: 4px;
2421
- font-size: 12px; font-weight: 500;
2437
+ font-size: var(--fs-small); font-weight: 500;
2422
2438
  color: var(--text);
2423
2439
  background: transparent; border: none;
2424
2440
  width: 100%; text-align: left;
@@ -2442,14 +2458,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2442
2458
  .agent-cost-row .name { font-family: var(--mono); color: var(--text); }
2443
2459
  .agent-cost-row .rail { display: block; height: 6px; background: var(--bg-strong); border-radius: 999px; overflow: hidden; }
2444
2460
  .agent-cost-row .fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; min-width: 2px; }
2445
- .agent-cost-row .amt { text-align: right; color: var(--text2); font-family: var(--mono); font-size: 11px; }
2461
+ .agent-cost-row .amt { text-align: right; color: var(--text2); font-family: var(--mono); font-size: var(--fs-caption); }
2446
2462
 
2447
2463
  /* ── Task timeline ────────────────────────────────────────────────────────── */
2448
2464
  .tl-event {
2449
2465
  display: flex; gap: 10px;
2450
2466
  padding: 6px 0;
2451
2467
  border-top: 1px solid var(--border);
2452
- font-size: 12px;
2468
+ font-size: var(--fs-small);
2453
2469
  }
2454
2470
  .tl-event:first-child { border-top: none; }
2455
2471
  .tl-dot {
@@ -2459,15 +2475,15 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2459
2475
  margin-top: 4px; flex-shrink: 0;
2460
2476
  }
2461
2477
  .tl-content { flex: 1; display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px; }
2462
- .tl-actor { font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--accent); }
2478
+ .tl-actor { font-family: var(--mono); font-size: var(--fs-caption); font-weight: 600; color: var(--accent); }
2463
2479
  .tl-action { color: var(--text2); }
2464
2480
  .tl-change {
2465
- font-family: var(--mono); font-size: 11px;
2481
+ font-family: var(--mono); font-size: var(--fs-caption);
2466
2482
  background: var(--bg-strong); color: var(--text);
2467
2483
  padding: 1px 6px; border-radius: 3px;
2468
2484
  }
2469
- .tl-ts { margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--text3); }
2470
- .side-section-head { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text3); margin-bottom: 8px; margin-top: 16px; }
2485
+ .tl-ts { margin-left: auto; font-family: var(--mono); font-size: var(--fs-eyebrow); color: var(--text3); }
2486
+ .side-section-head { font-family: var(--mono); font-size: var(--fs-eyebrow); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text3); margin-bottom: 8px; margin-top: 16px; }
2471
2487
 
2472
2488
  /* ── Security tab: hero KPI tile + activity feed ──────────────────────────── */
2473
2489
  .kpi-hero .kpi-value-hero {
@@ -2475,10 +2491,10 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2475
2491
  font-variant-numeric: tabular-nums;
2476
2492
  font-size: var(--fs-num-l); line-height: 1.1; letter-spacing: -0.02em;
2477
2493
  }
2478
- .kpi-hero .kpi-label { font-size: 13px; font-weight: 500; }
2479
- .kpi-empty .kpi-value { color: var(--accent); font-size: 22px; }
2494
+ .kpi-hero .kpi-label { font-size: var(--fs-body); font-weight: 500; }
2495
+ .kpi-empty .kpi-value { color: var(--accent); font-size: var(--fs-title-l); }
2480
2496
  .kpi-empty .kpi-value::after {
2481
- content: ' all clear'; font-size: 11px; color: var(--text3); font-weight: 400;
2497
+ content: ' all clear'; font-size: var(--fs-caption); color: var(--text3); font-weight: 400;
2482
2498
  }
2483
2499
  @media (max-width: 900px) {
2484
2500
  .kpi-hero { grid-column: span 2; }
@@ -2547,7 +2563,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2547
2563
  display: flex; flex-wrap: wrap;
2548
2564
  gap: 8px 10px; align-items: baseline;
2549
2565
  }
2550
- .inbox-row .ttl { order: 1; flex: 1 1 100%; font-size: 14px; }
2566
+ .inbox-row .ttl { order: 1; flex: 1 1 100%; font-size: var(--fs-base); }
2551
2567
  /* Two lines, wrapped — a phone has the vertical room it lacks sideways. */
2552
2568
  .inbox-row .ttl > :first-child { -webkit-line-clamp: 2; white-space: normal; }
2553
2569
  /* The id and the status share a line: stacked, each took a full row of its
@@ -2594,7 +2610,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2594
2610
  their own size say it after. A button added tomorrow is covered by default;
2595
2611
  one that must stay small has to opt out in writing. */
2596
2612
  button, [role="button"], .nav-item, .toggle, summary { min-height: 44px; }
2597
- .gate-btn, .inbox-row .actions .gate-btn { height: 44px; font-size: 13px; }
2613
+ .gate-btn, .inbox-row .actions .gate-btn { height: 44px; font-size: var(--fs-body); }
2598
2614
  .menu-btn { width: 44px; height: 44px; }
2599
2615
  .btn-bell { min-width: 44px; justify-content: center; }
2600
2616
  .gate-actions, .side-gate-actions, .inbox-row .actions { gap: 12px; }
@@ -2624,7 +2640,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2624
2640
  </svg>
2625
2641
  </span>
2626
2642
  <span class="brand-name">greatcto</span>
2627
- <span id="build-version" title="great_cto build version" style="margin-left:6px;font-size:10px;color:var(--text3);font-family:var(--mono);align-self:center">—</span>
2643
+ <span id="build-version" title="great_cto build version" style="margin-left:6px;font-size:var(--fs-eyebrow);color:var(--text3);font-family:var(--mono);align-self:center">—</span>
2628
2644
  </div>
2629
2645
  <div class="proj-switch" id="proj-switch" onclick="toggleProjMenu(event)">
2630
2646
  <span class="proj-icon" id="proj-switch-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span>
@@ -2709,7 +2725,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2709
2725
  <span class="task-count" id="topbar-task-count">0 ISSUES</span>
2710
2726
  <button class="btn-bell" data-tab="share" onclick="switchTab('share', this)" title="Share a public project report" aria-label="Share" style="gap:6px;padding:6px 10px;">
2711
2727
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
2712
- <span class="btn-label" style="font-size:12px;font-weight:500">Share</span>
2728
+ <span class="btn-label" style="font-size:var(--fs-small);font-weight:500">Share</span>
2713
2729
  <span id="share-nav-badge" class="share-badge off"><span class="share-badge-dot"></span>off</span>
2714
2730
  </button>
2715
2731
  <button class="btn-bell" id="board-theme-btn" onclick="toggleBoardTheme()" title="Toggle light / dark theme" aria-label="Toggle theme">🌙</button>
@@ -2731,7 +2747,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2731
2747
  <div class="inbox-summary" id="inbox-summary"></div>
2732
2748
  <!-- Top rung of the evidence ladder: are the bytes that were reviewed
2733
2749
  still the bytes in the tree? Hidden while they match. -->
2734
- <div id="inbox-receipt" style="display:none;margin:6px 0 2px;font-size:12px"></div>
2750
+ <div id="inbox-receipt" style="display:none;margin:6px 0 2px;font-size:var(--fs-small)"></div>
2735
2751
 
2736
2752
  <!-- Resume — pick up where you left off -->
2737
2753
  <div class="resume-card" id="resume-card" style="display:none">
@@ -2759,9 +2775,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2759
2775
  </div>
2760
2776
 
2761
2777
  <!-- Goal ancestry banner (Paperclip pattern) — shows archetype+compliance+phase -->
2762
- <div id="goal-ancestry-banner" style="display:none;font-family:var(--mono,monospace);font-size:11px;color:var(--text3,#6b7280);background:var(--bg-muted);padding:6px 10px;border-radius:6px;margin-bottom:10px;letter-spacing:0.3px;"></div>
2778
+ <div id="goal-ancestry-banner" style="display:none;font-family:var(--mono,monospace);font-size:var(--fs-caption);color:var(--text3,#6b7280);background:var(--bg-muted);padding:6px 10px;border-radius:6px;margin-bottom:10px;letter-spacing:0.3px;"></div>
2763
2779
  <!-- Tool failure rate watchdog warning -->
2764
- <div id="tool-failure-rate" style="display:none;font-size:12px;color:var(--status-blocked,#f87171);background:rgba(248,113,113,0.08);padding:6px 10px;border-radius:6px;margin-bottom:10px;"></div>
2780
+ <div id="tool-failure-rate" style="display:none;font-size:var(--fs-small);color:var(--status-blocked,#f87171);background:rgba(248,113,113,0.08);padding:6px 10px;border-radius:6px;margin-bottom:10px;"></div>
2765
2781
 
2766
2782
  <div class="inbox-section">
2767
2783
  <div class="inbox-section-head">
@@ -2859,7 +2875,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2859
2875
  project had declared no caps, which is exactly the project that
2860
2876
  needs the button to declare one. -->
2861
2877
  <div class="mp-section-label" style="margin-top:24px">Agent budgets</div>
2862
- <div id="agent-budgets" class="mp-card" style="padding:12px 16px;font-size:12px;margin-bottom:4px;"></div>
2878
+ <div id="agent-budgets" class="mp-card" style="padding:12px 16px;font-size:var(--fs-small);margin-bottom:4px;"></div>
2863
2879
 
2864
2880
  <div class="mp-section-label" id="mp-cost-label" style="margin-top:24px">Cost · last 30 days</div>
2865
2881
  <div class="mp-card cost-panel" id="cost-panel">
@@ -2939,10 +2955,10 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2939
2955
  <section aria-label="Legacy agents" id="legacy-agents-section" style="margin-top:20px" hidden>
2940
2956
  <div class="mp-card">
2941
2957
  <div class="mp-card-head">
2942
- <h3 style="font-size:13px">Legacy / non-canonical sources</h3>
2958
+ <h3 style="font-size:var(--fs-body)">Legacy / non-canonical sources</h3>
2943
2959
  <span class="mp-card-meta" id="legacy-agents-count"></span>
2944
2960
  </div>
2945
- <div style="font-size:12px;color:var(--text3);margin-top:6px">
2961
+ <div style="font-size:var(--fs-small);color:var(--text3);margin-top:6px">
2946
2962
  Verdicts logged under agent names not in <code>~/.claude/agents/great_cto-*.md</code>
2947
2963
  (older log files, custom scripts). Listed in <code>m.legacy_agent_runs</code>; cleanup is a
2948
2964
  one-time housekeeping task.
@@ -3047,7 +3063,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3047
3063
  <button class="btn-black" onclick="copyUrl(event)">Copy</button>
3048
3064
 
3049
3065
  </div>
3050
- <div class="share-meta" id="share-meta" style="font-size:11px;color:var(--text3);margin-top:8px;font-family:var(--mono);"></div>
3066
+ <div class="share-meta" id="share-meta" style="font-size:var(--fs-caption);color:var(--text3);margin-top:8px;font-family:var(--mono);"></div>
3051
3067
  <div class="share-note">
3052
3068
  The report includes shipped tasks, recent activity, agent utilization, and the six headline metrics.
3053
3069
  Open issues, code, and credentials are <strong>never</strong> exposed.
@@ -3082,18 +3098,18 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3082
3098
 
3083
3099
  <!-- Step 1: enter email + send code -->
3084
3100
  <div id="notif-step-email">
3085
- <div style="font-size:11px;color:var(--text3);margin-bottom:4px;font-family:var(--mono);letter-spacing:.05em">RECIPIENT EMAIL</div>
3101
+ <div style="font-size:var(--fs-caption);color:var(--text3);margin-bottom:4px;font-family:var(--mono);letter-spacing:.05em">RECIPIENT EMAIL</div>
3086
3102
  <div style="display:flex;gap:8px;flex-wrap:wrap">
3087
- <input id="notif-to" type="email" placeholder="you@example.com" style="flex:1 1 280px;min-width:240px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:14px" />
3103
+ <input id="notif-to" type="email" placeholder="you@example.com" style="flex:1 1 280px;min-width:240px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:var(--fs-base)" />
3088
3104
  <button class="btn-black" onclick="sendVerifyCode()">Send code</button>
3089
3105
  </div>
3090
3106
  </div>
3091
3107
 
3092
3108
  <!-- Step 2: enter code -->
3093
3109
  <div id="notif-step-code" style="display:none;margin-top:14px">
3094
- <div style="font-size:11px;color:var(--text3);margin-bottom:4px;font-family:var(--mono);letter-spacing:.05em">VERIFICATION CODE (CHECK <span id="notif-code-target">YOUR INBOX</span>)</div>
3110
+ <div style="font-size:var(--fs-caption);color:var(--text3);margin-bottom:4px;font-family:var(--mono);letter-spacing:.05em">VERIFICATION CODE (CHECK <span id="notif-code-target">YOUR INBOX</span>)</div>
3095
3111
  <div style="display:flex;gap:8px;flex-wrap:wrap">
3096
- <input id="notif-code" type="text" inputmode="numeric" pattern="\d{6}" maxlength="6" placeholder="123456" style="flex:1 1 200px;min-width:140px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:18px;font-family:var(--mono);letter-spacing:.2em;text-align:center" />
3112
+ <input id="notif-code" type="text" inputmode="numeric" pattern="\d{6}" maxlength="6" placeholder="123456" style="flex:1 1 200px;min-width:140px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:var(--fs-title-m);font-family:var(--mono);letter-spacing:.2em;text-align:center" />
3097
3113
  <button class="btn-black" onclick="confirmVerifyCode()">Verify</button>
3098
3114
  <button class="btn-ghost-sm" onclick="sendVerifyCode()">Resend code</button>
3099
3115
  </div>
@@ -3101,7 +3117,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3101
3117
 
3102
3118
  <!-- Step 3: verified -->
3103
3119
  <div id="notif-step-verified" style="display:none;margin-top:14px;padding:10px 14px;background:rgba(0,217,126,0.08);border:1px solid rgba(0,217,126,0.30);border-radius:8px;display:none;align-items:center;gap:10px;font-size: var(--fs-body)">
3104
- <span style="color:var(--accent);font-size:18px">✓</span>
3120
+ <span style="color:var(--accent);font-size:var(--fs-title-m)">✓</span>
3105
3121
  <span><strong id="notif-verified-email">—</strong> verified · alerts delivered through <code>greatcto.systems/notify</code></span>
3106
3122
  <button class="btn-ghost-sm" style="margin-left:auto" onclick="changeEmail()">Change</button>
3107
3123
  </div>
@@ -3122,7 +3138,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3122
3138
  <button class="btn-ghost-sm" onclick="testNotifications()">Send test email</button>
3123
3139
  </div>
3124
3140
 
3125
- <div id="notif-status" style="margin-top:14px;font-size:13px;font-family:var(--mono)"></div>
3141
+ <div id="notif-status" style="margin-top:14px;font-size:var(--fs-body);font-family:var(--mono)"></div>
3126
3142
 
3127
3143
  <div class="share-note" style="margin-top:24px">
3128
3144
  Free — up to 100 emails per day per verified email. Delivered through our <code>greatcto.systems/notify</code> relay; your inbox address never leaves your machine without your verification. Local state in <code>~/.great_cto/notifications.json</code>.
@@ -3153,7 +3169,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3153
3169
  <h2 class="section-title">Recent notifications</h2>
3154
3170
  <p class="sub">Last 100 alerts fired this session. Also accessible via the notification drawer (bell icon).</p>
3155
3171
  <div id="notif-history-inline" style="display:flex;flex-direction:column;gap:0;margin-top:12px;
3156
- border:1px solid var(--border);border-radius:8px;overflow:hidden;font-size:13px">
3172
+ border:1px solid var(--border);border-radius:8px;overflow:hidden;font-size:var(--fs-body)">
3157
3173
  <div id="notif-history-empty" style="padding:20px 16px;color:var(--text3);text-align:center">
3158
3174
  No notifications yet this session.
3159
3175
  </div>
@@ -3337,7 +3353,7 @@ function freshnessBadge(doc) {
3337
3353
  const unreadable = doc.freshnessBasis === 'unreadable';
3338
3354
  const colour = v === 'stale' ? 'var(--status-gate)' : 'var(--text3)';
3339
3355
  const label = v === 'stale' ? 'stale' : (unreadable ? 'could not read' : 'unknown');
3340
- return `<span title="${esc(doc.freshnessWhy || '')}" style="font-family:var(--mono);font-size:10px;margin-left:6px;padding:0 4px;border:1px solid var(--border);border-radius:3px;color:${colour}">${esc(label)}</span>`;
3356
+ return `<span title="${esc(doc.freshnessWhy || '')}" style="font-family:var(--mono);font-size:var(--fs-eyebrow);margin-left:6px;padding:0 4px;border:1px solid var(--border);border-radius:3px;color:${colour}">${esc(label)}</span>`;
3341
3357
  }
3342
3358
 
3343
3359
  // Will this gate wait for you, or did the pipeline already go past it?
@@ -3371,7 +3387,7 @@ function gateTierNote(t) {
3371
3387
  // conclusively, which says nothing about the rest of what this agent is for.
3372
3388
  // It is the reading most worth a second look, so it reads differently.
3373
3389
  const caveat = thin ? ' — one eval, coverage unmeasured' : '';
3374
- return `<div style="margin-top:3px;font-size:11px;color:${colour}">
3390
+ return `<div style="margin-top:3px;font-size:var(--fs-caption);color:${colour}">
3375
3391
  <span style="font-family:var(--mono);border:1px solid var(--border);border-radius:3px;padding:0 4px">${esc(info.tier)}</span>
3376
3392
  the pipeline did not wait for this${esc(caveat)} · <span title="${esc(info.why || '')}">${esc((info.why || '').slice(0, 90))}</span>
3377
3393
  </div>`;
@@ -3411,7 +3427,7 @@ async function refreshStandDowns() {
3411
3427
  // unmeasured coverage. That is the row most worth a second look, so it is
3412
3428
  // the row that looks different.
3413
3429
  const thin = r.tier === 'notify-thin';
3414
- const tierTag = `<span style="font-family:var(--mono);font-size:10px;padding:1px 5px;border-radius:3px;border:1px solid var(--border);color:${thin ? 'var(--status-gate)' : 'var(--text3)'}">${esc(r.tier || 'unknown')}</span>`;
3430
+ const tierTag = `<span style="font-family:var(--mono);font-size:var(--fs-eyebrow);padding:1px 5px;border-radius:3px;border:1px solid var(--border);color:${thin ? 'var(--status-gate)' : 'var(--text3)'}">${esc(r.tier || 'unknown')}</span>`;
3415
3431
  return `
3416
3432
  <div class="inbox-row">
3417
3433
  <span class="id">gate:${esc(r.gate || '?')}</span>
@@ -3456,7 +3472,7 @@ function renderInboxList(rootId, items, countId, opts = {}) {
3456
3472
  <button class="gate-btn gate-approve" onclick="event.stopPropagation(); gateAction('${esc(t.id)}', 'approve')">Approve</button>
3457
3473
  <button class="gate-btn gate-reject" onclick="event.stopPropagation(); gateAction('${esc(t.id)}', 'reject')">Reject</button>
3458
3474
  </div>`
3459
- : `<div class="actions"><span style="font-family:var(--mono);font-size:11px;color:var(--text3)">${t.status || ''}</span></div>`;
3475
+ : `<div class="actions"><span style="font-family:var(--mono);font-size:var(--fs-caption);color:var(--text3)">${t.status || ''}</span></div>`;
3460
3476
  const desc = t.description ? `<span class="ttl-desc">${esc(t.description.slice(0, 140))}${t.description.length > 140 ? '…' : ''}</span>` : '';
3461
3477
  // Only gate rows can stand down, so only they carry the note.
3462
3478
  const tierNote = opts.showApprove ? gateTierNote(t) : '';
@@ -3641,11 +3657,32 @@ async function loadBudgetsPage() {
3641
3657
  if (!body) return;
3642
3658
  body.innerHTML = '<div class="muted">loading…</div>';
3643
3659
 
3644
- const [h] = await Promise.all([
3645
- api(`/api/heartbeat${pqs()}`),
3646
- (typeof refreshAgentsInstalled === 'function' && !(fleetState?.agents || []).length)
3647
- ? refreshAgentsInstalled() : Promise.resolve(),
3648
- ]);
3660
+ // Three states, not two: loading | loaded | failed.
3661
+ //
3662
+ // Without this catch the two reads below were awaited bare, so a rejection
3663
+ // returned from the function and left `loading…` on screen permanently. Caught
3664
+ // once in the wild: opening Budgets during startup raced the agent list, the
3665
+ // promise rejected, and the page sat there looking busy. A page that failed
3666
+ // and a page still working are the same picture, and the one you are looking
3667
+ // at is the one where nothing more is coming.
3668
+ let h;
3669
+ try {
3670
+ [h] = await Promise.all([
3671
+ api(`/api/heartbeat${pqs()}`),
3672
+ (typeof refreshAgentsInstalled === 'function' && !(fleetState?.agents || []).length)
3673
+ ? refreshAgentsInstalled() : Promise.resolve(),
3674
+ ]);
3675
+ } catch (err) {
3676
+ body.innerHTML = `
3677
+ <div class="budgets-failed">
3678
+ <div class="bf-head">Could not read the caps.</div>
3679
+ <div class="bf-why">${esc(String(err && err.message || err))}</div>
3680
+ <div class="bf-note">Caps already set are unaffected — this failed to
3681
+ <em>read</em> them, and the pipeline reads them itself, not from here.</div>
3682
+ <button class="btn" onclick="loadBudgetsPage()">Try again</button>
3683
+ </div>`;
3684
+ return;
3685
+ }
3649
3686
  const caps = h?.budgets || {};
3650
3687
  const agents = (fleetState?.agents || []).filter((a) => !a.retired);
3651
3688
 
@@ -3989,7 +4026,7 @@ function showVerdictDetail(idx) {
3989
4026
  <span>${esc(a.path)}</span>
3990
4027
  </a>`;
3991
4028
  }).join('')
3992
- : '<span style="color: var(--text3); font-size: 12px;">No artefacts referenced in this verdict</span>';
4029
+ : '<span style="color: var(--text3); font-size: var(--fs-small);">No artefacts referenced in this verdict</span>';
3993
4030
 
3994
4031
  // Format additional metadata (cost, feature, etc.) as small chips
3995
4032
  const metaKeys = ['feature', 'task', 'cost'];
@@ -4017,7 +4054,7 @@ function showVerdictDetail(idx) {
4017
4054
  <div class="rdm-artefacts-title">Artefacts (click to open in editor)</div>
4018
4055
  ${artefactsHtml}
4019
4056
  </div>
4020
- <div class="rdm-foot">Press <kbd style="background:var(--bg-muted);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:10px;">Esc</kbd> or click outside to close</div>
4057
+ <div class="rdm-foot">Press <kbd style="background:var(--bg-muted);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:var(--fs-eyebrow);">Esc</kbd> or click outside to close</div>
4021
4058
  </div>
4022
4059
  `;
4023
4060
  document.body.appendChild(modal);
@@ -4497,7 +4534,7 @@ async function loadDocs() {
4497
4534
  ? `<div class="card"><b>Pipeline</b> <span class="muted">— how a feature moves, drawn from shared/pipeline.toml</span>
4498
4535
  ${renderDeclaredPipeline(d.pipeline)}
4499
4536
  <details style="margin-top:8px"><summary class="muted">Mermaid source</summary>
4500
- <pre style="white-space:pre-wrap;font-size:11px">${esc(d.pipeline)}</pre></details></div>`
4537
+ <pre style="white-space:pre-wrap;font-size:var(--fs-caption)">${esc(d.pipeline)}</pre></details></div>`
4501
4538
  : '';
4502
4539
 
4503
4540
  const map = d.map ? `<div class="card"><b>Components</b>
@@ -4505,19 +4542,19 @@ async function loadDocs() {
4505
4542
  <div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:10px">
4506
4543
  ${d.map.nodes.map(n => `<div style="border:1px solid var(--border,#ddd);border-radius:6px;padding:8px 10px;min-width:120px">
4507
4544
  <div><b>${esc(n.label)}</b> <span class="muted">${n.count}</span></div>
4508
- <div class="muted" style="font-size:11px">${esc(n.what || '')}</div></div>`).join('')}
4545
+ <div class="muted" style="font-size:var(--fs-caption)">${esc(n.what || '')}</div></div>`).join('')}
4509
4546
  </div>
4510
- ${d.map.edges.length ? `<div class="muted" style="margin-top:8px;font-size:11px">
4547
+ ${d.map.edges.length ? `<div class="muted" style="margin-top:8px;font-size:var(--fs-caption)">
4511
4548
  ${d.map.edges.map(e => `${esc(e.from)}→${esc(e.to)} (${e.count})`).join(' · ')}</div>` : ''}
4512
4549
  <details style="margin-top:8px"><summary class="muted">Mermaid source</summary>
4513
- <pre style="white-space:pre-wrap;font-size:11px">${esc(d.map.mermaid)}</pre></details></div>` : '';
4550
+ <pre style="white-space:pre-wrap;font-size:var(--fs-caption)">${esc(d.map.mermaid)}</pre></details></div>` : '';
4514
4551
 
4515
4552
  const groups = (d.groups || []).map(g => `<div class="card">
4516
4553
  <b>${esc(g.label)}</b> <span class="muted">${g.docs.length} — ${esc(g.why || '')}</span>
4517
4554
  <div style="margin-top:8px">${g.docs.map(doc => `
4518
4555
  <div style="padding:4px 0;border-bottom:1px solid var(--border,#eee)">
4519
4556
  <a href="#" onclick="openDoc('${esc(doc.path)}');return false">${esc(doc.title)}</a>
4520
- <span class="muted" style="font-size:11px"> · ${esc(doc.path)} · ${ago(doc.modified)}</span>
4557
+ <span class="muted" style="font-size:var(--fs-caption)"> · ${esc(doc.path)} · ${ago(doc.modified)}</span>
4521
4558
  ${freshnessBadge(doc)}
4522
4559
  </div>`).join('')}</div>
4523
4560
  </div>`).join('');
@@ -4600,11 +4637,11 @@ async function loadContextLayers() {
4600
4637
 
4601
4638
  const layers = (m.layers || []).map(l => {
4602
4639
  const global = String(l.path || '').includes('/.great_cto/') && !String(l.path || '').includes(projectRootHint());
4603
- const where = global ? ' <span class="muted" style="font-size:11px">shared by every project</span>' : '';
4640
+ const where = global ? ' <span class="muted" style="font-size:var(--fs-caption)">shared by every project</span>' : '';
4604
4641
  return l.exists
4605
4642
  ? `<div style="padding:4px 0;border-bottom:1px solid var(--border,#eee)">
4606
4643
  <a href="#" onclick="openDoc('${esc(l.path)}');return false">${esc(l.id)}</a>${where}
4607
- <span class="muted" style="font-size:11px"> · ${esc(l.path)}</span></div>`
4644
+ <span class="muted" style="font-size:var(--fs-caption)"> · ${esc(l.path)}</span></div>`
4608
4645
  : `<div style="padding:4px 0;border-bottom:1px solid var(--border,#eee)" class="muted">
4609
4646
  ${esc(l.id)} — not written yet${where}</div>`;
4610
4647
  }).join('');
@@ -4648,7 +4685,7 @@ function renderDeclaredPipeline(mermaid) {
4648
4685
  }
4649
4686
  if (!edges.length) return '<div class="muted">no transitions declared</div>';
4650
4687
  const name = (id) => id.replace(/_/g, '-');
4651
- return `<div style="margin-top:10px;font-size:12px">${edges.map(e => `
4688
+ return `<div style="margin-top:10px;font-size:var(--fs-small)">${edges.map(e => `
4652
4689
  <div style="display:flex;align-items:center;gap:8px;padding:3px 0">
4653
4690
  <span style="border:1px solid var(--border,#ddd);border-radius:4px;padding:2px 8px">${esc(name(e.from))}</span>
4654
4691
  <span class="muted">${e.join ? '⋯ join ⋯' : (e.gate ? `→ ${esc(e.gate)} →` : '→')}</span>
@@ -4683,7 +4720,7 @@ async function openDoc(p) {
4683
4720
  const d = await api(`/api/doc?path=${encodeURIComponent(p)}${q ? '&' + q.slice(1) : ''}`);
4684
4721
  if (!d || d.error) throw new Error(d?.error || 'doc not found');
4685
4722
  idEl.textContent = d.name || p;
4686
- body.innerHTML = `<div class="muted" style="font-size:11px;margin-bottom:10px">${esc(d.path)}</div>
4723
+ body.innerHTML = `<div class="muted" style="font-size:var(--fs-caption);margin-bottom:10px">${esc(d.path)}</div>
4687
4724
  <div class="side-desc md">${mdToHtml(d.content)}</div>`;
4688
4725
  } catch (e) {
4689
4726
  body.innerHTML = `<div class="side-desc"><b>Could not open ${esc(p)}</b>
@@ -5549,7 +5586,7 @@ function cardHTML(t) {
5549
5586
  ${t.agent ? `<span class="chip chip-agent">${esc(t.agent)}</span>` : ''}
5550
5587
  ${labels.map(l => `<span class="chip">${esc(l)}</span>`).join('')}
5551
5588
  <div class="card-status-edit" onclick="event.stopPropagation(); openStatusPicker('${esc(t.id)}', this)">
5552
- <span class="pill pill-${t.status || 'backlog'}" style="font-size:10px;height:18px;padding:0 7px">${(t.status || 'backlog').replace('_',' ')}</span>
5589
+ <span class="pill pill-${t.status || 'backlog'}" style="font-size:var(--fs-eyebrow);height:18px;padding:0 7px">${(t.status || 'backlog').replace('_',' ')}</span>
5553
5590
  <span class="card-status-chev"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg></span>
5554
5591
  </div>
5555
5592
  ${elapsed}
@@ -6182,7 +6219,7 @@ function openSide(task) {
6182
6219
  const docsSec = docRefs.length ? `
6183
6220
  <div class="side-section"><div class="side-section-head">Documents</div>
6184
6221
  ${docRefs.map(p => `
6185
- <button onclick="openDocInSide('${esc(p)}')" style="display:flex;width:100%;gap:8px;align-items:center;text-align:left;background:var(--bg2,transparent);border:1px solid var(--line,#ddd);border-radius:6px;padding:6px 10px;margin:4px 0;cursor:pointer;font-family:var(--mono);font-size:12px;color:inherit">
6222
+ <button onclick="openDocInSide('${esc(p)}')" style="display:flex;width:100%;gap:8px;align-items:center;text-align:left;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;padding:6px 10px;margin:4px 0;cursor:pointer;font-family:var(--mono);font-size:var(--fs-small);color:inherit">
6186
6223
  <span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(p)}</span>
6187
6224
  <span style="color:var(--text3)">open →</span>
6188
6225
  </button>`).join('')}
@@ -6585,7 +6622,7 @@ function renderLogsList() {
6585
6622
 
6586
6623
  el.innerHTML = bar + shown.map(({ log, i }) => `
6587
6624
  <div class="log-item ${i === 0 ? 'active' : ''}" onclick="selectLog(${i}, this)">
6588
- <div class="log-date">${esc(log.date)}${log.time ? ' · ' + esc(log.time) : ''}${log.source === 'verdicts' || log.source === 'auto' ? ' <span style="color:var(--text3);font-size:10px">· auto</span>' : ''}</div>
6625
+ <div class="log-date">${esc(log.date)}${log.time ? ' · ' + esc(log.time) : ''}${log.source === 'verdicts' || log.source === 'auto' ? ' <span style="color:var(--text3);font-size:var(--fs-eyebrow)">· auto</span>' : ''}</div>
6589
6626
  <div class="log-title">${esc(log.title)}</div>
6590
6627
  <div class="log-meta">${log.done.length} done · ${log.pending.length} pending${log.duration ? ' · ' + esc(log.duration) : ''}</div>
6591
6628
  </div>`).join('');
@@ -6948,14 +6985,14 @@ function renderAgentProfile(p) {
6948
6985
  <dt>domain</dt> <dd>${esc(p.domain)}</dd>
6949
6986
  <dt>model</dt> <dd>${esc(p.model)}</dd>
6950
6987
  ${p.applies_to.length ? `<dt>applies_to</dt><dd>${esc(p.applies_to.join(', '))}</dd>` : ''}
6951
- ${(p.tools && p.tools.length) ? `<dt>tools</dt><dd>${p.tools.map(t => `<span class="chip" style="font-size:11px">${esc(t)}</span>`).join(' ')}</dd>` : '<dt>tools</dt><dd style="color:var(--text3)">none declared</dd>'}
6952
- ${(p.skills && p.skills.length) ? `<dt>skills</dt><dd>${p.skills.map(s => `<span class="chip chip-agent" style="font-size:11px">${esc(s)}</span>`).join(' ')}</dd>` : ''}
6988
+ ${(p.tools && p.tools.length) ? `<dt>tools</dt><dd>${p.tools.map(t => `<span class="chip" style="font-size:var(--fs-caption)">${esc(t)}</span>`).join(' ')}</dd>` : '<dt>tools</dt><dd style="color:var(--text3)">none declared</dd>'}
6989
+ ${(p.skills && p.skills.length) ? `<dt>skills</dt><dd>${p.skills.map(s => `<span class="chip chip-agent" style="font-size:var(--fs-caption)">${esc(s)}</span>`).join(' ')}</dd>` : ''}
6953
6990
  <dt>runs total</dt> <dd>${p.runs_total} · ${p.runs_30d} in 30d</dd>
6954
6991
  <dt>success</dt> <dd>${p.success_rate != null ? `${p.success_rate}% (${p.ok_30d}/${p.ok_30d + p.fail_30d})` : 'no verdicts in 30d'}</dd>
6955
6992
  <dt>last run</dt> <dd>${p.last_run ? esc(p.last_run.slice(0, 16).replace('T', ' ') + ' · ' + agoStr(p.last_run)) : 'never'}</dd>
6956
6993
  <dt>savings</dt> <dd>${esc(savings)} ${esc(realCost)}</dd>
6957
6994
  </dl>
6958
- ${p.description ? `<div style="font-size:11px;color:var(--text3);margin-top:8px;line-height:1.5">${esc(p.description)}</div>` : ''}
6995
+ ${p.description ? `<div style="font-size:var(--fs-caption);color:var(--text3);margin-top:8px;line-height:1.5">${esc(p.description)}</div>` : ''}
6959
6996
  </div>
6960
6997
 
6961
6998
  <div class="mp-card">
@@ -6993,7 +7030,7 @@ function renderAgentProfile(p) {
6993
7030
  ? `<button type="button" onclick="restoreAgentAction('${esc(p.slug)}')">Restore agent</button>`
6994
7031
  : `<button type="button" class="danger" onclick="retireAgentAction('${esc(p.slug)}')">Retire agent</button>`}
6995
7032
  </div>
6996
- <div style="font-size:11px;color:var(--text3);margin-top:8px">
7033
+ <div style="font-size:var(--fs-caption);color:var(--text3);margin-top:8px">
6997
7034
  File: <code>${esc(p.file_path)}</code>
6998
7035
  </div>
6999
7036
  </div>`;
@@ -7328,12 +7365,12 @@ function renderNotifDrawer() {
7328
7365
  style="padding:12px 16px;border-bottom:1px solid var(--border);
7329
7366
  ${n.read ? '' : 'background:rgba(59,130,246,0.06);'}">
7330
7367
  <div style="display:flex;align-items:flex-start;gap:10px">
7331
- <span style="font-size:15px;flex-shrink:0;margin-top:1px">${_notifLevelIcon(n.level)}</span>
7368
+ <span style="font-size:var(--fs-title-s);flex-shrink:0;margin-top:1px">${_notifLevelIcon(n.level)}</span>
7332
7369
  <div style="flex:1;min-width:0">
7333
- <div style="font-size:13px;font-weight:${n.read ? '400' : '600'};color:var(--text);
7370
+ <div style="font-size:var(--fs-body);font-weight:${n.read ? '400' : '600'};color:var(--text);
7334
7371
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${esc(n.title)}</div>
7335
- <div style="font-size:12px;color:var(--text3);margin-top:2px;line-height:1.4">${esc(n.body)}</div>
7336
- <div style="font-size:11px;color:var(--text3);margin-top:4px;font-family:var(--mono)">
7372
+ <div style="font-size:var(--fs-small);color:var(--text3);margin-top:2px;line-height:1.4">${esc(n.body)}</div>
7373
+ <div style="font-size:var(--fs-caption);color:var(--text3);margin-top:4px;font-family:var(--mono)">
7337
7374
  ${n.project ? `<span style="color:var(--accent);margin-right:6px">${esc(n.project)}</span>` : ''}
7338
7375
  ${new Date(n.ts).toLocaleString()}
7339
7376
  </div>
@@ -7354,12 +7391,12 @@ function renderNotifHistoryInline() {
7354
7391
  el.innerHTML = _notifHistory.slice(0, 20).map(n => `
7355
7392
  <div class="notif-inline-row" style="padding:10px 14px;border-bottom:1px solid var(--border);
7356
7393
  display:flex;align-items:center;gap:10px;${n.read ? '' : 'background:rgba(59,130,246,0.05);'}">
7357
- <span style="font-size:14px">${_notifLevelIcon(n.level)}</span>
7394
+ <span style="font-size:var(--fs-base)">${_notifLevelIcon(n.level)}</span>
7358
7395
  <div style="flex:1;min-width:0">
7359
- <span style="font-size:13px;font-weight:${n.read?'400':'600'}">${esc(n.title)}</span>
7360
- <span style="font-size:12px;color:var(--text3);margin-left:8px">${esc(n.body)}</span>
7396
+ <span style="font-size:var(--fs-body);font-weight:${n.read?'400':'600'}">${esc(n.title)}</span>
7397
+ <span style="font-size:var(--fs-small);color:var(--text3);margin-left:8px">${esc(n.body)}</span>
7361
7398
  </div>
7362
- <span style="font-size:11px;color:var(--text3);font-family:var(--mono);flex-shrink:0">
7399
+ <span style="font-size:var(--fs-caption);color:var(--text3);font-family:var(--mono);flex-shrink:0">
7363
7400
  ${new Date(n.ts).toLocaleTimeString()}
7364
7401
  </span>
7365
7402
  </div>`).join('');
@@ -7519,16 +7556,16 @@ if (_costLbl) _costLbl.textContent = `Cost · last ${periodLabel(currentPeriod)}
7519
7556
  style="width:16px;height:16px;color:var(--text2)">
7520
7557
  <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9 M10.3 21a1.94 1.94 0 0 0 3.4 0"/>
7521
7558
  </svg>
7522
- <span style="font-weight:600;font-size:14px;flex:1;color:var(--text)">Notifications</span>
7559
+ <span style="font-weight:600;font-size:var(--fs-base);flex:1;color:var(--text)">Notifications</span>
7523
7560
  <button class="btn-ghost-sm" onclick="markAllNotifsRead()"
7524
- style="font-size:12px">Mark all read</button>
7561
+ style="font-size:var(--fs-small)">Mark all read</button>
7525
7562
  <button onclick="closeNotifDrawer()" style="background:none;border:none;cursor:pointer;
7526
- color:var(--text3);font-size:22px;line-height:1;padding:0 4px;margin-left:4px">&times;</button>
7563
+ color:var(--text3);font-size:var(--fs-title-l);line-height:1;padding:0 4px;margin-left:4px">&times;</button>
7527
7564
  </div>
7528
7565
  <div id="notif-drawer-list" tabindex="0" role="region" aria-label="Notifications list" style="flex:1;overflow-y:auto"></div>
7529
7566
  <div id="notif-drawer-empty" style="display:flex;flex:1;align-items:center;
7530
- justify-content:center;flex-direction:column;gap:8px;color:var(--text3);font-size:13px">
7531
- <span style="font-size:36px">🔔</span>No notifications yet
7567
+ justify-content:center;flex-direction:column;gap:8px;color:var(--text3);font-size:var(--fs-body)">
7568
+ <span style="font-size:var(--fs-num-l)">🔔</span>No notifications yet
7532
7569
  </div>
7533
7570
  </div>
7534
7571
  <div id="notif-drawer-overlay" onclick="closeNotifDrawer()"