aegis-desktop 1.0.10 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,14 +6,26 @@
6
6
  * corresponding source under this licence (§5/§13) — see LICENSING.md.
7
7
  */
8
8
  :root {
9
- --bg: #0a0e14;
10
- --surface: #12161e;
11
- --surface2: #181e28;
12
- --border: #2a3342;
13
- --border2: #3d4a5e;
14
- --text: #d0dfee;
15
- --text2: #7e9ab8;
16
- --text3: #3a4a60;
9
+ /* Surface ramp: four neutral-cool steps, farthest-back to nearest-front.
10
+ The rule this stylesheet now follows is that no selector picks a grey of
11
+ its own — "a bit lighter than the page" is --surface, never #151a22. That
12
+ is what stops forty near-identical darks accumulating, each one a slightly
13
+ different hue depending on the year it was written, and it is why the
14
+ values below are a ramp rather than five unrelated colours.
15
+
16
+ --bg the page
17
+ --surface a rail resting ON the page: sidebar, topbar
18
+ --surface2 an object ON a rail: composer, lane cards, chips
19
+ --surface3 that object, hovered or pressed */
20
+ --bg: #0b0d11;
21
+ --surface: #10131a;
22
+ --surface2: #171b23;
23
+ --surface3: #1f242e;
24
+ --border: #232934; /* hairline — used once per edge, never doubled */
25
+ --border2: #333b49; /* the same line lifted: hover, focus, emphasis */
26
+ --text: #dbe3ee;
27
+ --text2: #94a2b6;
28
+ --text3: #4c586a;
17
29
  --teal: #00e5c0;
18
30
  --purple: #6b8fd4;
19
31
  --green: #00ff88;
@@ -27,6 +39,47 @@
27
39
  --user: var(--purple);
28
40
  --assistant: var(--surface2);
29
41
 
42
+ /* One radius scale, three steps plus pills. 6px is inline (a chip, a key, a
43
+ small button), 10px is a sitting object (a card, a lane card), 14px is a
44
+ large surface (the composer). The ad-hoc 3/4/8/9/12/16/18px values that
45
+ used to be scattered here rounded to the nearest step: one at a time the
46
+ spread was invisible, all together it read as impatient. */
47
+ --r-sm: 6px;
48
+ --r-md: 10px;
49
+ --r-lg: 14px;
50
+ --r-pill: 999px;
51
+
52
+ /* The reading column: ~72 characters of Ubuntu at 15px. One number governs
53
+ the transcript's prose AND the composer's width, so the input sits exactly
54
+ under the column of text it produces — if these drifted apart the window
55
+ would look assembled from two different grids.
56
+
57
+ Deliberately px, not ch. `ch` resolves against the font-size of whichever
58
+ element uses it, and the transcript sets 14px on `.messages` while the
59
+ composer inherits 14px from <body> and 15px inside the textarea — so the
60
+ same `72ch` would have produced three different widths for the two things
61
+ that must line up. A fixed length is the only spelling that agrees with
62
+ itself in both places. */
63
+ --measure: 640px;
64
+
65
+ /* Spacing, in 4px steps. Named only where a value repeats across sections. */
66
+ --s-1: 4px;
67
+ --s-2: 8px;
68
+ --s-3: 12px;
69
+ --s-4: 16px;
70
+
71
+ /* Two shadows: resting, and lifted. A wide translucent blur reads as depth;
72
+ a tight opaque offset reads as a sticker. */
73
+ --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.28);
74
+ --shadow-2: 0 12px 32px -18px rgba(0, 0, 0, 0.9);
75
+
76
+ /* The accent at the three opacities this sheet actually uses. Naming them
77
+ keeps "teal at 0.35" from being retyped as rgba(0,229,192,.35) eight
78
+ times, which is what made a colour tweak a find-and-replace. */
79
+ --accent-soft: var(--accent-soft);
80
+ --accent-mid: var(--accent-mid);
81
+ --accent-edge: var(--accent-edge);
82
+
30
83
  /* Deterministic stacks. All three named families are vendored in
31
84
  renderer/fonts/ (see fonts/NOTICE.md), so they resolve to the same bytes on
32
85
  every platform and with the network down — the desktop counterpart of the
@@ -148,7 +201,7 @@ body {
148
201
  max-width: 40ch;
149
202
  padding: 2px 8px;
150
203
  border: 1px solid var(--border);
151
- border-radius: 999px;
204
+ border-radius: var(--r-pill);
152
205
  }
153
206
 
154
207
  .session-meter[hidden] {
@@ -195,7 +248,7 @@ body {
195
248
  .slider {
196
249
  position: absolute;
197
250
  inset: 0;
198
- border-radius: 18px;
251
+ border-radius: var(--r-lg);
199
252
  background: var(--border);
200
253
  transition: 0.15s;
201
254
  cursor: pointer;
@@ -248,20 +301,45 @@ main {
248
301
  background: var(--bg);
249
302
  }
250
303
 
304
+ /* NO BOXES.
305
+ A card used to be a filled, bordered, rounded rectangle — nine of them down
306
+ the sidebar, each with its own frame, which made a single scrolling column
307
+ read as a page of unrelated widgets and spent the eye's attention on borders
308
+ that carry no information. The sidebar is one continuous surface now: only a
309
+ hairline between sections, and the heading set as a quiet label rather than
310
+ a panel title.
311
+
312
+ The `.card` class itself stays in the markup, and that is deliberate: it is a
313
+ layout hook, not a decoration. `revealSidebarCard()` in app.js resolves
314
+ `node.closest('section.card')` to find the section it must scroll to when the
315
+ welcome panel sends the user to the key field, and
316
+ desktop/test/renderer-model-key-focus.test.mjs mirrors that selector. Deleting
317
+ the class from the template would silently break that jump; deleting the
318
+ fill, border and radius from the rule does not. */
251
319
  .card {
252
- background: var(--surface2);
253
- border: 1px solid var(--border);
254
- border-radius: 8px;
255
- padding: 12px;
320
+ background: none;
321
+ border: 0;
322
+ border-radius: 0;
323
+ padding: 0;
324
+ }
325
+
326
+ /* The divider replaces the box. It is on the section, not a wrapper, so a
327
+ hidden section (the companion card before it has anything true to say) never
328
+ leaves a floating rule behind. */
329
+ .side .card + .card {
330
+ border-top: 1px solid var(--border);
331
+ padding-top: 12px;
256
332
  }
257
333
 
258
334
  .card h2 {
259
- font-size: 12px;
335
+ font-size: 11px;
260
336
  text-transform: uppercase;
261
- letter-spacing: 0.06em;
337
+ letter-spacing: 0.08em;
262
338
  color: var(--text2);
263
339
  margin: 0 0 8px;
264
340
  font-family: var(--font-display);
341
+ font-weight: 600;
342
+ opacity: 0.75;
265
343
  }
266
344
 
267
345
  .card dl {
@@ -288,7 +366,7 @@ select {
288
366
  background: var(--surface);
289
367
  color: var(--text);
290
368
  border: 1px solid var(--border);
291
- border-radius: 6px;
369
+ border-radius: var(--r-sm);
292
370
  padding: 6px;
293
371
  font-family: var(--font-display);
294
372
  }
@@ -321,7 +399,7 @@ select:disabled {
321
399
  background: var(--surface);
322
400
  color: var(--text);
323
401
  border: 1px solid var(--border);
324
- border-radius: 6px;
402
+ border-radius: var(--r-sm);
325
403
  padding: 5px 8px;
326
404
  font: inherit;
327
405
  font-size: 12px;
@@ -352,7 +430,7 @@ select:disabled {
352
430
  background: var(--surface);
353
431
  color: var(--text);
354
432
  border: 1px solid var(--border);
355
- border-radius: 6px;
433
+ border-radius: var(--r-sm);
356
434
  padding: 4px 6px;
357
435
  font: inherit;
358
436
  font-size: 12px;
@@ -380,7 +458,7 @@ select:disabled {
380
458
  background: var(--surface);
381
459
  color: var(--text);
382
460
  border: 1px solid var(--border);
383
- border-radius: 6px;
461
+ border-radius: var(--r-sm);
384
462
  padding: 6px 8px;
385
463
  font: inherit;
386
464
  }
@@ -392,7 +470,7 @@ select:disabled {
392
470
 
393
471
  .memory-search button {
394
472
  border: none;
395
- border-radius: 6px;
473
+ border-radius: var(--r-sm);
396
474
  background: var(--surface2);
397
475
  color: var(--text);
398
476
  border: 1px solid var(--border);
@@ -420,7 +498,7 @@ select:disabled {
420
498
  .memory-results li {
421
499
  font-size: 12px;
422
500
  border: 1px solid var(--border);
423
- border-radius: 6px;
501
+ border-radius: var(--r-sm);
424
502
  padding: 6px 8px;
425
503
  word-break: break-word;
426
504
  }
@@ -490,7 +568,7 @@ select:disabled {
490
568
  .card-title-btn:focus-visible {
491
569
  outline: 1px solid var(--teal);
492
570
  outline-offset: 2px;
493
- border-radius: 3px;
571
+ border-radius: var(--r-sm);
494
572
  }
495
573
 
496
574
  /* ------------------------------------------------------ memory inspector */
@@ -527,7 +605,7 @@ select:disabled {
527
605
  min-height: 0;
528
606
  background: color-mix(in srgb, var(--surface2) 88%, transparent);
529
607
  border: 1px solid var(--border2);
530
- border-radius: 12px;
608
+ border-radius: var(--r-md);
531
609
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
532
610
  overflow: hidden;
533
611
  }
@@ -571,7 +649,7 @@ select:disabled {
571
649
  background: var(--surface);
572
650
  color: var(--text);
573
651
  border: 1px solid var(--border);
574
- border-radius: 6px;
652
+ border-radius: var(--r-sm);
575
653
  padding: 7px 9px;
576
654
  font: inherit;
577
655
  }
@@ -583,7 +661,7 @@ select:disabled {
583
661
 
584
662
  .memory-filters button {
585
663
  border: 1px solid var(--border);
586
- border-radius: 6px;
664
+ border-radius: var(--r-sm);
587
665
  background: var(--surface);
588
666
  color: var(--text);
589
667
  padding: 0 12px;
@@ -628,7 +706,7 @@ select:disabled {
628
706
  font-size: 10px;
629
707
  line-height: 1.6;
630
708
  padding: 1px 7px;
631
- border-radius: 999px;
709
+ border-radius: var(--r-pill);
632
710
  border: 1px solid var(--border);
633
711
  color: var(--text2);
634
712
  background: var(--surface);
@@ -656,7 +734,7 @@ button.mem-chip.on {
656
734
  }
657
735
 
658
736
  .mem-chip.src {
659
- border-color: rgba(0, 229, 192, 0.45);
737
+ border-color: var(--accent-edge);
660
738
  color: var(--teal);
661
739
  }
662
740
 
@@ -698,7 +776,7 @@ button.mem-chip.on {
698
776
 
699
777
  .mem-card {
700
778
  border: 1px solid var(--border);
701
- border-radius: 8px;
779
+ border-radius: var(--r-sm);
702
780
  background: var(--surface);
703
781
  padding: 9px 11px;
704
782
  }
@@ -749,7 +827,7 @@ button.mem-chip.on {
749
827
  font-size: 12px;
750
828
  color: var(--text2);
751
829
  border: 1px dashed var(--border);
752
- border-radius: 8px;
830
+ border-radius: var(--r-sm);
753
831
  padding: 18px;
754
832
  text-align: center;
755
833
  }
@@ -798,7 +876,7 @@ button.mem-chip.on {
798
876
  background: var(--surface);
799
877
  color: var(--text);
800
878
  border: 1px solid var(--border);
801
- border-radius: 6px;
879
+ border-radius: var(--r-sm);
802
880
  padding: 6px 8px;
803
881
  font: inherit;
804
882
  }
@@ -820,7 +898,7 @@ body.memory-open {
820
898
  background: var(--surface);
821
899
  color: var(--text);
822
900
  border: 1px solid var(--border);
823
- border-radius: 6px;
901
+ border-radius: var(--r-sm);
824
902
  padding: 6px 8px;
825
903
  font: inherit;
826
904
  }
@@ -834,7 +912,7 @@ body.memory-open {
834
912
  width: 100%;
835
913
  margin-top: 6px;
836
914
  border: none;
837
- border-radius: 6px;
915
+ border-radius: var(--r-sm);
838
916
  background: var(--teal);
839
917
  color: #04140f;
840
918
  padding: 6px 0;
@@ -844,7 +922,7 @@ body.memory-open {
844
922
  }
845
923
 
846
924
  .btn-block:hover:not(:disabled) {
847
- box-shadow: 0 0 12px rgba(0, 229, 192, 0.35);
925
+ box-shadow: 0 0 12px var(--accent-mid);
848
926
  }
849
927
 
850
928
  .btn-block:disabled {
@@ -852,43 +930,95 @@ body.memory-open {
852
930
  cursor: default;
853
931
  }
854
932
 
855
- /* ------------------------------------------------------------- messages */
933
+ /* ------------------------------------------------------------- messages
934
+ The transcript is the application. Everything else in this window — the
935
+ rail, the topbar, the composer — is chrome around it, and the layout here
936
+ is what decides whether the window reads as a serious tool or as a chat
937
+ widget.
938
+
939
+ What changed and why (a lift, not a repaint):
940
+
941
+ 1. NO BUBBLES. A turn used to be a rounded 76%-wide box, the user's in a
942
+ saturated purple block with white text. That is the geometry of a
943
+ messaging app, and it costs twice: the coloured block is the loudest
944
+ thing on screen even when the *answer* is what the eye came for, and
945
+ the ragged 76% width puts prose at a measure that changes every turn,
946
+ so nothing settles into a column you can read down.
947
+
948
+ 2. ONE MEASURE, ONE COLUMN. Every turn is full-width inside a single
949
+ reading column (`--measure`), assistant and user alike. Turns are
950
+ separated by a hairline and by space, never by a frame — the same
951
+ no-boxes rule the sidebar adopted, applied to the transcript. The
952
+ reader's eye then has exactly one left edge to return to.
953
+
954
+ 3. AUTHOR LINE, NOT A CHAT HEADER. `.who` was 11px uppercase with 0.7
955
+ opacity sitting *inside* the bubble; it is now a quiet line above the
956
+ prose in the body colour, so it labels the turn without competing
957
+ with it.
958
+ */
856
959
  .messages {
857
960
  flex: 1;
858
961
  overflow-y: auto;
859
- padding: 16px;
962
+ padding: var(--s-4) var(--s-4) var(--s-2);
860
963
  display: flex;
861
964
  flex-direction: column;
862
- gap: 10px;
965
+ gap: 0;
863
966
  min-height: 0;
864
967
  font-family: var(--font-chat);
968
+ /* The reading column is centered in the pane and capped at ~72ch: past
969
+ that a line of prose is a head-turn, under it the column looks stingy.
970
+ The number lives on :root (--measure) so the composer shares it; `auto`
971
+ side margins in a column flex container center each child horizontally,
972
+ and in a narrow window the cap simply stops binding — no media query. */
865
973
  }
866
974
 
867
975
  .msg {
868
- max-width: 76%;
869
- padding: 10px 12px;
870
- border-radius: 10px;
976
+ width: 100%;
977
+ max-width: var(--measure);
978
+ margin: 0 auto;
979
+ /* Horizontal padding is the same on every role, including the assistant
980
+ turn that paints no background. That is deliberate: the moment the user's
981
+ plate is inset and the reply is not, the two turns start on different left
982
+ edges and the column this whole section is built around stops existing. */
983
+ padding: 14px var(--s-3) 16px;
984
+ border: 0;
985
+ border-radius: 0;
986
+ background: none;
987
+ /* The hairline that replaces the bubble: a divider between turns, drawn on
988
+ the TOP of every turn including the first, then suppressed on the first
989
+ one below. One line, one place, no double border between neighbours. */
990
+ border-top: 1px solid var(--border);
991
+ }
992
+
993
+ .msg:first-child {
994
+ border-top: 0;
995
+ padding-top: 0;
871
996
  }
872
997
 
998
+ /* The user's turn is distinguished by typography and a quiet plate, not by a
999
+ saturated fill. `--surface2` is the "object on the page" step of the ramp,
1000
+ so the band is faintly recessed — enough to find while scrolling back,
1001
+ never enough to outshout the answer under it. */
873
1002
  .msg.user {
874
- align-self: flex-end;
875
- background: var(--purple);
876
- color: #fff;
1003
+ align-self: center;
1004
+ background: var(--surface2);
1005
+ color: var(--text);
1006
+ border-radius: var(--r-md);
1007
+ border-top-color: transparent;
877
1008
  }
878
1009
 
879
1010
  .msg.assistant {
880
- align-self: flex-start;
881
- background: var(--surface2);
882
- border: 1px solid var(--border);
1011
+ align-self: center;
1012
+ background: none;
883
1013
  }
884
1014
 
885
1015
  .msg.system {
886
1016
  align-self: center;
887
- background: transparent;
1017
+ background: none;
888
1018
  color: var(--text2);
889
1019
  font-size: 12px;
890
- border: 1px dashed var(--border);
891
- max-width: 92%;
1020
+ border-top: 1px dashed var(--border);
1021
+ max-width: min(var(--measure), 92%);
892
1022
  text-align: center;
893
1023
  }
894
1024
 
@@ -968,23 +1098,35 @@ body.memory-open {
968
1098
  display: none;
969
1099
  }
970
1100
 
1101
+ /* The author line. Uppercase micro-type at 600 weight is the one place this
1102
+ sheet uses it, because it is a label and not prose — but it now sits in
1103
+ --text2 on the page background instead of inside a bubble, and the user's
1104
+ is tinted rather than right-aligned: in a single reading column a ragged
1105
+ right edge on one turn's header breaks the column the whole layout exists
1106
+ to establish. */
971
1107
  .who {
972
1108
  font-size: 11px;
973
1109
  font-weight: 600;
974
1110
  text-transform: uppercase;
975
- letter-spacing: 0.05em;
976
- opacity: 0.7;
977
- margin-bottom: 2px;
1111
+ letter-spacing: 0.07em;
1112
+ color: var(--text2);
1113
+ margin-bottom: var(--s-2);
978
1114
  font-family: var(--font-display);
979
1115
  }
980
1116
 
981
1117
  .msg.user .who {
982
- text-align: right;
1118
+ color: var(--purple);
983
1119
  }
984
1120
 
1121
+ /* Prose. 15px/1.65 is a touch larger than the 14px body default because this
1122
+ is the only long-form text in the window and the measure above is wide
1123
+ enough to carry it; anything smaller at 72ch reads as a log file. */
985
1124
  .body {
986
1125
  white-space: pre-wrap;
987
1126
  word-break: break-word;
1127
+ font-size: 15px;
1128
+ line-height: 1.65;
1129
+ color: var(--text);
988
1130
  }
989
1131
 
990
1132
  /* Tool-call log for a turn (desktop/lib/local/engine.js onDelta's `tool`
@@ -1022,14 +1164,17 @@ body.memory-open {
1022
1164
 
1023
1165
  .meta {
1024
1166
  font-size: 11px;
1025
- color: var(--text2);
1026
- margin-top: 6px;
1167
+ color: var(--text3);
1168
+ margin-top: var(--s-2);
1027
1169
  font-family: var(--font-mono);
1028
1170
  }
1029
1171
 
1172
+ /* Was `rgba(255, 255, 255, 0.75)`, chosen to survive on the purple bubble.
1173
+ The bubble is gone, so a white wash here would be a faint smudge on the
1174
+ page; the user's meta takes the muted step of the ramp like everyone
1175
+ else's. */
1030
1176
  .msg.user .meta {
1031
- color: rgba(255, 255, 255, 0.75);
1032
- text-align: right;
1177
+ color: var(--text3);
1033
1178
  }
1034
1179
 
1035
1180
  /* --------------------------------------------------- discovery lane (D2.2)
@@ -1038,17 +1183,13 @@ body.memory-open {
1038
1183
  beside the answer instead of being buried under it. */
1039
1184
  .chatflow {
1040
1185
  align-self: stretch;
1041
- max-width: 100%;
1042
- margin: 2px 0 6px;
1043
- padding: 8px 10px 10px;
1186
+ max-width: var(--measure);
1187
+ margin: 2px auto var(--s-2);
1188
+ padding: var(--s-2) 10px 10px;
1044
1189
  border: 1px solid var(--border);
1045
1190
  border-left: 2px solid var(--teal);
1046
- border-radius: 10px;
1047
- background: linear-gradient(
1048
- 90deg,
1049
- rgba(0, 229, 192, 0.05),
1050
- rgba(107, 143, 212, 0.05)
1051
- );
1191
+ border-radius: var(--r-md);
1192
+ background: linear-gradient(90deg, var(--accent-soft), rgba(107, 143, 212, 0.05));
1052
1193
  }
1053
1194
 
1054
1195
  .flow-rail {
@@ -1095,7 +1236,7 @@ body.memory-open {
1095
1236
  gap: 6px;
1096
1237
  padding: 9px 11px;
1097
1238
  border: 1px solid var(--border2);
1098
- border-radius: 10px;
1239
+ border-radius: var(--r-md);
1099
1240
  background: var(--surface2);
1100
1241
  min-height: 92px;
1101
1242
  }
@@ -1116,7 +1257,7 @@ body.memory-open {
1116
1257
  }
1117
1258
 
1118
1259
  .flow-card.done {
1119
- border-color: rgba(0, 229, 192, 0.45);
1260
+ border-color: var(--accent-edge);
1120
1261
  }
1121
1262
 
1122
1263
  .flow-card.stopped {
@@ -1212,31 +1353,30 @@ body.memory-open {
1212
1353
  scroll-snap-align: end;
1213
1354
  }
1214
1355
 
1215
- /* Composer side rail: the lane toggle sits above Send so it is visible
1216
- without stealing composer width. */
1217
- .composer-side {
1356
+ /* Composer side rail. Now the composer's own control row (see .composer-row):
1357
+ the discovery toggle, the mode chip and Send on one line under the input. */
1358
+ .composer-row {
1218
1359
  display: flex;
1219
- flex-direction: column;
1220
- align-items: stretch;
1221
- gap: 6px;
1360
+ align-items: center;
1361
+ gap: var(--s-2);
1362
+ /* The top hairline is what makes the row read as *inside* the composer
1363
+ rather than as a second bar attached to its bottom edge. */
1364
+ border-top: 1px solid var(--border);
1365
+ padding-top: var(--s-2);
1366
+ margin-top: var(--s-1);
1222
1367
  }
1223
1368
 
1224
- .composer-side button[type='submit'] {
1369
+ .composer-spacer {
1225
1370
  flex: 1;
1226
- min-height: 34px;
1227
1371
  }
1228
1372
 
1229
- /* Composer mode strip: the tool-approval mode hoisted out of the Settings
1230
- column and under the chat bar, so it is readable while typing. Painted only
1231
- from renderConfirmMode()/renderAutoModeChip() in app.js. */
1232
- .composer-modes {
1233
- display: flex;
1234
- align-items: center;
1235
- gap: 10px;
1236
- padding: 0 16px 10px;
1237
- min-height: 22px;
1373
+ .composer-row button[type='submit'] {
1374
+ flex: none;
1375
+ min-height: 30px;
1238
1376
  }
1239
1377
 
1378
+ /* Composer mode chip: the tool-approval mode, inside the composer. Painted
1379
+ only from renderConfirmMode()/renderAutoModeChip() in app.js. */
1240
1380
  .mode-chip {
1241
1381
  display: inline-flex;
1242
1382
  align-items: center;
@@ -1246,9 +1386,9 @@ body.memory-open {
1246
1386
  font-size: 10.5px;
1247
1387
  line-height: 1.7;
1248
1388
  color: var(--text2);
1249
- background: var(--surface2);
1389
+ background: var(--surface);
1250
1390
  border: 1px solid var(--border);
1251
- border-radius: 999px;
1391
+ border-radius: var(--r-pill);
1252
1392
  cursor: pointer;
1253
1393
  user-select: none;
1254
1394
  white-space: nowrap;
@@ -1274,7 +1414,8 @@ body.memory-open {
1274
1414
  quiet, because the safe state should not look like the exciting one. */
1275
1415
  .mode-chip.is-auto {
1276
1416
  color: var(--teal);
1277
- border-color: var(--teal);
1417
+ border-color: var(--accent-edge);
1418
+ background: var(--accent-soft);
1278
1419
  }
1279
1420
 
1280
1421
  .mode-chip.is-auto .mode-dot {
@@ -1282,10 +1423,14 @@ body.memory-open {
1282
1423
  text-shadow: 0 0 6px var(--teal);
1283
1424
  }
1284
1425
 
1285
- .composer-modes .hint {
1426
+ .composer-row .hint {
1286
1427
  font-size: 10.5px;
1287
1428
  color: var(--text3);
1288
1429
  margin: 0;
1430
+ min-width: 0;
1431
+ overflow: hidden;
1432
+ text-overflow: ellipsis;
1433
+ white-space: nowrap;
1289
1434
  }
1290
1435
 
1291
1436
 
@@ -1372,60 +1517,91 @@ label[hidden] {
1372
1517
  color: var(--teal);
1373
1518
  }
1374
1519
 
1375
- /* ------------------------------------------------------------- composer */
1520
+ /* ------------------------------------------------------------- composer
1521
+ The one elevated object in the window. Everything else is flat: the rail is
1522
+ a surface, turns are a column with hairlines, and this is the thing that
1523
+ sits ON the page, so it is the only element allowed a shadow and the only
1524
+ one whose focus changes its border. That is the whole hierarchy — if the
1525
+ composer is not the most obviously interactive object on screen, the eye
1526
+ does not land on the place it is supposed to type.
1527
+
1528
+ Flex direction is column now, not row: the textarea is the box and the
1529
+ control row (toggle, mode chip, Send) sits on a hairline inside it. */
1376
1530
  .composer {
1377
1531
  display: flex;
1378
- gap: 8px;
1379
- align-items: flex-end;
1380
- padding: 12px;
1381
- margin: 0 auto;
1382
- max-width: 100%;
1383
- width: calc(100% - 24px);
1532
+ flex-direction: column;
1533
+ gap: var(--s-1);
1534
+ align-items: stretch;
1535
+ padding: var(--s-3);
1536
+ margin: 0 auto var(--s-3);
1537
+ max-width: var(--measure);
1538
+ width: calc(100% - 32px);
1384
1539
  border: 1px solid var(--border2);
1385
- border-radius: 16px;
1540
+ border-radius: var(--r-lg);
1386
1541
  background: var(--surface2);
1387
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
1542
+ box-shadow: var(--shadow-1);
1388
1543
  transition: border-color 0.15s, box-shadow 0.15s;
1389
- margin-bottom: 12px;
1390
1544
  }
1391
1545
 
1546
+ /* Focus is communicated by the border and a single soft ring — not by a glow
1547
+ in the accent colour at full strength, which turned the whole box teal and
1548
+ made every keystroke feel like an alert. */
1392
1549
  .composer:focus-within {
1393
- border-color: var(--teal);
1394
- box-shadow: 0 0 0 1px rgba(0, 229, 192, 0.35), 0 6px 22px rgba(0, 229, 192, 0.08);
1550
+ border-color: var(--border2);
1551
+ box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow-2);
1395
1552
  }
1396
1553
 
1397
1554
  .composer textarea {
1398
- flex: 1;
1555
+ /* `flex: 1` lives here from the row-era composer, where it let the input eat
1556
+ the leftover WIDTH. The composer is a column now, so a 0 flex-basis would
1557
+ resolve vertically and collapse the field to its padding: basis `auto`
1558
+ keeps the two rows the markup asks for, and the textarea's own
1559
+ `rows="2"` supplies the height. Growth is no longer its job. */
1560
+ flex: 0 0 auto;
1399
1561
  resize: none;
1400
1562
  background: transparent;
1401
1563
  color: var(--text);
1402
1564
  border: none;
1403
- border-radius: 8px;
1404
- padding: 8px 10px;
1565
+ border-radius: var(--r-sm);
1566
+ padding: var(--s-1) 2px;
1405
1567
  font: inherit;
1406
1568
  font-family: var(--font-chat);
1569
+ font-size: 15px;
1570
+ line-height: 1.6;
1571
+ }
1572
+
1573
+ .composer textarea::placeholder {
1574
+ color: var(--text3);
1407
1575
  }
1408
1576
 
1409
1577
  .composer textarea:focus {
1410
1578
  outline: none;
1411
1579
  }
1412
1580
 
1413
- .composer button {
1581
+ /* Send is filled accent, not a second grey button: it is the terminal action
1582
+ of the only form in the window. */
1583
+ /* Scoped to [type='submit'] on purpose. The mode chip is a <button> inside
1584
+ this form now (it used to sit outside it), and `.composer button` at
1585
+ (0,1,1) outranks `.mode-chip` at (0,1,0) — so an unscoped rule here would
1586
+ have filled the chip with the send accent. Send is the only submit. */
1587
+ .composer button[type='submit'] {
1414
1588
  border: none;
1415
- border-radius: 8px;
1589
+ border-radius: var(--r-sm);
1416
1590
  background: var(--teal);
1417
1591
  color: #04140f;
1418
- padding: 0 18px;
1592
+ padding: 0 16px;
1419
1593
  font: inherit;
1420
1594
  font-weight: 700;
1421
1595
  cursor: pointer;
1596
+ transition: filter 0.15s, box-shadow 0.15s;
1422
1597
  }
1423
1598
 
1424
- .composer button:hover:not(:disabled) {
1425
- box-shadow: 0 0 12px rgba(0, 229, 192, 0.35);
1599
+ .composer button[type='submit']:hover:not(:disabled) {
1600
+ filter: brightness(1.08);
1601
+ box-shadow: 0 0 12px var(--accent-mid);
1426
1602
  }
1427
1603
 
1428
- .composer button:disabled {
1604
+ .composer button[type='submit']:disabled {
1429
1605
  opacity: 0.5;
1430
1606
  cursor: default;
1431
1607
  }
@@ -1440,7 +1616,7 @@ label {
1440
1616
 
1441
1617
  .ghost-btn {
1442
1618
  border: 1px solid var(--border);
1443
- border-radius: 6px;
1619
+ border-radius: var(--r-sm);
1444
1620
  background: var(--surface2);
1445
1621
  color: var(--text);
1446
1622
  padding: 4px 10px;
@@ -1470,7 +1646,7 @@ label {
1470
1646
  background: var(--surface);
1471
1647
  color: var(--text);
1472
1648
  border: 1px solid var(--border);
1473
- border-radius: 6px;
1649
+ border-radius: var(--r-sm);
1474
1650
  padding: 6px;
1475
1651
  font: inherit;
1476
1652
  }
@@ -1483,7 +1659,7 @@ label {
1483
1659
  .cancel-btn {
1484
1660
  margin-top: 8px;
1485
1661
  border: 1px solid var(--red);
1486
- border-radius: 6px;
1662
+ border-radius: var(--r-sm);
1487
1663
  background: transparent;
1488
1664
  color: var(--red);
1489
1665
  padding: 2px 10px;
@@ -1495,7 +1671,7 @@ label {
1495
1671
  .copy-btn {
1496
1672
  margin-top: 8px;
1497
1673
  border: 1px solid var(--teal);
1498
- border-radius: 6px;
1674
+ border-radius: var(--r-sm);
1499
1675
  background: transparent;
1500
1676
  color: var(--teal);
1501
1677
  padding: 2px 10px;
@@ -1522,7 +1698,7 @@ label {
1522
1698
 
1523
1699
  .approval-card {
1524
1700
  border: 1px solid var(--amber);
1525
- border-radius: 8px;
1701
+ border-radius: var(--r-sm);
1526
1702
  background: rgba(255, 145, 0, 0.08);
1527
1703
  padding: 8px 10px;
1528
1704
  }
@@ -1596,7 +1772,7 @@ label {
1596
1772
  white-space: pre;
1597
1773
  background: var(--bg);
1598
1774
  border: 1px solid var(--border);
1599
- border-radius: 6px;
1775
+ border-radius: var(--r-sm);
1600
1776
  padding: 6px 8px;
1601
1777
  margin-bottom: 8px;
1602
1778
  }
@@ -1628,7 +1804,7 @@ label {
1628
1804
  }
1629
1805
 
1630
1806
  .approval-btn {
1631
- border-radius: 6px;
1807
+ border-radius: var(--r-sm);
1632
1808
  padding: 4px 10px;
1633
1809
  font: inherit;
1634
1810
  font-size: 11px;
@@ -1664,16 +1840,21 @@ label {
1664
1840
  }
1665
1841
 
1666
1842
  /* ------------------------------------------------------- settings pane */
1843
+ /* NO BOXES, same rule as .card (see that comment above): each key row used
1844
+ to be its own filled, bordered, rounded rectangle, which re-introduced the
1845
+ boxed-widgets look one level down inside a card the Oct-18/19 session had
1846
+ just de-boxed. A hairline between rows replaces the per-row frame. */
1667
1847
  .setting-row {
1668
- border: 1px solid var(--border);
1669
- border-radius: 6px;
1670
- padding: 8px;
1671
- margin-bottom: 8px;
1848
+ padding: 8px 0;
1672
1849
  display: flex;
1673
1850
  flex-direction: column;
1674
1851
  gap: 6px;
1675
1852
  }
1676
1853
 
1854
+ .setting-row + .setting-row {
1855
+ border-top: 1px solid var(--border);
1856
+ }
1857
+
1677
1858
  .setting-name {
1678
1859
  font-size: 12px;
1679
1860
  font-weight: 600;
@@ -1684,7 +1865,7 @@ label {
1684
1865
  background: var(--surface);
1685
1866
  color: var(--text);
1686
1867
  border: 1px solid var(--border);
1687
- border-radius: 6px;
1868
+ border-radius: var(--r-sm);
1688
1869
  padding: 5px 7px;
1689
1870
  font: inherit;
1690
1871
  font-size: 12px;
@@ -1721,7 +1902,7 @@ label {
1721
1902
  font-size: 12px;
1722
1903
  color: var(--text2);
1723
1904
  border: 1px dashed var(--border);
1724
- border-radius: 6px;
1905
+ border-radius: var(--r-sm);
1725
1906
  padding: 6px 8px;
1726
1907
  }
1727
1908
 
@@ -1744,18 +1925,23 @@ label {
1744
1925
  color: var(--teal);
1745
1926
  }
1746
1927
 
1928
+ /* Sessions, same no-boxes rule as .card: a list of past sessions is a list,
1929
+ not a stack of cards. Each row is a full-width hit target separated by a
1930
+ hairline; hover paints the row's own surface instead of outlining it. */
1747
1931
  .session-row {
1748
1932
  display: flex;
1749
1933
  flex-direction: column;
1750
1934
  gap: 2px;
1751
- border: 1px solid var(--border);
1752
- border-radius: 6px;
1753
- padding: 6px 8px;
1935
+ border: 0;
1936
+ border-left: 2px solid transparent;
1937
+ border-radius: var(--r-sm);
1938
+ padding: 5px 8px;
1754
1939
  cursor: pointer;
1755
1940
  }
1756
1941
 
1757
1942
  .session-row:hover {
1758
- border-color: var(--teal);
1943
+ background: var(--surface2);
1944
+ border-left-color: var(--teal);
1759
1945
  }
1760
1946
 
1761
1947
  .session-title {
@@ -1831,41 +2017,39 @@ label {
1831
2017
  margin: 0;
1832
2018
  }
1833
2019
 
1834
- .chat-quick-actions {
1835
- display: flex;
1836
- flex-wrap: wrap;
1837
- gap: 8px;
1838
- justify-content: center;
1839
- max-width: 560px;
2020
+ /* The one personalized line that replaced the five topic pills. It sits where
2021
+ the "start with one of these" sentence and the pill row used to be, and it
2022
+ carries the same visual weight as .sub so the panel still reads as a greeting
2023
+ with one sentence under it — not a card, not a menu. Wider than .sub because
2024
+ it is a sentence about this install rather than a tagline, and `[hidden]` has
2025
+ to be restated: a class selector outranks the UA `[hidden] { display:none }`,
2026
+ which is the same defect fixed for .welcome-connect below. */
2027
+ .welcome-personal {
2028
+ max-width: 480px;
1840
2029
  margin-top: 2px;
2030
+ color: var(--text2);
1841
2031
  }
1842
2032
 
1843
- .chat-quick-pill {
1844
- display: inline-flex;
1845
- align-items: center;
1846
- gap: 7px;
1847
- padding: 7px 14px;
1848
- border-radius: 999px;
1849
- border: 1px solid var(--border2);
1850
- background: var(--surface);
1851
- color: var(--text2);
1852
- font-size: 12px;
1853
- font-weight: 500;
1854
- cursor: pointer;
1855
- transition: all 0.15s;
1856
- font-family: var(--font-chat);
1857
- white-space: nowrap;
2033
+ .welcome-personal[hidden] {
2034
+ display: none;
1858
2035
  }
1859
2036
 
1860
- .chat-quick-pill:hover {
1861
- border-color: var(--teal);
1862
- color: var(--teal);
1863
- background: rgba(0, 229, 192, 0.06);
2037
+ /* An action line (a downloaded update, a key not yet pasted) is a step the user
2038
+ can take now, so it gets the teal edge the pills used to carry on hover. A
2039
+ tip rotation stays quiet — it is information, not a task. */
2040
+ /* The model's own sentence. It is not a task and not a status, so it gets no
2041
+ teal edge and no button-like weight — just the body colour, so it reads as
2042
+ the first thing the assistant said rather than as another app notice. */
2043
+ .welcome-personal[data-kind='ai'] {
2044
+ color: var(--text);
2045
+ text-align: left;
1864
2046
  }
1865
2047
 
1866
- .chat-quick-pill .qp-ic {
1867
- font-size: 13px;
1868
- line-height: 1;
2048
+ .welcome-personal[data-kind='action'] {
2049
+ color: var(--text2);
2050
+ border-left: 2px solid var(--teal);
2051
+ padding-left: 10px;
2052
+ text-align: left;
1869
2053
  }
1870
2054
 
1871
2055
  .chat-welcome .hints {
@@ -1895,7 +2079,7 @@ label {
1895
2079
  margin-top: 8px;
1896
2080
  padding: 16px 18px;
1897
2081
  border: 1px solid var(--border);
1898
- border-radius: 10px;
2082
+ border-radius: var(--r-md);
1899
2083
  background: var(--surface);
1900
2084
  text-align: left;
1901
2085
  animation: wc-in 0.25s ease-out;
@@ -1991,7 +2175,7 @@ label {
1991
2175
  text-align: left;
1992
2176
  padding: 13px 14px;
1993
2177
  border: 1px solid var(--border2);
1994
- border-radius: 9px;
2178
+ border-radius: var(--r-sm);
1995
2179
  background: var(--surface2);
1996
2180
  color: var(--text2);
1997
2181
  font-family: var(--font-chat);
@@ -2002,7 +2186,7 @@ label {
2002
2186
  .wc-path:hover,
2003
2187
  .wc-path:focus-visible {
2004
2188
  border-color: var(--teal);
2005
- background: rgba(0, 229, 192, 0.06);
2189
+ background: var(--accent-soft);
2006
2190
  outline: none;
2007
2191
  }
2008
2192
 
@@ -2012,7 +2196,7 @@ label {
2012
2196
  existing surface, no new colours, no change to the shared .wc-path rules
2013
2197
  above. */
2014
2198
  .wc-path-primary {
2015
- border-color: rgba(0, 229, 192, 0.45);
2199
+ border-color: var(--accent-edge);
2016
2200
  background: linear-gradient(180deg, rgba(0, 229, 192, 0.07), rgba(0, 229, 192, 0.02));
2017
2201
  }
2018
2202
 
@@ -2235,7 +2419,7 @@ label {
2235
2419
  font-size: 0.92em;
2236
2420
  background: rgba(255, 255, 255, 0.06);
2237
2421
  border: 1px solid var(--border);
2238
- border-radius: 4px;
2422
+ border-radius: var(--r-sm);
2239
2423
  padding: 1px 5px;
2240
2424
  }
2241
2425
 
@@ -2248,7 +2432,7 @@ label {
2248
2432
  padding-top: 26px;
2249
2433
  background: var(--bg);
2250
2434
  border: 1px solid var(--border);
2251
- border-radius: 8px;
2435
+ border-radius: var(--r-sm);
2252
2436
  overflow-x: auto;
2253
2437
  }
2254
2438
 
@@ -2265,7 +2449,7 @@ label {
2265
2449
  top: 6px;
2266
2450
  right: 6px;
2267
2451
  border: 1px solid var(--border2);
2268
- border-radius: 6px;
2452
+ border-radius: var(--r-sm);
2269
2453
  background: var(--surface);
2270
2454
  color: var(--text2);
2271
2455
  padding: 1px 8px;
@@ -2353,15 +2537,15 @@ label {
2353
2537
  pointer-events: none;
2354
2538
  background: linear-gradient(
2355
2539
  to bottom,
2356
- rgba(10, 14, 20, 0.6),
2357
- rgba(10, 14, 20, 0)
2540
+ rgba(11, 13, 17, 0.6),
2541
+ rgba(11, 13, 17, 0)
2358
2542
  );
2359
2543
  opacity: 0;
2360
2544
  transition: opacity 0.18s ease;
2361
2545
  }
2362
2546
 
2363
2547
  body.is-scrolled .topbar {
2364
- background-color: rgba(18, 22, 30, 0.86);
2548
+ background-color: rgba(23, 27, 35, 0.86);
2365
2549
  -webkit-backdrop-filter: blur(12px) saturate(140%);
2366
2550
  backdrop-filter: blur(12px) saturate(140%);
2367
2551
  box-shadow: 0 1px 0 var(--border), 0 14px 34px -22px rgba(0, 0, 0, 0.9);
@@ -2375,7 +2559,7 @@ body.is-scrolled .topbar::after {
2375
2559
  `.composer` already transitions box-shadow (see the composer section), so
2376
2560
  this animates with no new transition to keep in sync. */
2377
2561
  body.is-scrolled .composer {
2378
- box-shadow: 0 18px 40px -26px rgba(0, 0, 0, 0.95);
2562
+ box-shadow: var(--shadow-2);
2379
2563
  }
2380
2564
 
2381
2565
  /* Scrollbars: thin, self-effacing, and only really visible while in use —
@@ -2426,7 +2610,7 @@ body.is-scrolled .composer {
2426
2610
  background-color: var(--border);
2427
2611
  background-clip: padding-box;
2428
2612
  border: 3px solid transparent;
2429
- border-radius: 999px;
2613
+ border-radius: var(--r-pill);
2430
2614
  }
2431
2615
 
2432
2616
  .messages::-webkit-scrollbar-thumb:hover,
@@ -2556,14 +2740,22 @@ body.keys-open {
2556
2740
  padding: 4px 0;
2557
2741
  }
2558
2742
 
2743
+ /* Key rows: a ledger, so rows are separated by a hairline rather than each
2744
+ carrying a frame. `--line` was never defined anywhere in this sheet — the
2745
+ `#2a2f3a` fallback was doing all the work — so it now names the real token. */
2559
2746
  .keys-row {
2560
2747
  display: grid;
2561
2748
  grid-template-columns: 78px 1fr auto;
2562
2749
  align-items: center;
2563
2750
  gap: 10px;
2564
- padding: 10px 12px;
2565
- border: 1px solid var(--line, #2a2f3a);
2566
- border-radius: 10px;
2751
+ padding: 10px 2px;
2752
+ border: 0;
2753
+ border-top: 1px solid var(--border);
2754
+ border-radius: 0;
2755
+ }
2756
+
2757
+ .keys-row:first-child {
2758
+ border-top: 0;
2567
2759
  }
2568
2760
 
2569
2761
  /* A row whose key is already on the machine reads as done and drops back, so
@@ -2578,7 +2770,7 @@ body.keys-open {
2578
2770
  font-size: 12px;
2579
2771
  text-align: center;
2580
2772
  padding: 2px 6px;
2581
- border-radius: 6px;
2773
+ border-radius: var(--r-sm);
2582
2774
  color: #9aa4b2;
2583
2775
  }
2584
2776
 
@@ -2598,3 +2790,187 @@ body.keys-open {
2598
2790
  flex-wrap: wrap;
2599
2791
  padding-top: 6px;
2600
2792
  }
2793
+
2794
+ /* ------------------------------------------------ providers & models card
2795
+ One list, every provider path. The URL line is monospace because it IS an
2796
+ address — proportional text misreads `l`/`1` and `0`/`O` in a hostname. */
2797
+ /* The one bar: provider, model, effort. Laid out as a single row of labelled
2798
+ controls rather than a stack of label-above-select pairs, because these three
2799
+ are read together — the model list is the provider's, the effort applies to
2800
+ whatever is picked. Wraps at narrow widths instead of overflowing; the
2801
+ sidebar is 300px and a fixed row would clip the effort select. */
2802
+ .pt-toolbar {
2803
+ display: flex;
2804
+ flex-wrap: wrap;
2805
+ gap: 6px 8px;
2806
+ align-items: flex-end;
2807
+ margin-bottom: 6px;
2808
+ }
2809
+ .pt-field {
2810
+ display: flex;
2811
+ flex-direction: column;
2812
+ gap: 2px;
2813
+ flex: 1 1 92px;
2814
+ min-width: 0;
2815
+ font-size: 10px;
2816
+ text-transform: uppercase;
2817
+ letter-spacing: 0.08em;
2818
+ color: var(--text2);
2819
+ font-family: var(--font-display);
2820
+ }
2821
+ /* The base `select` rule puts `margin-top: 6px` and `width: 100%` on every
2822
+ select in the app, which inside the bar leaves a floating 6px gap above each
2823
+ control and forces the row to wrap. Reset here only. */
2824
+ .pt-field select {
2825
+ margin-top: 0;
2826
+ padding: 4px 6px;
2827
+ }
2828
+ .pt-toolbar .budget-row {
2829
+ display: flex;
2830
+ flex-direction: column;
2831
+ gap: 2px;
2832
+ flex: 0 0 72px;
2833
+ font-size: 10px;
2834
+ text-transform: uppercase;
2835
+ letter-spacing: 0.08em;
2836
+ color: var(--text2);
2837
+ font-family: var(--font-display);
2838
+ }
2839
+ .pt-toolbar .budget-row select {
2840
+ margin-top: 0;
2841
+ padding: 4px 6px;
2842
+ }
2843
+
2844
+ /* The two cards' split (index.html): the catalog card is key rows, the custom
2845
+ card is an address + key + model id. Its fields are the bar's `.pt-field`
2846
+ stacked one per line — the three go together, so none of them is `flex: 1 1
2847
+ 92px` beside another and wrapping mid-decision. */
2848
+ #custom-provider-card .pt-field {
2849
+ flex: 1 1 100%;
2850
+ margin-bottom: 6px;
2851
+ }
2852
+ #custom-provider-card .pt-field input {
2853
+ margin-top: 0;
2854
+ font-family: var(--font-mono);
2855
+ text-transform: none;
2856
+ letter-spacing: normal;
2857
+ }
2858
+ #custom-provider-card .api-key-row {
2859
+ margin-top: 2px;
2860
+ }
2861
+
2862
+ .pt-bar {
2863
+ display: flex;
2864
+ gap: 8px;
2865
+ align-items: center;
2866
+ margin-bottom: 6px;
2867
+ }
2868
+ .pt-bar input {
2869
+ flex: 1 1 auto;
2870
+ min-width: 0;
2871
+ }
2872
+ .pt-list {
2873
+ list-style: none;
2874
+ margin: 4px 0 0;
2875
+ padding: 0;
2876
+ max-height: 340px;
2877
+ overflow-y: auto;
2878
+ }
2879
+ .pt-group {
2880
+ margin-bottom: 8px;
2881
+ }
2882
+ .pt-group-title {
2883
+ font-size: 11px;
2884
+ text-transform: uppercase;
2885
+ letter-spacing: 0.06em;
2886
+ opacity: 0.65;
2887
+ margin: 4px 0 2px;
2888
+ }
2889
+ .pt-rows {
2890
+ list-style: none;
2891
+ margin: 0;
2892
+ padding: 0;
2893
+ }
2894
+ .pt-row {
2895
+ display: flex;
2896
+ flex-direction: column;
2897
+ gap: 1px;
2898
+ padding: 4px 6px;
2899
+ border-radius: var(--r-sm);
2900
+ border-left: 2px solid transparent;
2901
+ }
2902
+ .pt-row:hover {
2903
+ background: rgba(255, 255, 255, 0.04);
2904
+ border-left-color: currentColor;
2905
+ }
2906
+ /* The clickable row fills its <li>; the browser's default button chrome is
2907
+ reset here rather than using a div, so keyboard focus and Enter work. */
2908
+ .pt-pick {
2909
+ display: flex;
2910
+ flex-direction: column;
2911
+ gap: 1px;
2912
+ width: 100%;
2913
+ text-align: left;
2914
+ background: none;
2915
+ border: 0;
2916
+ padding: 0;
2917
+ color: inherit;
2918
+ font: inherit;
2919
+ cursor: pointer;
2920
+ }
2921
+ .pt-pick:focus-visible {
2922
+ outline: 2px solid currentColor;
2923
+ outline-offset: 2px;
2924
+ }
2925
+ .pt-model {
2926
+ font-weight: 600;
2927
+ }
2928
+ .pt-muted {
2929
+ opacity: 0.6;
2930
+ font-weight: 400;
2931
+ }
2932
+ .pt-id,
2933
+ .pt-target {
2934
+ font-family: var(--font-mono);
2935
+ font-size: 11px;
2936
+ opacity: 0.7;
2937
+ overflow-wrap: anywhere;
2938
+ }
2939
+ .pt-billing,
2940
+ .pt-blocked {
2941
+ font-size: 11px;
2942
+ opacity: 0.6;
2943
+ }
2944
+ /* A blocked row states the reason in the warning colour, but stays in the list:
2945
+ hiding it would answer "what can I address" with a shorter list than reality. */
2946
+ .pt-blocked {
2947
+ color: #f5a524;
2948
+ opacity: 0.95;
2949
+ }
2950
+ .pt-empty {
2951
+ opacity: 0.6;
2952
+ font-size: 12px;
2953
+ padding: 4px 6px;
2954
+ }
2955
+
2956
+ /* ------------------------------------------------------------ daily line */
2957
+ .companion-line {
2958
+ margin: 2px 0 6px;
2959
+ line-height: 1.45;
2960
+ }
2961
+ .companion-row {
2962
+ display: flex;
2963
+ gap: 8px;
2964
+ }
2965
+ /* An 'action' message is something to do, so it reads a shade stronger than a
2966
+ tip. Both are plain text — no badge, no colour that implies urgency. */
2967
+ #companion-card[data-kind='action'] .companion-line {
2968
+ font-weight: 600;
2969
+ }
2970
+ /* Same author-stylesheet-beats-UA-stylesheet gotcha as .update-banner above:
2971
+ .card's `display: grid` (or similar) outranks the UA sheet's
2972
+ `[hidden] { display: none }`, so the card's initial `hidden` in index.html
2973
+ would otherwise be a no-op. */
2974
+ #companion-card[hidden] {
2975
+ display: none;
2976
+ }