great-cto 3.20.0 → 3.22.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.
- package/board/.claude-plugin/plugin.json +15 -8
- package/board/packages/board/lib/alerts.mjs +18 -2
- package/board/packages/board/lib/beads.mjs +13 -2
- package/board/packages/board/lib/data-readers.mjs +59 -2
- package/board/packages/board/lib/digest-policy.mjs +48 -0
- package/board/packages/board/lib/routes.mjs +117 -23
- package/board/packages/board/public/index.html +406 -200
- package/board/packages/board/server.mjs +7 -1
- package/board/scripts/lib/doc-links.mjs +6 -1
- package/board/scripts/lib/gate-reversibility.mjs +118 -0
- package/dist/board-path.js +14 -8
- package/package.json +1 -1
|
@@ -31,14 +31,29 @@
|
|
|
31
31
|
<script src="/assets/vendor/purify.min.js"></script>
|
|
32
32
|
<style>
|
|
33
33
|
:root {
|
|
34
|
+
/* The <meta name="color-scheme"> above is static, so it says "dark" in BOTH
|
|
35
|
+
themes. Every form control that never set its own `color` then takes the
|
|
36
|
+
dark scheme's UA defaults — `buttontext` is white — and in the light theme
|
|
37
|
+
six "Copy re-run" buttons rendered white on white, 1.00:1. The token audit
|
|
38
|
+
could not see it: no declared pair was wrong; the wrong colour was simply
|
|
39
|
+
never declared. This property wins over the meta, and the light block
|
|
40
|
+
below flips it. tests/lib/rendered-contrast.test.mjs reads the pixels. */
|
|
41
|
+
color-scheme: dark;
|
|
34
42
|
/* Colours that used to be written straight into rules.
|
|
35
43
|
Twenty-nine declarations bypassed the token system, so the contrast audit —
|
|
36
44
|
which reads `:root` — could not see any of them. Values are carried across
|
|
37
45
|
unchanged: this is a rename, not a redesign, and the rendered-layout check
|
|
38
46
|
proves it by comparing the colours the page actually paints. */
|
|
39
|
-
|
|
47
|
+
/* Three tokens, because "text on a solid chip" was three different questions
|
|
48
|
+
wearing one name. --on-solid covered the near-black button (#0f1115), the
|
|
49
|
+
green accent and the status colours at once, and #fff is right for exactly
|
|
50
|
+
one of them: white on the accent measures 1.87:1 on dark and 3.82:1 on
|
|
51
|
+
light. Naming the bed each one sits on is what makes the value checkable. */
|
|
52
|
+
--on-solid: #fff; /* on --bg-solid (#0f1115) — 18.90:1 */
|
|
53
|
+
--on-status: #0d1a14; /* on --status-* — bright on dark; the light theme flips it */
|
|
40
54
|
--on-warm: #000; /* text on amber, where white fails contrast */
|
|
41
|
-
--on-accent: #04140d; /*
|
|
55
|
+
--on-accent: #04140d; /* on --accent: 10.10 dark, 4.95 light. White fails both
|
|
56
|
+
(1.87 / 3.82) — the accent is a mid-green, not a dark bed. */
|
|
42
57
|
--bg-solid: #0f1115; /* opaque button and tooltip surface */
|
|
43
58
|
--bg-solid-hover: #2a2d33;
|
|
44
59
|
--accent-hover: #00ee8a;
|
|
@@ -66,7 +81,12 @@
|
|
|
66
81
|
|
|
67
82
|
--text: #ecf2ee;
|
|
68
83
|
--text2: #8a9a92;
|
|
69
|
-
|
|
84
|
+
/* FIND-3. The widest step this ladder allows — 4.53 against the lightest
|
|
85
|
+
surface, where the floor is 4.5. A five-step dark ladder cannot carry
|
|
86
|
+
three clearly distinct ink steps; the light ground can. Two-and-a-bit
|
|
87
|
+
steps is what this surface has, and calling it three would be the same
|
|
88
|
+
defect one layer up. See the commit for the measurements. */
|
|
89
|
+
--text3: #848f88;
|
|
70
90
|
|
|
71
91
|
|
|
72
92
|
/* Focus ring — a separate token from --accent on purpose.
|
|
@@ -78,7 +98,6 @@
|
|
|
78
98
|
--rail-ok-bg: rgba(0, 217, 126, 0.12);
|
|
79
99
|
--rail-fail-bg: rgba(255, 84, 102, 0.12);
|
|
80
100
|
--surface-drawer: #0f1411;
|
|
81
|
-
--drawer-shadow: 0 16px 32px rgba(0, 0, 0, 0.5);
|
|
82
101
|
|
|
83
102
|
--accent: #00d97e;
|
|
84
103
|
--accent-2: #34e69b;
|
|
@@ -89,6 +108,7 @@
|
|
|
89
108
|
--status-todo: #9ca3af;
|
|
90
109
|
--status-progress: #f59e0b;
|
|
91
110
|
--status-review: #00d97e;
|
|
111
|
+
--status-review-text: #00d97e; /* bright on dark: the bed and the ink can be one colour */
|
|
92
112
|
--status-done: #00d97e;
|
|
93
113
|
--status-blocked: #ff5466;
|
|
94
114
|
--status-gate: #a78bfa;
|
|
@@ -98,17 +118,25 @@
|
|
|
98
118
|
--p2-bg: rgba(240, 210, 78, 0.14); --p2-fg: #fde68a;
|
|
99
119
|
--p3-bg: rgba(255, 255, 255, 0.05); --p3-fg: #9ca3af;
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
/* Was `--serif`, and there is no serif anywhere in the stack — it is the
|
|
122
|
+
DISPLAY face, doing display work correctly under a name that promised a
|
|
123
|
+
contrast this page has never had. greatcto.systems already calls the same
|
|
124
|
+
stack `--display`; this is that name arriving here. */
|
|
125
|
+
/* Direction ①: one voice. The display face IS the mono face — rule 4 already
|
|
126
|
+
said mono means machine-truth, and a board whose every figure is a number
|
|
127
|
+
you would paste has no second voice to speak in. Weight caps at 500: the
|
|
128
|
+
vendored axis is 400-500 and hierarchy is size and ink, never bold. */
|
|
129
|
+
--display: "Geist Mono", "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
102
130
|
--sans: "Geist", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
103
131
|
--mono: "Geist Mono", "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
104
|
-
|
|
105
|
-
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.30);
|
|
106
|
-
--shadow-card-hover: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 217, 126, 0.06);
|
|
107
132
|
--accent-text: #00d97e; /* contrast-safe accent for TEXT (bright on dark) */
|
|
108
133
|
--surface-translucent: rgba(17, 22, 26, 0.7); /* translucent card surface (themable) */
|
|
109
134
|
}
|
|
110
135
|
/* Light theme — same emerald system, AA-contrast on white. Toggle via data-theme. */
|
|
111
136
|
[data-theme="light"] {
|
|
137
|
+
/* Flip the UA scheme with the tokens, or every unstyled control stays on the
|
|
138
|
+
dark scheme's defaults (see :root). */
|
|
139
|
+
color-scheme: light;
|
|
112
140
|
/* Four values that had never been measured.
|
|
113
141
|
The contrast audit read `:root` and nothing else, so this theme — 39 token
|
|
114
142
|
overrides, half the board's surface area — was unchecked for months. When it
|
|
@@ -129,17 +157,33 @@
|
|
|
129
157
|
--border-strong: rgba(0, 0, 0, 0.18);
|
|
130
158
|
--text: #0d1a14;
|
|
131
159
|
--text2: #56655e;
|
|
132
|
-
--
|
|
160
|
+
/* Below --text2, which it was not: #55625b measured 6.39 on --bg-card against
|
|
161
|
+
text2's 6.14 — the quietest step was the louder one, so a caption read with
|
|
162
|
+
more emphasis than the secondary line above it. Both cleared AA, so no
|
|
163
|
+
contrast guard could see it; it is a hierarchy defect, not a readability one.
|
|
164
|
+
#5b6b64 is the quietest this ladder allows: 5.62 on --bg-card and 4.52 on
|
|
165
|
+
--bg-strong, the hardest surface here. The step is 0.52 where the dark theme
|
|
166
|
+
gets 0.74 — --bg-strong is what runs out first, the same way the dark
|
|
167
|
+
theme's five surfaces limit its own third step. */
|
|
168
|
+
--text3: #5b6b64;
|
|
133
169
|
--focus-ring: #047857;
|
|
134
170
|
--accent: #009657;
|
|
135
|
-
--accent-2: #
|
|
171
|
+
--accent-2: #097448; /* 4.06 on the approve tint, floor 4.5 → 4.55 */
|
|
136
172
|
--accent-soft: rgba(0, 217, 126, 0.12);
|
|
137
173
|
--accent-strong: rgba(0, 168, 98, 0.20);
|
|
138
|
-
--accent-text: #
|
|
174
|
+
--accent-text: #097547;
|
|
175
|
+
/* The light theme's status colours are dark (#0a7d4d, #b45309, #c43030),
|
|
176
|
+
so the ink that reads on the dark theme's bright ones fails here: 3.45,
|
|
177
|
+
3.56, 3.24. White gives 5.18, 5.02, 5.51. */
|
|
178
|
+
--on-status: #fff;
|
|
139
179
|
--status-backlog: #6b7280;
|
|
140
180
|
--status-todo: #6b7280;
|
|
141
181
|
--status-progress: #b45309;
|
|
142
182
|
--status-review: #0a7d4d;
|
|
183
|
+
/* Same split as --accent / --accent-text: this colour is a BED for tier
|
|
184
|
+
badges and a dot, and INK for approve buttons and budget chips. As ink it
|
|
185
|
+
measures 4.06 on the .12 approve tint it most often sits on. */
|
|
186
|
+
--status-review-text: #097448;
|
|
143
187
|
--status-done: #0a7d4d;
|
|
144
188
|
--status-blocked: #c43030;
|
|
145
189
|
--status-gate: #7c3aed;
|
|
@@ -219,6 +263,10 @@ body {
|
|
|
219
263
|
button, input, select, textarea {
|
|
220
264
|
font-family: inherit;
|
|
221
265
|
font-size: var(--fs-body);
|
|
266
|
+
/* Controls do not inherit `color` by default; they take the UA scheme's.
|
|
267
|
+
Belt to the color-scheme braces above: authored tokens govern text here
|
|
268
|
+
regardless of what scheme the browser thinks it is in. */
|
|
269
|
+
color: inherit;
|
|
222
270
|
}
|
|
223
271
|
button { cursor: pointer; }
|
|
224
272
|
|
|
@@ -290,7 +338,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
290
338
|
margin: 0 0 12px; padding: 10px 14px;
|
|
291
339
|
background: rgba(180, 83, 9, 0.10);
|
|
292
340
|
border: 1px solid var(--status-progress);
|
|
293
|
-
border-radius:
|
|
341
|
+
border-radius: 0;
|
|
294
342
|
font-size: var(--fs-body); color: var(--text);
|
|
295
343
|
}
|
|
296
344
|
.stale-board code { font-family: var(--mono); font-size: var(--fs-small); }
|
|
@@ -301,11 +349,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
301
349
|
board spends its time refusing to render. */
|
|
302
350
|
.budget-chip {
|
|
303
351
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
304
|
-
padding: 1px 6px; border-radius:
|
|
352
|
+
padding: 1px 6px; border-radius: 0;
|
|
305
353
|
border: 1px solid var(--border);
|
|
306
354
|
white-space: nowrap;
|
|
307
355
|
}
|
|
308
|
-
.budget-chip.within { color: var(--status-review); border-color: rgba(22, 163, 74, 0.35); }
|
|
356
|
+
.budget-chip.within { color: var(--status-review-text); border-color: rgba(22, 163, 74, 0.35); }
|
|
309
357
|
.budget-chip.exceeded { color: var(--status-blocked); border-color: rgba(220, 38, 38, 0.35); background: var(--p0-bg); font-weight: 600; }
|
|
310
358
|
.budget-chip.unmeasured { color: var(--status-progress); border-color: rgba(180, 83, 9, 0.35); cursor: help; }
|
|
311
359
|
|
|
@@ -325,7 +373,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
325
373
|
.ab-btn {
|
|
326
374
|
font-family: inherit; font-size: var(--fs-caption);
|
|
327
375
|
min-height: 24px; padding: 0 8px;
|
|
328
|
-
border: 1px solid var(--border); border-radius:
|
|
376
|
+
border: 1px solid var(--border); border-radius: 0;
|
|
329
377
|
background: var(--bg-card); color: var(--text2);
|
|
330
378
|
}
|
|
331
379
|
.ab-btn:hover { border-color: var(--focus-ring); color: var(--text); }
|
|
@@ -335,7 +383,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
335
383
|
|
|
336
384
|
.budget-line {
|
|
337
385
|
margin: -18px 0 24px; padding: 8px 12px;
|
|
338
|
-
border-radius:
|
|
386
|
+
border-radius: 0; font-size: var(--fs-small);
|
|
339
387
|
border: 1px solid var(--border);
|
|
340
388
|
}
|
|
341
389
|
.budget-line.exceeded { color: var(--status-blocked); border-color: var(--status-blocked); background: var(--p0-bg); font-weight: 500; }
|
|
@@ -350,6 +398,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
350
398
|
-webkit-backdrop-filter: blur(20px);
|
|
351
399
|
border-right: 1px solid var(--border);
|
|
352
400
|
display: flex; flex-direction: column;
|
|
401
|
+
border: 1px solid var(--border);
|
|
353
402
|
}
|
|
354
403
|
/* Below this width the fixed 240px sidebar left 135px of workspace on a 375px
|
|
355
404
|
screen — narrower than a single kanban column (264px) — so every visit from a
|
|
@@ -371,7 +420,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
371
420
|
display: none;
|
|
372
421
|
align-items: center; justify-content: center;
|
|
373
422
|
width: 40px; height: 40px; flex-shrink: 0;
|
|
374
|
-
border: 1px solid var(--border); border-radius:
|
|
423
|
+
border: 1px solid var(--border); border-radius: 0;
|
|
375
424
|
background: var(--bg-card); color: var(--text2);
|
|
376
425
|
}
|
|
377
426
|
.menu-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
|
|
@@ -387,14 +436,20 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
387
436
|
.sidebar-brand .brand-mark {
|
|
388
437
|
width: 24px; height: 24px;
|
|
389
438
|
background: var(--bg-page);
|
|
390
|
-
border-radius:
|
|
439
|
+
border-radius: 0;
|
|
391
440
|
display: flex; align-items: center; justify-content: center;
|
|
392
441
|
flex-shrink: 0;
|
|
393
442
|
border: 1px solid var(--border-strong);
|
|
394
443
|
}
|
|
395
444
|
.sidebar-brand .brand-mark svg { width: 22px; height: 22px; }
|
|
396
445
|
.sidebar-brand .brand-name {
|
|
397
|
-
|
|
446
|
+
/* 500, not 600: --display is the mono stack, and Geist Mono's weight axis
|
|
447
|
+
ends at 500. Asking for 600 gets a SYNTHETIC bold — the browser strokes
|
|
448
|
+
around the glyph — and 600 and 700 render identically because of it
|
|
449
|
+
(measured: 1926 ink pixels for both, against a smooth 1473/1598 for
|
|
450
|
+
400/500). The brand already outranks its neighbours by size, 16px against
|
|
451
|
+
12-14, which is the hierarchy this direction asks for. */
|
|
452
|
+
font-family: var(--display); font-weight: 500;
|
|
398
453
|
font-size: var(--fs-title-s); letter-spacing: -0.01em;
|
|
399
454
|
color: var(--text);
|
|
400
455
|
}
|
|
@@ -407,7 +462,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
407
462
|
user-select: none;
|
|
408
463
|
}
|
|
409
464
|
.proj-switch:hover { background: var(--bg-muted); }
|
|
410
|
-
.proj-switch .star { color: var(--accent); font-size: 16px; }
|
|
465
|
+
.proj-switch .star { color: var(--accent-text); font-size: 16px; }
|
|
411
466
|
.proj-switch .name { font-weight: 600; font-size: var(--fs-base); }
|
|
412
467
|
.proj-switch .sw-meta { flex: 1; display: flex; flex-direction: column; gap: 1px; }
|
|
413
468
|
.sw-ancestry[hidden] { display: none; }
|
|
@@ -430,8 +485,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
430
485
|
top: calc(100% - 4px); left: 8px; right: 8px;
|
|
431
486
|
background: var(--bg-card);
|
|
432
487
|
border: 1px solid var(--border-strong);
|
|
433
|
-
border-radius:
|
|
434
|
-
box-shadow:
|
|
488
|
+
border-radius: 0;
|
|
489
|
+
box-shadow: none;
|
|
435
490
|
padding: 4px;
|
|
436
491
|
z-index: 100;
|
|
437
492
|
display: none;
|
|
@@ -452,13 +507,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
452
507
|
.proj-item {
|
|
453
508
|
display: flex; align-items: center; gap: 8px;
|
|
454
509
|
padding: 8px 10px;
|
|
455
|
-
border-radius:
|
|
510
|
+
border-radius: 0;
|
|
456
511
|
cursor: pointer;
|
|
457
512
|
font-size: var(--fs-body);
|
|
458
513
|
}
|
|
459
514
|
.proj-item:hover { background: var(--bg-muted); }
|
|
460
515
|
.proj-item.active { background: var(--bg-strong); font-weight: 500; }
|
|
461
|
-
.proj-item .star { color: var(--accent); font-size: var(--fs-base); }
|
|
516
|
+
.proj-item .star { color: var(--accent-text); font-size: var(--fs-base); }
|
|
462
517
|
.proj-icon, .proj-switch .proj-icon, .crumbs .proj-icon {
|
|
463
518
|
display: inline-flex; align-items: center; justify-content: center;
|
|
464
519
|
flex-shrink: 0;
|
|
@@ -475,7 +530,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
475
530
|
}
|
|
476
531
|
.proj-item .check {
|
|
477
532
|
width: 14px; height: 14px;
|
|
478
|
-
color: var(--accent);
|
|
533
|
+
color: var(--accent-text);
|
|
479
534
|
}
|
|
480
535
|
.proj-item:not(.active) .check { display: none; }
|
|
481
536
|
.proj-menu-empty {
|
|
@@ -489,7 +544,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
489
544
|
.nav-item {
|
|
490
545
|
display: flex; align-items: center; gap: 10px;
|
|
491
546
|
height: 32px; padding: 0 10px;
|
|
492
|
-
border-radius:
|
|
547
|
+
border-radius: 0;
|
|
493
548
|
font-size: var(--fs-body);
|
|
494
549
|
color: var(--text);
|
|
495
550
|
cursor: pointer;
|
|
@@ -504,10 +559,10 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
504
559
|
left: -8px; top: 6px; bottom: 6px;
|
|
505
560
|
width: 3px;
|
|
506
561
|
background: var(--accent);
|
|
507
|
-
border-radius: 0
|
|
562
|
+
border-radius: 0;
|
|
508
563
|
}
|
|
509
564
|
.nav-item .icon { width: 16px; height: 16px; color: var(--text2); flex-shrink: 0; }
|
|
510
|
-
.nav-item.active .icon { color: var(--accent); }
|
|
565
|
+
.nav-item.active .icon { color: var(--accent-text); }
|
|
511
566
|
.nav-item .count { margin-left: auto; font-size: var(--fs-caption); color: var(--text3); font-family: var(--mono); }
|
|
512
567
|
.nav-divider { height: 1px; background: var(--border); margin: 12px 8px; }
|
|
513
568
|
|
|
@@ -538,7 +593,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
538
593
|
font-size: var(--fs-body);
|
|
539
594
|
}
|
|
540
595
|
.crumbs { display: flex; align-items: center; gap: 8px; color: var(--text2); }
|
|
541
|
-
.crumbs .star { color: var(--accent); }
|
|
596
|
+
.crumbs .star { color: var(--accent-text); }
|
|
542
597
|
.crumbs .sep { color: var(--text3); }
|
|
543
598
|
.crumbs .here { color: var(--text); font-weight: 500; }
|
|
544
599
|
|
|
@@ -546,7 +601,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
546
601
|
display: flex; align-items: center; gap: 6px;
|
|
547
602
|
background: var(--bg-muted);
|
|
548
603
|
border: 1px solid var(--border);
|
|
549
|
-
border-radius:
|
|
604
|
+
border-radius: 0;
|
|
550
605
|
padding: 4px 10px;
|
|
551
606
|
width: 280px;
|
|
552
607
|
margin: 0 16px;
|
|
@@ -564,13 +619,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
564
619
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
565
620
|
color: var(--text3);
|
|
566
621
|
background: var(--bg-strong);
|
|
567
|
-
border-radius:
|
|
622
|
+
border-radius: 0; padding: 1px 5px;
|
|
568
623
|
}
|
|
569
624
|
|
|
570
625
|
.topbar-actions { display: flex; align-items: center; gap: 6px; }
|
|
571
626
|
.btn-bell {
|
|
572
627
|
position: relative; background: transparent;
|
|
573
|
-
border: 1px solid var(--border); border-radius:
|
|
628
|
+
border: 1px solid var(--border); border-radius: 0;
|
|
574
629
|
padding: 6px 8px; cursor: pointer; color: var(--text2);
|
|
575
630
|
display: flex; align-items: center; transition: border-color .15s, color .15s;
|
|
576
631
|
}
|
|
@@ -579,7 +634,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
579
634
|
position: absolute; top: -5px; right: -5px;
|
|
580
635
|
background: var(--accent); color: var(--on-accent);
|
|
581
636
|
font-size: var(--fs-eyebrow); font-weight: 700; line-height: 1;
|
|
582
|
-
min-width: 16px; height: 16px; border-radius:
|
|
637
|
+
min-width: 16px; height: 16px; border-radius: 0;
|
|
583
638
|
display: none; align-items: center; justify-content: center;
|
|
584
639
|
padding: 0 3px;
|
|
585
640
|
}
|
|
@@ -587,7 +642,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
587
642
|
.tab-row { display: flex; align-items: center; gap: 4px; margin-right: 12px; }
|
|
588
643
|
.tab-btn {
|
|
589
644
|
background: transparent; border: none;
|
|
590
|
-
padding: 6px 12px; border-radius:
|
|
645
|
+
padding: 6px 12px; border-radius: 0;
|
|
591
646
|
font-size: var(--fs-body); color: var(--text2);
|
|
592
647
|
}
|
|
593
648
|
.tab-btn:hover { background: var(--bg-muted); color: var(--text); }
|
|
@@ -599,7 +654,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
599
654
|
.icon-btn {
|
|
600
655
|
background: transparent; border: 1px solid transparent;
|
|
601
656
|
width: 30px; height: 30px;
|
|
602
|
-
border-radius:
|
|
657
|
+
border-radius: 0;
|
|
603
658
|
display: inline-flex; align-items: center; justify-content: center;
|
|
604
659
|
color: var(--text2);
|
|
605
660
|
}
|
|
@@ -637,7 +692,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
637
692
|
backdrop-filter: blur(12px);
|
|
638
693
|
-webkit-backdrop-filter: blur(12px);
|
|
639
694
|
border: 1px solid var(--border);
|
|
640
|
-
border-radius:
|
|
695
|
+
border-radius: 0;
|
|
641
696
|
padding: 10px;
|
|
642
697
|
display: flex; flex-direction: column; gap: 8px;
|
|
643
698
|
max-height: 100%;
|
|
@@ -666,7 +721,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
666
721
|
.card {
|
|
667
722
|
background: var(--bg-card);
|
|
668
723
|
border: 1px solid var(--border);
|
|
669
|
-
border-radius:
|
|
724
|
+
border-radius: 0;
|
|
670
725
|
padding: 10px 12px;
|
|
671
726
|
cursor: pointer;
|
|
672
727
|
transition: box-shadow 0.15s, transform 0.15s;
|
|
@@ -686,7 +741,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
686
741
|
overflow: hidden;
|
|
687
742
|
text-overflow: ellipsis;
|
|
688
743
|
}
|
|
689
|
-
.card:hover { box-shadow:
|
|
744
|
+
.card:hover { box-shadow: none; transform: translateY(-1px); }
|
|
690
745
|
.card.selected { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
691
746
|
.card .id {
|
|
692
747
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
@@ -748,7 +803,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
748
803
|
display: inline-flex; align-items: center; justify-content: center; gap: 4px;
|
|
749
804
|
height: 24px;
|
|
750
805
|
border: 1px solid var(--border);
|
|
751
|
-
border-radius:
|
|
806
|
+
border-radius: 0;
|
|
752
807
|
font-family: inherit; font-size: var(--fs-caption); font-weight: 500;
|
|
753
808
|
cursor: pointer;
|
|
754
809
|
background: var(--bg-card);
|
|
@@ -761,26 +816,26 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
761
816
|
makes a long gate list scannable is left exactly as it is. 44px is the iOS
|
|
762
817
|
minimum; Android asks for 48dp, and the gap does the rest. */
|
|
763
818
|
|
|
764
|
-
.gate-approve { color: var(--status-review); border-color: rgba(22, 163, 74, 0.35); }
|
|
819
|
+
.gate-approve { color: var(--status-review-text); border-color: rgba(22, 163, 74, 0.35); }
|
|
765
820
|
.gate-approve:hover { background: rgba(22, 163, 74, 0.08); border-color: var(--status-review); }
|
|
766
821
|
/* Agent runner (launch control) */
|
|
767
|
-
#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:
|
|
822
|
+
#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: 0; box-shadow: none; z-index: 60; overflow: hidden; }
|
|
768
823
|
#agent-panel.open { display: flex; }
|
|
769
824
|
#agent-panel header { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border, #1f2a26); background: transparent; }
|
|
770
825
|
#agent-panel header b { font-size: var(--fs-body); }
|
|
771
826
|
#agent-panel .ap-x { margin-left: auto; cursor: pointer; border: 0; background: none; color: var(--text3); font-size: 16px; }
|
|
772
827
|
#agent-log { flex: 1; overflow: auto; padding: 10px 14px; font: 12px/1.55 ui-monospace, Menlo, monospace; }
|
|
773
828
|
#agent-log .al { margin: 2px 0; white-space: pre-wrap; word-break: break-word; }
|
|
774
|
-
#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; }
|
|
829
|
+
#agent-log .al.tool { color: var(--status-review-text); } #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; }
|
|
775
830
|
#agent-panel .ap-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--border, #1f2a26); }
|
|
776
|
-
#agent-panel input { flex: 1; background: var(--bg-muted); color: var(--text, #ecf2ee); border: 1px solid var(--border, #1f2a26); border-radius:
|
|
831
|
+
#agent-panel input { flex: 1; background: var(--bg-muted); color: var(--text, #ecf2ee); border: 1px solid var(--border, #1f2a26); border-radius: 0; padding: 7px 10px; font: inherit; font-size: var(--fs-body); }
|
|
777
832
|
#agent-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text3); }
|
|
778
833
|
#agent-dot.live { background: var(--status-review); animation: apulse 1.2s infinite; }
|
|
779
834
|
@keyframes apulse { 0%,100%{opacity:1} 50%{opacity:.4} }
|
|
780
835
|
.gate-reject { color: var(--status-blocked); border-color: rgba(220, 38, 38, 0.30); }
|
|
781
836
|
.gate-reject:hover { background: rgba(220, 38, 38, 0.06); border-color: var(--status-blocked); }
|
|
782
837
|
.chip-agent {
|
|
783
|
-
background: var(--accent-soft); color: var(--accent);
|
|
838
|
+
background: var(--accent-soft); color: var(--accent-text);
|
|
784
839
|
font-family: var(--mono);
|
|
785
840
|
}
|
|
786
841
|
|
|
@@ -812,7 +867,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
812
867
|
padding: 10px 12px;
|
|
813
868
|
border: 1px solid #f0b429;
|
|
814
869
|
border-left-width: 3px;
|
|
815
|
-
border-radius:
|
|
870
|
+
border-radius: 0;
|
|
816
871
|
background: rgba(240, 180, 41, 0.08);
|
|
817
872
|
font-size: var(--fs-small);
|
|
818
873
|
line-height: 1.45;
|
|
@@ -820,6 +875,21 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
820
875
|
.degraded-banner strong { color: var(--text); font-weight: 600; }
|
|
821
876
|
.degraded-banner span { color: var(--text2); font-family: var(--mono, ui-monospace, monospace); word-break: break-word; }
|
|
822
877
|
|
|
878
|
+
/* Louder than .degraded-banner, and after it, so the shared properties are
|
|
879
|
+
inherited and only the severity is overridden — at equal specificity the later
|
|
880
|
+
rule wins, which is the whole reason this sits below rather than above.
|
|
881
|
+
|
|
882
|
+
The two banners report different failures and must not look alike: amber says
|
|
883
|
+
a panel could not be read and is showing empty; red says every panel WAS read,
|
|
884
|
+
correctly, for a different project. The second is the more dangerous, because
|
|
885
|
+
nothing else on the screen looks wrong. */
|
|
886
|
+
.project-fallback-banner {
|
|
887
|
+
border-color: var(--status-blocked);
|
|
888
|
+
background: var(--rail-fail-bg);
|
|
889
|
+
}
|
|
890
|
+
.project-fallback-banner strong { color: var(--status-blocked); }
|
|
891
|
+
.project-fallback-banner span { color: var(--text); font-family: var(--sans); }
|
|
892
|
+
|
|
823
893
|
/* ── Side panel ───────────────────────────────────────────────────────────── */
|
|
824
894
|
.side {
|
|
825
895
|
position: absolute;
|
|
@@ -827,7 +897,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
827
897
|
width: 480px;
|
|
828
898
|
background: var(--bg-card);
|
|
829
899
|
border-left: 1px solid var(--border);
|
|
830
|
-
box-shadow:
|
|
900
|
+
box-shadow: none;
|
|
831
901
|
z-index: 10;
|
|
832
902
|
display: flex; flex-direction: column;
|
|
833
903
|
transform: translateX(100%);
|
|
@@ -860,14 +930,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
860
930
|
.side-id {
|
|
861
931
|
font-family: var(--mono); font-size: var(--fs-caption); font-weight: 500;
|
|
862
932
|
background: var(--bg-strong); color: var(--text);
|
|
863
|
-
padding: 4px 8px; border-radius:
|
|
933
|
+
padding: 4px 8px; border-radius: 0;
|
|
864
934
|
letter-spacing: 0.04em;
|
|
865
935
|
}
|
|
866
936
|
.side-close {
|
|
867
937
|
margin-left: auto; cursor: pointer;
|
|
868
938
|
width: 28px; height: 28px;
|
|
869
939
|
display: flex; align-items: center; justify-content: center;
|
|
870
|
-
border-radius:
|
|
940
|
+
border-radius: 0;
|
|
871
941
|
color: var(--text3);
|
|
872
942
|
}
|
|
873
943
|
.side-close:hover { background: var(--bg-muted); color: var(--text); }
|
|
@@ -897,7 +967,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
897
967
|
font-style: normal;
|
|
898
968
|
background: var(--bg-muted);
|
|
899
969
|
padding: 1px 5px;
|
|
900
|
-
border-radius:
|
|
970
|
+
border-radius: 0;
|
|
901
971
|
font-size: var(--fs-small);
|
|
902
972
|
}
|
|
903
973
|
.side-desc.md { white-space: normal; }
|
|
@@ -910,15 +980,15 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
910
980
|
.side-desc.md li { margin: 2px 0; }
|
|
911
981
|
.side-desc.md code {
|
|
912
982
|
background: var(--bg-muted); padding: 1px 4px;
|
|
913
|
-
border-radius:
|
|
983
|
+
border-radius: 0; font-size: var(--fs-small); font-family: ui-monospace, monospace;
|
|
914
984
|
}
|
|
915
985
|
.side-desc.md pre.md-pre {
|
|
916
986
|
background: var(--bg-muted); padding: 8px 10px;
|
|
917
|
-
border-radius:
|
|
987
|
+
border-radius: 0; overflow-x: auto; font-size: var(--fs-small);
|
|
918
988
|
margin: 6px 0;
|
|
919
989
|
}
|
|
920
990
|
.side-desc.md pre.md-pre code { background: none; padding: 0; }
|
|
921
|
-
.side-desc.md a { color: var(--accent); text-decoration: none; }
|
|
991
|
+
.side-desc.md a { color: var(--accent-text); text-decoration: none; }
|
|
922
992
|
.side-desc.md a:hover { text-decoration: underline; }
|
|
923
993
|
.side-desc.md strong { color: var(--text); font-weight: 600; }
|
|
924
994
|
.side-id-row { display: inline-flex; align-items: center; gap: 6px; }
|
|
@@ -935,20 +1005,21 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
935
1005
|
}
|
|
936
1006
|
.toast {
|
|
937
1007
|
background: var(--toast-bg); color: var(--on-solid);
|
|
938
|
-
padding: 8px 14px; border-radius:
|
|
1008
|
+
padding: 8px 14px; border-radius: 0;
|
|
939
1009
|
font-size: var(--fs-body); min-width: 200px; max-width: 360px;
|
|
940
|
-
box-shadow:
|
|
1010
|
+
box-shadow: none;
|
|
941
1011
|
opacity: 0; transform: translateY(8px);
|
|
942
1012
|
transition: opacity 0.2s, transform 0.2s;
|
|
943
1013
|
pointer-events: auto;
|
|
1014
|
+
border: 1px solid var(--border-strong);
|
|
944
1015
|
}
|
|
945
1016
|
.toast.show { opacity: 1; transform: translateY(0); }
|
|
946
1017
|
.toast-success { background: var(--toast-ok); border-left: 3px solid #22c55e; }
|
|
947
1018
|
.toast-error { background: var(--toast-err); border-left: 3px solid #ef4444; }
|
|
948
1019
|
.toast-info { background: var(--toast-info); border-left: 3px solid #3b82f6; }
|
|
949
|
-
.toast code { background: rgba(255,255,255,0.1); padding: 1px 4px; border-radius:
|
|
1020
|
+
.toast code { background: rgba(255,255,255,0.1); padding: 1px 4px; border-radius: 0; }
|
|
950
1021
|
.pl-stage { cursor: pointer; transition: background 0.12s; }
|
|
951
|
-
.pl-stage:hover { background: var(--bg-muted); border-radius:
|
|
1022
|
+
.pl-stage:hover { background: var(--bg-muted); border-radius: 0; }
|
|
952
1023
|
.card-selected { outline: 2px solid var(--focus-ring); outline-offset: -1px; }
|
|
953
1024
|
#cmdk {
|
|
954
1025
|
position: fixed; inset: 0; z-index: 10001;
|
|
@@ -961,7 +1032,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
961
1032
|
width: min(620px, 92vw); max-height: 62vh;
|
|
962
1033
|
display: flex; flex-direction: column;
|
|
963
1034
|
background: var(--surface-drawer); border: 1px solid var(--border-strong);
|
|
964
|
-
border-radius:
|
|
1035
|
+
border-radius: 0; box-shadow: none; overflow: hidden;
|
|
965
1036
|
}
|
|
966
1037
|
#cmdk-input {
|
|
967
1038
|
border: 0; border-bottom: 1px solid var(--border);
|
|
@@ -971,7 +1042,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
971
1042
|
#cmdk-list { overflow-y: auto; padding: 6px; }
|
|
972
1043
|
.cmdk-item {
|
|
973
1044
|
display: flex; align-items: center; gap: 10px;
|
|
974
|
-
padding: 9px 10px; border-radius:
|
|
1045
|
+
padding: 9px 10px; border-radius: 0; cursor: pointer;
|
|
975
1046
|
min-height: 40px;
|
|
976
1047
|
}
|
|
977
1048
|
.cmdk-item.on { background: var(--bg-muted); }
|
|
@@ -995,8 +1066,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
995
1066
|
.hk-card {
|
|
996
1067
|
position: relative; background: var(--bg-elevated);
|
|
997
1068
|
color: var(--text); border: 1px solid var(--border);
|
|
998
|
-
border-radius:
|
|
999
|
-
box-shadow:
|
|
1069
|
+
border-radius: 0; padding: 20px 24px; min-width: 360px;
|
|
1070
|
+
box-shadow: none;
|
|
1000
1071
|
}
|
|
1001
1072
|
.hk-title { font-size: var(--fs-title-s); font-weight: 600; margin-bottom: 12px; }
|
|
1002
1073
|
.hk-table { border-collapse: collapse; width: 100%; }
|
|
@@ -1004,7 +1075,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1004
1075
|
.hk-table td:first-child { text-align: right; white-space: nowrap; color: var(--text2); }
|
|
1005
1076
|
.hk-table kbd {
|
|
1006
1077
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
1007
|
-
border-radius:
|
|
1078
|
+
border-radius: 0; padding: 1px 5px; font-size: var(--fs-caption);
|
|
1008
1079
|
font-family: ui-monospace, monospace; color: var(--text);
|
|
1009
1080
|
}
|
|
1010
1081
|
.hk-foot { margin-top: 12px; font-size: var(--fs-caption); color: var(--text3, #9ca3af); text-align: center; }
|
|
@@ -1018,8 +1089,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1018
1089
|
.rdm-card {
|
|
1019
1090
|
position: relative; background: var(--bg-elevated);
|
|
1020
1091
|
color: var(--text); border: 1px solid var(--border);
|
|
1021
|
-
border-radius:
|
|
1022
|
-
box-shadow:
|
|
1092
|
+
border-radius: 0; padding: 18px 22px; min-width: 480px; max-width: 720px;
|
|
1093
|
+
box-shadow: none;
|
|
1023
1094
|
}
|
|
1024
1095
|
.rdm-head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
|
|
1025
1096
|
.rdm-head .rdm-agent { font-weight: 600; }
|
|
@@ -1027,7 +1098,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1027
1098
|
.rdm-head .rdm-verdict.fail { color: var(--verdict-fail); }
|
|
1028
1099
|
.rdm-head .rdm-ts { color: var(--text2); font-size: var(--fs-caption); margin-left: auto; }
|
|
1029
1100
|
.rdm-raw {
|
|
1030
|
-
background: var(--bg-muted); padding: 8px 10px; border-radius:
|
|
1101
|
+
background: var(--bg-muted); padding: 8px 10px; border-radius: 0;
|
|
1031
1102
|
font-family: ui-monospace, monospace; font-size: var(--fs-small);
|
|
1032
1103
|
white-space: pre-wrap; word-break: break-all;
|
|
1033
1104
|
border: 1px solid var(--border);
|
|
@@ -1039,18 +1110,18 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1039
1110
|
display: inline-flex; align-items: center; gap: 4px;
|
|
1040
1111
|
margin: 2px 4px 2px 0; padding: 4px 8px;
|
|
1041
1112
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
1042
|
-
border-radius:
|
|
1113
|
+
border-radius: 0; font-size: var(--fs-small); cursor: pointer;
|
|
1043
1114
|
text-decoration: none; color: var(--text);
|
|
1044
1115
|
font-family: ui-monospace, monospace;
|
|
1045
1116
|
}
|
|
1046
1117
|
.rdm-artefact:hover { background: var(--bg-muted); }
|
|
1047
1118
|
.rdm-artefact .rdm-art-kind {
|
|
1048
|
-
font-size: var(--fs-eyebrow); padding: 1px 4px; border-radius:
|
|
1119
|
+
font-size: var(--fs-eyebrow); padding: 1px 4px; border-radius: 0;
|
|
1049
1120
|
background: var(--accent-soft); color: var(--accent-text);
|
|
1050
1121
|
}
|
|
1051
1122
|
.rdm-foot { margin-top: 12px; font-size: var(--fs-caption); color: var(--text3, #9ca3af); }
|
|
1052
1123
|
.resume-item { cursor: pointer; }
|
|
1053
|
-
.resume-item:hover { background: var(--bg-muted); border-radius:
|
|
1124
|
+
.resume-item:hover { background: var(--bg-muted); border-radius: 0; }
|
|
1054
1125
|
|
|
1055
1126
|
/* ── New issue modal ──────────────────────────────────────────────────── */
|
|
1056
1127
|
#new-issue-modal {
|
|
@@ -1064,9 +1135,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1064
1135
|
.ni-card {
|
|
1065
1136
|
position: relative; background: var(--bg-elevated, #161a1f);
|
|
1066
1137
|
color: var(--text); border: 1px solid var(--border-strong, #2a2d33);
|
|
1067
|
-
border-radius:
|
|
1138
|
+
border-radius: 0; width: 480px; max-width: calc(100vw - 32px);
|
|
1068
1139
|
max-height: calc(100vh - 64px); overflow: auto;
|
|
1069
|
-
box-shadow:
|
|
1140
|
+
box-shadow: none;
|
|
1070
1141
|
animation: ni-pop 140ms ease-out;
|
|
1071
1142
|
}
|
|
1072
1143
|
@keyframes ni-pop {
|
|
@@ -1080,7 +1151,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1080
1151
|
.ni-header h3 { font-size: var(--fs-title-s); font-weight: 600; margin: 0; }
|
|
1081
1152
|
.ni-close {
|
|
1082
1153
|
background: transparent; border: 0; color: var(--text3);
|
|
1083
|
-
cursor: pointer; padding: 4px; display: flex; border-radius:
|
|
1154
|
+
cursor: pointer; padding: 4px; display: flex; border-radius: 0;
|
|
1084
1155
|
}
|
|
1085
1156
|
.ni-close:hover { background: var(--bg-muted, rgba(255,255,255,0.06)); color: var(--text); }
|
|
1086
1157
|
.ni-form {
|
|
@@ -1100,7 +1171,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1100
1171
|
.ni-field select {
|
|
1101
1172
|
background: var(--bg-page, #0a0e0c);
|
|
1102
1173
|
border: 1px solid var(--border, #2a2d33);
|
|
1103
|
-
border-radius:
|
|
1174
|
+
border-radius: 0;
|
|
1104
1175
|
color: var(--text);
|
|
1105
1176
|
font: inherit; font-size: var(--fs-body);
|
|
1106
1177
|
padding: 8px 10px;
|
|
@@ -1127,7 +1198,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1127
1198
|
margin-top: 4px;
|
|
1128
1199
|
}
|
|
1129
1200
|
.ni-btn {
|
|
1130
|
-
border: 1px solid transparent; border-radius:
|
|
1201
|
+
border: 1px solid transparent; border-radius: 0;
|
|
1131
1202
|
padding: 7px 14px; font: inherit; font-size: var(--fs-body);
|
|
1132
1203
|
cursor: pointer; transition: all 120ms;
|
|
1133
1204
|
}
|
|
@@ -1138,7 +1209,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1138
1209
|
.ni-btn-ghost:hover { background: var(--bg-muted, rgba(255,255,255,0.05)); color: var(--text); }
|
|
1139
1210
|
.ni-btn-primary {
|
|
1140
1211
|
background: var(--accent, #00d97e);
|
|
1141
|
-
color: var(--
|
|
1212
|
+
color: var(--on-accent); font-weight: 500;
|
|
1142
1213
|
}
|
|
1143
1214
|
.ni-btn-primary:hover { background: var(--accent-hover); }
|
|
1144
1215
|
.ni-btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
@@ -1193,7 +1264,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1193
1264
|
margin-top: 14px;
|
|
1194
1265
|
background: var(--bg-muted);
|
|
1195
1266
|
border: 1px solid var(--border);
|
|
1196
|
-
border-radius:
|
|
1267
|
+
border-radius: 0;
|
|
1197
1268
|
padding: 12px 14px;
|
|
1198
1269
|
font-size: var(--fs-body);
|
|
1199
1270
|
color: var(--text2);
|
|
@@ -1224,7 +1295,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1224
1295
|
.mem-item {
|
|
1225
1296
|
display: flex; flex-direction: column; gap: 2px;
|
|
1226
1297
|
padding: 10px 12px;
|
|
1227
|
-
border-radius:
|
|
1298
|
+
border-radius: 0;
|
|
1228
1299
|
cursor: pointer;
|
|
1229
1300
|
margin-bottom: 2px;
|
|
1230
1301
|
border: 1px solid transparent;
|
|
@@ -1233,8 +1304,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1233
1304
|
.mem-item.active { background: var(--accent-soft); border-color: rgba(37, 64, 212, 0.16); }
|
|
1234
1305
|
.mem-item .layer-tag {
|
|
1235
1306
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
1236
|
-
color: var(--accent); letter-spacing: 0.06em;
|
|
1237
|
-
font-weight:
|
|
1307
|
+
color: var(--accent-text); letter-spacing: 0.06em;
|
|
1308
|
+
font-weight: 500;
|
|
1238
1309
|
}
|
|
1239
1310
|
.mem-item .name {
|
|
1240
1311
|
font-family: var(--mono); font-size: var(--fs-small);
|
|
@@ -1270,8 +1341,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1270
1341
|
max-width: 880px;
|
|
1271
1342
|
margin: 0 auto;
|
|
1272
1343
|
}
|
|
1273
|
-
.memory-doc h1 { font-family: var(--
|
|
1274
|
-
.memory-doc h2 { font-family: var(--
|
|
1344
|
+
.memory-doc h1 { font-family: var(--display); font-weight: 600; font-size: 28px; letter-spacing: -0.02em; margin: 0 0 14px; color: var(--text); }
|
|
1345
|
+
.memory-doc h2 { font-family: var(--display); font-weight: 600; font-size: 20px; margin: 28px 0 10px; color: var(--text); }
|
|
1275
1346
|
.memory-doc h3 { font-family: var(--sans); font-weight: 600; font-size: var(--fs-title-s); margin: 18px 0 8px; color: var(--text); }
|
|
1276
1347
|
.memory-doc p { font-size: var(--fs-base); line-height: 1.65; color: var(--text2); margin: 0 0 10px; }
|
|
1277
1348
|
.memory-doc ul, .memory-doc ol { padding-left: 22px; margin: 0 0 12px; color: var(--text2); }
|
|
@@ -1279,14 +1350,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1279
1350
|
.memory-doc code {
|
|
1280
1351
|
font-family: var(--mono); font-size: var(--fs-small);
|
|
1281
1352
|
background: var(--bg-muted);
|
|
1282
|
-
padding: 1px 6px; border-radius:
|
|
1353
|
+
padding: 1px 6px; border-radius: 0;
|
|
1283
1354
|
color: var(--text);
|
|
1284
1355
|
}
|
|
1285
1356
|
.memory-doc pre {
|
|
1286
1357
|
font-family: var(--mono); font-size: var(--fs-small);
|
|
1287
1358
|
background: var(--bg-strong);
|
|
1288
1359
|
padding: 14px 16px;
|
|
1289
|
-
border-radius:
|
|
1360
|
+
border-radius: 0;
|
|
1290
1361
|
overflow-x: auto;
|
|
1291
1362
|
margin: 12px 0;
|
|
1292
1363
|
line-height: 1.55;
|
|
@@ -1313,9 +1384,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1313
1384
|
}
|
|
1314
1385
|
.memory-doc-head .layer-tag {
|
|
1315
1386
|
font-family: var(--mono); font-size: var(--fs-caption);
|
|
1316
|
-
background: var(--accent-soft); color: var(--accent);
|
|
1317
|
-
padding: 4px 10px; border-radius:
|
|
1318
|
-
letter-spacing: 0.06em; font-weight:
|
|
1387
|
+
background: var(--accent-soft); color: var(--accent-text);
|
|
1388
|
+
padding: 4px 10px; border-radius: 0;
|
|
1389
|
+
letter-spacing: 0.06em; font-weight: 500;
|
|
1319
1390
|
}
|
|
1320
1391
|
.memory-doc-head .filename {
|
|
1321
1392
|
font-family: var(--mono); font-size: var(--fs-body);
|
|
@@ -1335,9 +1406,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1335
1406
|
.tier-badge { font-size: var(--fs-caption); padding: 1px 8px; border-radius: 999px; margin-left: 6px;
|
|
1336
1407
|
font-family: var(--mono); white-space: nowrap; }
|
|
1337
1408
|
.tier-badge:empty { display: none; }
|
|
1338
|
-
.tier-badge.tier-T0 { background: var(--status-review); color: var(--on-
|
|
1339
|
-
.tier-badge.tier-T1 { background: var(--status-progress); color: var(--on-
|
|
1340
|
-
.tier-badge.tier-T2 { background: var(--status-blocked); color: var(--on-
|
|
1409
|
+
.tier-badge.tier-T0 { background: var(--status-review); color: var(--on-status); }
|
|
1410
|
+
.tier-badge.tier-T1 { background: var(--status-progress); color: var(--on-status); }
|
|
1411
|
+
.tier-badge.tier-T2 { background: var(--status-blocked); color: var(--on-status); }
|
|
1341
1412
|
|
|
1342
1413
|
/* ── Pipeline track ───────────────────────────────────────────────────────── */
|
|
1343
1414
|
.pipeline-track {
|
|
@@ -1346,7 +1417,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1346
1417
|
padding: 14px 16px;
|
|
1347
1418
|
background: var(--surface-translucent);
|
|
1348
1419
|
border: 1px solid var(--border);
|
|
1349
|
-
border-radius:
|
|
1420
|
+
border-radius: 0;
|
|
1350
1421
|
overflow-x: auto;
|
|
1351
1422
|
}
|
|
1352
1423
|
.pl-stage {
|
|
@@ -1376,7 +1447,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1376
1447
|
.pl-recover { display: flex; gap: 6px; margin-top: 6px; }
|
|
1377
1448
|
.pl-recover-btn {
|
|
1378
1449
|
min-height: 26px; padding: 0 8px;
|
|
1379
|
-
border: 1px solid var(--border); border-radius:
|
|
1450
|
+
border: 1px solid var(--border); border-radius: 0;
|
|
1380
1451
|
background: var(--bg-card); color: var(--text2);
|
|
1381
1452
|
font-family: var(--mono); font-size: var(--fs-eyebrow); cursor: pointer;
|
|
1382
1453
|
}
|
|
@@ -1445,7 +1516,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1445
1516
|
display: flex; align-items: center; gap: 14px;
|
|
1446
1517
|
padding: 22px 22px; margin-top: 4px;
|
|
1447
1518
|
background: var(--surface-translucent); border: 1px solid var(--border);
|
|
1448
|
-
border-radius:
|
|
1519
|
+
border-radius: 0;
|
|
1449
1520
|
}
|
|
1450
1521
|
.inbox-allclear .ac-mark {
|
|
1451
1522
|
width: 34px; height: 34px; flex-shrink: 0;
|
|
@@ -1457,7 +1528,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1457
1528
|
.inbox-allclear .ac-text b { color: var(--text); font-weight: 600; }
|
|
1458
1529
|
.inbox-allclear .ac-actions { display: flex; gap: 8px; }
|
|
1459
1530
|
.inbox-allclear .ac-btn {
|
|
1460
|
-
font-size: var(--fs-body); padding: 7px 14px; border-radius:
|
|
1531
|
+
font-size: var(--fs-body); padding: 7px 14px; border-radius: 0;
|
|
1461
1532
|
border: 1px solid var(--border-strong); background: transparent;
|
|
1462
1533
|
color: var(--text); cursor: pointer; transition: background 0.12s, border-color 0.12s;
|
|
1463
1534
|
}
|
|
@@ -1470,7 +1541,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1470
1541
|
.pl-gate {
|
|
1471
1542
|
background: var(--surface-translucent);
|
|
1472
1543
|
border-left: 2px solid var(--border-strong);
|
|
1473
|
-
border-radius:
|
|
1544
|
+
border-radius: 0;
|
|
1474
1545
|
margin: -4px 2px;
|
|
1475
1546
|
padding: 12px 12px 10px;
|
|
1476
1547
|
}
|
|
@@ -1487,7 +1558,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1487
1558
|
background: var(--status-blocked); color: var(--on-solid);
|
|
1488
1559
|
font-size: var(--fs-eyebrow); font-weight: 700;
|
|
1489
1560
|
min-width: 16px; height: 16px; padding: 0 4px;
|
|
1490
|
-
border-radius:
|
|
1561
|
+
border-radius: 0;
|
|
1491
1562
|
display: inline-flex; align-items: center; justify-content: center;
|
|
1492
1563
|
}
|
|
1493
1564
|
.pl-gate.active.fresh .pl-name .gate-glyph { animation: pl-pulse 1.4s infinite; }
|
|
@@ -1532,7 +1603,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1532
1603
|
color: var(--text2);
|
|
1533
1604
|
}
|
|
1534
1605
|
.cost-cell .v {
|
|
1535
|
-
font-family: var(--
|
|
1606
|
+
font-family: var(--display); font-weight: 500;
|
|
1536
1607
|
font-variant-numeric: tabular-nums;
|
|
1537
1608
|
font-size: var(--fs-num-s); letter-spacing: -0.02em;
|
|
1538
1609
|
color: var(--text); line-height: 1.1;
|
|
@@ -1540,7 +1611,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1540
1611
|
.cost-cell .v small { font-size: var(--fs-base); color: var(--text2); margin-left: 2px; font-weight: 400; }
|
|
1541
1612
|
.cost-cell .sub { font-size: var(--fs-caption); color: var(--text2); }
|
|
1542
1613
|
.cost-cell.warn .v { color: var(--status-blocked); }
|
|
1543
|
-
.cost-cell.good .v { color: var(--status-review); }
|
|
1614
|
+
.cost-cell.good .v { color: var(--status-review-text); }
|
|
1544
1615
|
|
|
1545
1616
|
.cost-chart {
|
|
1546
1617
|
position: relative;
|
|
@@ -1556,7 +1627,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1556
1627
|
.cost-bar {
|
|
1557
1628
|
flex: 1; min-width: 4px;
|
|
1558
1629
|
background: var(--accent-soft);
|
|
1559
|
-
border-radius:
|
|
1630
|
+
border-radius: 0;
|
|
1560
1631
|
position: relative;
|
|
1561
1632
|
cursor: pointer;
|
|
1562
1633
|
transition: background 0.12s;
|
|
@@ -1569,7 +1640,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1569
1640
|
bottom: calc(100% + 4px); left: 50%;
|
|
1570
1641
|
transform: translateX(-50%);
|
|
1571
1642
|
background: var(--bg-solid); color: var(--on-solid);
|
|
1572
|
-
padding: 4px 8px; border-radius:
|
|
1643
|
+
padding: 4px 8px; border-radius: 0;
|
|
1573
1644
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
1574
1645
|
white-space: nowrap;
|
|
1575
1646
|
display: none;
|
|
@@ -1586,7 +1657,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1586
1657
|
cursor: pointer; font-size: var(--fs-small); color: var(--text2);
|
|
1587
1658
|
padding: 4px 0; list-style-position: inside;
|
|
1588
1659
|
}
|
|
1589
|
-
.cost-table summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius:
|
|
1660
|
+
.cost-table summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 0; }
|
|
1590
1661
|
.cost-table table { border-collapse: collapse; width: 100%; margin-top: 6px; }
|
|
1591
1662
|
.cost-table th, .cost-table td {
|
|
1592
1663
|
text-align: left; padding: 5px 10px 5px 0;
|
|
@@ -1642,8 +1713,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1642
1713
|
font-family: var(--mono); color: var(--text);
|
|
1643
1714
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1644
1715
|
}
|
|
1645
|
-
.feat-bar-wrap { background: var(--bg-strong); border-radius:
|
|
1646
|
-
.feat-bar { height: 6px; border-radius:
|
|
1716
|
+
.feat-bar-wrap { background: var(--bg-strong); border-radius: 0; height: 6px; }
|
|
1717
|
+
.feat-bar { height: 6px; border-radius: 0; background: var(--accent); transition: width .3s; }
|
|
1647
1718
|
.feat-cost { font-family: var(--mono); color: var(--text); text-align: right; }
|
|
1648
1719
|
.feat-runs { font-size: var(--fs-caption); color: var(--text3); text-align: right; }
|
|
1649
1720
|
|
|
@@ -1654,7 +1725,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1654
1725
|
margin: 0 auto;
|
|
1655
1726
|
}
|
|
1656
1727
|
.inbox-greet {
|
|
1657
|
-
font-family: var(--
|
|
1728
|
+
font-family: var(--display); font-weight: 500;
|
|
1658
1729
|
font-size: clamp(22px, 2.5vw, 30px);
|
|
1659
1730
|
letter-spacing: -0.02em;
|
|
1660
1731
|
margin-bottom: 24px;
|
|
@@ -1667,12 +1738,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1667
1738
|
background: var(--surface-translucent);
|
|
1668
1739
|
backdrop-filter: blur(8px);
|
|
1669
1740
|
border: 1px solid var(--border);
|
|
1670
|
-
border-radius:
|
|
1741
|
+
border-radius: 0;
|
|
1671
1742
|
padding: 14px 16px;
|
|
1672
1743
|
display: flex; align-items: center; gap: 10px;
|
|
1673
1744
|
}
|
|
1674
1745
|
.inbox-summary .pill-stat .num {
|
|
1675
|
-
font-family: var(--
|
|
1746
|
+
font-family: var(--display); font-weight: 500;
|
|
1676
1747
|
font-variant-numeric: tabular-nums;
|
|
1677
1748
|
font-size: var(--fs-num-s); letter-spacing: -0.02em;
|
|
1678
1749
|
color: var(--text);
|
|
@@ -1702,7 +1773,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1702
1773
|
.ac-primary:focus-visible {
|
|
1703
1774
|
outline: 2px solid var(--focus-ring);
|
|
1704
1775
|
outline-offset: 2px;
|
|
1705
|
-
border-radius:
|
|
1776
|
+
border-radius: 0;
|
|
1706
1777
|
}
|
|
1707
1778
|
|
|
1708
1779
|
/* Reason chip — why a row is in the queue, carried by the row itself. */
|
|
@@ -1713,19 +1784,27 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1713
1784
|
letter-spacing: 0.04em;
|
|
1714
1785
|
padding: 1px 7px;
|
|
1715
1786
|
margin-right: 8px;
|
|
1716
|
-
border-radius:
|
|
1787
|
+
border-radius: 0;
|
|
1717
1788
|
border: 1px solid var(--border-strong);
|
|
1718
1789
|
color: var(--text2);
|
|
1719
1790
|
vertical-align: 1px;
|
|
1720
1791
|
}
|
|
1721
1792
|
.why-gate { color: var(--status-gate); border-color: var(--status-gate); }
|
|
1793
|
+
/* An expensive-to-undo gate must not wear the same chip as a routine one — that
|
|
1794
|
+
equivalence is the finding this rule exists to remove. Colour AND a glyph:
|
|
1795
|
+
colour alone fails the reader who cannot see it. */
|
|
1796
|
+
/* An expensive-to-undo gate must not wear the same chip as a routine one — that
|
|
1797
|
+
equivalence is the whole finding. And `cost not classified` gets a third look,
|
|
1798
|
+
because a gate nobody has judged is not a cheap gate. */
|
|
1799
|
+
.why-gate-expensive { color: var(--status-blocked); border-color: var(--status-blocked); }
|
|
1800
|
+
.why-gate-unclassified { color: var(--p1-fg); border-color: var(--p1-fg); }
|
|
1722
1801
|
.why-p0 { color: var(--status-blocked); border-color: var(--status-blocked); }
|
|
1723
1802
|
.why-blocked { color: var(--status-progress); border-color: var(--status-progress); }
|
|
1724
1803
|
.why-stale { color: var(--text3); }
|
|
1725
1804
|
|
|
1726
1805
|
.proj-status {
|
|
1727
1806
|
border: 1px solid var(--border);
|
|
1728
|
-
border-radius:
|
|
1807
|
+
border-radius: 0;
|
|
1729
1808
|
background: var(--bg-card);
|
|
1730
1809
|
margin: 10px 0 18px;
|
|
1731
1810
|
}
|
|
@@ -1748,14 +1827,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1748
1827
|
.proj-status-summary:focus-visible {
|
|
1749
1828
|
outline: 2px solid var(--focus-ring);
|
|
1750
1829
|
outline-offset: 2px;
|
|
1751
|
-
border-radius:
|
|
1830
|
+
border-radius: 0;
|
|
1752
1831
|
}
|
|
1753
1832
|
/* A link with no colour rule gets the browser's default, which is on nobody's
|
|
1754
1833
|
palette. This one — "stale, listed below" in the empty resume column — was
|
|
1755
1834
|
added today without one, and the rendered-layout check found it on both themes
|
|
1756
1835
|
within the hour. That is the check working on its author. */
|
|
1757
1836
|
.resume-list a, .empty a { color: var(--accent-text); text-decoration: underline; }
|
|
1758
|
-
.resume-list a:hover, .empty a:hover { color: var(--accent); }
|
|
1837
|
+
.resume-list a:hover, .empty a:hover { color: var(--accent-text); }
|
|
1759
1838
|
|
|
1760
1839
|
.proj-status-verdict { color: var(--text3); font-family: var(--mono); font-size: var(--fs-eyebrow); }
|
|
1761
1840
|
.proj-status-body { padding: 0 16px 14px; }
|
|
@@ -1764,7 +1843,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1764
1843
|
.resume-card {
|
|
1765
1844
|
background: linear-gradient(135deg, rgba(0, 217, 126, 0.04), var(--surface-translucent));
|
|
1766
1845
|
border: 1px solid rgba(0, 217, 126, 0.18);
|
|
1767
|
-
border-radius:
|
|
1846
|
+
border-radius: 0;
|
|
1768
1847
|
padding: 18px 20px;
|
|
1769
1848
|
margin-bottom: 28px;
|
|
1770
1849
|
backdrop-filter: blur(8px);
|
|
@@ -1774,7 +1853,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1774
1853
|
margin-bottom: 14px;
|
|
1775
1854
|
}
|
|
1776
1855
|
.resume-icon {
|
|
1777
|
-
display: inline-flex; color: var(--accent);
|
|
1856
|
+
display: inline-flex; color: var(--accent-text);
|
|
1778
1857
|
}
|
|
1779
1858
|
.resume-title {
|
|
1780
1859
|
font-family: var(--sans); font-weight: 600;
|
|
@@ -1814,13 +1893,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1814
1893
|
.resume-list .resume-item .ri-tag {
|
|
1815
1894
|
flex-shrink: 0;
|
|
1816
1895
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
1817
|
-
padding: 2px 5px; border-radius:
|
|
1896
|
+
padding: 2px 5px; border-radius: 0;
|
|
1818
1897
|
letter-spacing: 0.04em;
|
|
1819
1898
|
background: rgba(255,255,255,0.05);
|
|
1820
1899
|
color: var(--text3);
|
|
1821
1900
|
margin-top: 1px;
|
|
1822
1901
|
}
|
|
1823
|
-
.resume-list .resume-item .ri-tag.ok { color: var(--accent); background: rgba(0,217,126,0.08); }
|
|
1902
|
+
.resume-list .resume-item .ri-tag.ok { color: var(--accent-text); background: rgba(0,217,126,0.08); }
|
|
1824
1903
|
.resume-list .resume-item .ri-tag.fail { color: var(--p1-fg); background: rgba(255,80,80,0.08); }
|
|
1825
1904
|
.resume-list .resume-item .ri-text {
|
|
1826
1905
|
flex: 1; min-width: 0;
|
|
@@ -1863,13 +1942,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1863
1942
|
padding: 12px 14px;
|
|
1864
1943
|
background: var(--bg-card);
|
|
1865
1944
|
border: 1px solid var(--border);
|
|
1866
|
-
border-radius:
|
|
1945
|
+
border-radius: 0;
|
|
1867
1946
|
margin-bottom: 6px;
|
|
1868
1947
|
transition: box-shadow .12s, transform .12s;
|
|
1869
1948
|
cursor: pointer;
|
|
1870
1949
|
min-width: 0;
|
|
1871
1950
|
}
|
|
1872
|
-
.inbox-row:hover { box-shadow:
|
|
1951
|
+
.inbox-row:hover { box-shadow: none; transform: translateY(-1px); }
|
|
1873
1952
|
.inbox-row .id {
|
|
1874
1953
|
padding-top: 2px;
|
|
1875
1954
|
font-family: var(--mono); font-size: var(--fs-caption);
|
|
@@ -1938,7 +2017,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1938
2017
|
blocked colour — so "sat here longest" is legible at a glance instead of
|
|
1939
2018
|
requiring five timestamps to be compared by eye. */
|
|
1940
2019
|
.inbox-row.row-aged { border-left: 3px solid var(--status-blocked); }
|
|
1941
|
-
|
|
2020
|
+
/* Colour carries this, not weight: the age is mono, whose axis stops at 500,
|
|
2021
|
+
so 600 only buys a synthetic stroke. */
|
|
2022
|
+
.inbox-row.row-aged .actions.age-col .age { color: var(--status-blocked); font-weight: 500; }
|
|
1942
2023
|
|
|
1943
2024
|
/* Quiet page footer for conditions that report but do not ask. */
|
|
1944
2025
|
.inbox-foot-note {
|
|
@@ -1952,7 +2033,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1952
2033
|
.budgets-failed .bf-note { font-size: var(--fs-body); color: var(--text2); line-height: 1.5; margin-bottom: 14px; }
|
|
1953
2034
|
.judge-status {
|
|
1954
2035
|
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
|
|
1955
|
-
padding: 10px 12px; margin-bottom: 16px; border-radius:
|
|
2036
|
+
padding: 10px 12px; margin-bottom: 16px; border-radius: 0;
|
|
1956
2037
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
1957
2038
|
font-size: var(--fs-body);
|
|
1958
2039
|
}
|
|
@@ -1965,11 +2046,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1965
2046
|
.jscores[hidden] { display: none; }
|
|
1966
2047
|
.jscores-head {
|
|
1967
2048
|
display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
|
|
1968
|
-
padding: 10px 12px; border: 1px solid var(--border); border-radius:
|
|
2049
|
+
padding: 10px 12px; border: 1px solid var(--border); border-radius: 0;
|
|
1969
2050
|
background: var(--bg-muted); font-size: var(--fs-body);
|
|
1970
2051
|
}
|
|
1971
2052
|
.jscores-head .rate { font-size: var(--fs-num-s); font-weight: 600; }
|
|
1972
|
-
.jscores-rows { border: 1px solid var(--border); border-top: 0; border-radius: 0
|
|
2053
|
+
.jscores-rows { border: 1px solid var(--border); border-top: 0; border-radius: 0; }
|
|
1973
2054
|
.jscore {
|
|
1974
2055
|
display: grid; grid-template-columns: minmax(9rem, 12rem) 7.5rem 1fr auto;
|
|
1975
2056
|
gap: 12px; align-items: baseline;
|
|
@@ -1997,7 +2078,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
1997
2078
|
.judge-status .js-form { display: flex; gap: 8px; align-items: center; margin-left: auto; }
|
|
1998
2079
|
.judge-status input {
|
|
1999
2080
|
font-family: var(--mono); font-size: var(--fs-small); padding: 5px 8px;
|
|
2000
|
-
border: 1px solid var(--border-strong); border-radius:
|
|
2081
|
+
border: 1px solid var(--border-strong); border-radius: 0;
|
|
2001
2082
|
background: var(--bg-card); color: var(--text); min-width: 26ch;
|
|
2002
2083
|
}
|
|
2003
2084
|
.budgets-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
|
|
@@ -2024,7 +2105,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2024
2105
|
.budgets-table .bt-actions { text-align: right; white-space: nowrap; }
|
|
2025
2106
|
.budgets-table .bt-actions .ab-btn + .ab-btn { margin-left: 6px; }
|
|
2026
2107
|
.budgets-empty {
|
|
2027
|
-
padding: 16px; border: 1px dashed var(--border-strong); border-radius:
|
|
2108
|
+
padding: 16px; border: 1px dashed var(--border-strong); border-radius: 0;
|
|
2028
2109
|
color: var(--text2); font-size: var(--fs-body);
|
|
2029
2110
|
}
|
|
2030
2111
|
.budgets-uncapped { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
@@ -2032,7 +2113,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2032
2113
|
.docs-search input {
|
|
2033
2114
|
flex: 1 1 auto; min-width: 0; max-width: 460px;
|
|
2034
2115
|
height: 32px; padding: 0 10px;
|
|
2035
|
-
border: 1px solid var(--border); border-radius:
|
|
2116
|
+
border: 1px solid var(--border); border-radius: 0;
|
|
2036
2117
|
background: var(--bg-card); color: var(--text); font: inherit; font-size: var(--fs-body);
|
|
2037
2118
|
}
|
|
2038
2119
|
.docs-search input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
|
|
@@ -2045,7 +2126,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2045
2126
|
display: block; width: 100%;
|
|
2046
2127
|
margin-top: 6px; padding: 8px;
|
|
2047
2128
|
font-family: var(--mono); font-size: var(--fs-caption);
|
|
2048
|
-
border: 1px dashed var(--border-strong); border-radius:
|
|
2129
|
+
border: 1px dashed var(--border-strong); border-radius: 0;
|
|
2049
2130
|
background: transparent; color: var(--text2);
|
|
2050
2131
|
}
|
|
2051
2132
|
.col-more:hover { border-style: solid; color: var(--text); }
|
|
@@ -2053,7 +2134,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2053
2134
|
.inbox-empty {
|
|
2054
2135
|
padding: 14px 16px;
|
|
2055
2136
|
background: var(--bg-muted);
|
|
2056
|
-
border-radius:
|
|
2137
|
+
border-radius: 0;
|
|
2057
2138
|
font-size: var(--fs-small); color: var(--text3);
|
|
2058
2139
|
text-align: center;
|
|
2059
2140
|
}
|
|
@@ -2071,7 +2152,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2071
2152
|
}
|
|
2072
2153
|
.window-fill {
|
|
2073
2154
|
font-size: var(--fs-body); color: var(--text2); line-height: 1.5;
|
|
2074
|
-
padding: 8px 12px; margin-bottom: 12px; border-radius:
|
|
2155
|
+
padding: 8px 12px; margin-bottom: 12px; border-radius: 0;
|
|
2075
2156
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2076
2157
|
}
|
|
2077
2158
|
.window-fill[hidden] { display: none; }
|
|
@@ -2086,22 +2167,22 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2086
2167
|
.period-chips {
|
|
2087
2168
|
display: flex; gap: 4px;
|
|
2088
2169
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2089
|
-
border-radius:
|
|
2170
|
+
border-radius: 0; padding: 3px;
|
|
2090
2171
|
}
|
|
2091
2172
|
.period-chip {
|
|
2092
2173
|
background: transparent; border: none; color: var(--text2);
|
|
2093
|
-
padding: 5px 12px; border-radius:
|
|
2174
|
+
padding: 5px 12px; border-radius: 0;
|
|
2094
2175
|
font-family: var(--mono); font-size: var(--fs-caption); font-weight: 500;
|
|
2095
2176
|
letter-spacing: 0.05em; cursor: pointer;
|
|
2096
2177
|
transition: background .12s, color .12s;
|
|
2097
2178
|
}
|
|
2098
2179
|
.period-chip:hover { color: var(--text); }
|
|
2099
2180
|
.period-chip.active {
|
|
2100
|
-
background: var(--accent); color: var(--
|
|
2181
|
+
background: var(--accent); color: var(--on-accent);
|
|
2101
2182
|
}
|
|
2102
2183
|
.metric-card {
|
|
2103
2184
|
background: var(--bg-card); border: 1px solid var(--border);
|
|
2104
|
-
border-radius:
|
|
2185
|
+
border-radius: 0; padding: 26px 24px;
|
|
2105
2186
|
position: relative; overflow: hidden;
|
|
2106
2187
|
}
|
|
2107
2188
|
.metric-card::before {
|
|
@@ -2114,7 +2195,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2114
2195
|
.metric-card.red::before { background: var(--status-blocked); }
|
|
2115
2196
|
.metric-card.purple::before { background: var(--metric-purple); }
|
|
2116
2197
|
.metric-num {
|
|
2117
|
-
font-family: var(--
|
|
2198
|
+
font-family: var(--display); font-weight: 500;
|
|
2118
2199
|
/* The board is live (SSE). In a proportional face a 1 is narrower than an 8,
|
|
2119
2200
|
so a figure that ticks over re-flows its own width and the number appears to
|
|
2120
2201
|
breathe. Tabular figures cost nothing here and stop it. */
|
|
@@ -2136,11 +2217,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2136
2217
|
}
|
|
2137
2218
|
.delta {
|
|
2138
2219
|
margin-left: 6px;
|
|
2139
|
-
font-family: var(--mono); font-size: var(--fs-eyebrow); font-weight:
|
|
2220
|
+
font-family: var(--mono); font-size: var(--fs-eyebrow); font-weight: 500;
|
|
2140
2221
|
letter-spacing: 0.02em; white-space: nowrap;
|
|
2141
2222
|
color: var(--text3); cursor: help;
|
|
2142
2223
|
}
|
|
2143
|
-
.delta.up { color: var(--status-review); }
|
|
2224
|
+
.delta.up { color: var(--status-review-text); }
|
|
2144
2225
|
.delta.down { color: var(--status-progress); }
|
|
2145
2226
|
.metric-num small {
|
|
2146
2227
|
font-size: var(--fs-title-l); color: var(--text2);
|
|
@@ -2167,7 +2248,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2167
2248
|
}
|
|
2168
2249
|
.mp-card {
|
|
2169
2250
|
background: var(--bg-card); border: 1px solid var(--border);
|
|
2170
|
-
border-radius:
|
|
2251
|
+
border-radius: 0; padding: 22px;
|
|
2171
2252
|
}
|
|
2172
2253
|
.mp-card h3 {
|
|
2173
2254
|
font-family: var(--sans); font-weight: 600;
|
|
@@ -2182,9 +2263,9 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2182
2263
|
}
|
|
2183
2264
|
.feed-cost {
|
|
2184
2265
|
font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
2185
|
-
color: var(--accent);
|
|
2266
|
+
color: var(--accent-text);
|
|
2186
2267
|
background: var(--accent-soft);
|
|
2187
|
-
padding: 2px 6px; border-radius:
|
|
2268
|
+
padding: 2px 6px; border-radius: 0;
|
|
2188
2269
|
margin-right: 6px;
|
|
2189
2270
|
}
|
|
2190
2271
|
|
|
@@ -2235,7 +2316,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2235
2316
|
.logs-header { padding: 20px 24px 12px; border-bottom: 1px solid var(--border); display: flex; align-items: baseline; gap: 16px; }
|
|
2236
2317
|
.logs-header h2 { margin: 0; font-size: var(--fs-title-l); letter-spacing: -0.02em; }
|
|
2237
2318
|
.logs-hint { font-size: var(--fs-small); color: var(--text3); }
|
|
2238
|
-
.logs-hint code { font-family: var(--mono); background: var(--bg-strong); padding: 1px 5px; border-radius:
|
|
2319
|
+
.logs-hint code { font-family: var(--mono); background: var(--bg-strong); padding: 1px 5px; border-radius: 0; }
|
|
2239
2320
|
.logs-layout { display: flex; flex: 1; overflow: hidden; }
|
|
2240
2321
|
.logs-list { width: 280px; min-width: 220px; border-right: 1px solid var(--border); overflow-y: auto; padding: 8px 0; flex-shrink: 0; }
|
|
2241
2322
|
.logs-detail { flex: 1; overflow-y: auto; padding: 24px 28px; }
|
|
@@ -2266,7 +2347,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2266
2347
|
.log-section-label { font-size: var(--fs-caption); color: var(--text3); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; }
|
|
2267
2348
|
.log-bullets { list-style: none; padding: 0; margin: 0; }
|
|
2268
2349
|
.log-bullets li { padding: 3px 0 3px 12px; position: relative; font-size: var(--fs-body); color: var(--text2); }
|
|
2269
|
-
.log-bullets li::before { content: '·'; position: absolute; left: 0; color: var(--accent); }
|
|
2350
|
+
.log-bullets li::before { content: '·'; position: absolute; left: 0; color: var(--accent-text); }
|
|
2270
2351
|
.log-empty { font-size: var(--fs-body); color: var(--text3); font-style: italic; }
|
|
2271
2352
|
|
|
2272
2353
|
/* ── Agents installed list (legacy — kept for fallback) ───────────────── */
|
|
@@ -2274,14 +2355,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2274
2355
|
/* Agent budget bars (Paperclip cascading budget pattern) */
|
|
2275
2356
|
.ab-title { font-weight: 600; color: var(--text2); margin-bottom: 6px; }
|
|
2276
2357
|
.ab-row { display: flex; justify-content: space-between; padding: 2px 0; }
|
|
2277
|
-
.ab-name { font-family: var(--mono, monospace); color: var(--accent); }
|
|
2358
|
+
.ab-name { font-family: var(--mono, monospace); color: var(--accent-text); }
|
|
2278
2359
|
.ab-cap { color: var(--text3); }
|
|
2279
|
-
.agent-card { background: var(--bg-muted); border: 1px solid var(--border); border-radius:
|
|
2280
|
-
.agent-card .ac-name { font-size: var(--fs-small); font-weight:
|
|
2360
|
+
.agent-card { background: var(--bg-muted); border: 1px solid var(--border); border-radius: 0; padding: 10px 12px; }
|
|
2361
|
+
.agent-card .ac-name { font-size: var(--fs-small); font-weight: 500; font-family: var(--mono); color: var(--accent-text); }
|
|
2281
2362
|
.agent-card .ac-desc { font-size: var(--fs-caption); color: var(--text3); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2282
2363
|
.agent-card .ac-meta { font-size: var(--fs-eyebrow); color: var(--text3); margin-top: 5px; display: flex; gap: 8px; }
|
|
2283
|
-
.agent-card .ac-model { background: var(--bg-strong); padding: 1px 5px; border-radius:
|
|
2284
|
-
.agent-card .ac-used { color: var(--status-review); }
|
|
2364
|
+
.agent-card .ac-model { background: var(--bg-strong); padding: 1px 5px; border-radius: 0; }
|
|
2365
|
+
.agent-card .ac-used { color: var(--status-review-text); }
|
|
2285
2366
|
.agent-card .ac-never { color: var(--text3); font-style: italic; }
|
|
2286
2367
|
|
|
2287
2368
|
/* ── Agents fleet view (DESIGN-agents-fleet-view §3) ───────────────────
|
|
@@ -2310,7 +2391,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2310
2391
|
flex: 1; max-width: 320px;
|
|
2311
2392
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2312
2393
|
color: var(--text); font-family: var(--mono); font-size: var(--fs-body);
|
|
2313
|
-
padding: 8px 12px; border-radius:
|
|
2394
|
+
padding: 8px 12px; border-radius: 0;
|
|
2314
2395
|
}
|
|
2315
2396
|
.fleet-search input[type="search"]:focus {
|
|
2316
2397
|
outline: 2px solid var(--focus-ring); outline-offset: 1px;
|
|
@@ -2319,7 +2400,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2319
2400
|
.fleet-search .search-hint { font-size: var(--fs-caption); color: var(--text3); font-family: var(--mono); }
|
|
2320
2401
|
.fleet-search .search-hint kbd {
|
|
2321
2402
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2322
|
-
border-radius:
|
|
2403
|
+
border-radius: 0; padding: 1px 5px; font-size: var(--fs-eyebrow);
|
|
2323
2404
|
}
|
|
2324
2405
|
|
|
2325
2406
|
.fleet-facets { display: flex; flex-direction: column; gap: 6px; }
|
|
@@ -2334,13 +2415,13 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2334
2415
|
.facet-chip {
|
|
2335
2416
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2336
2417
|
color: var(--text2); font-size: var(--fs-small); font-family: var(--mono);
|
|
2337
|
-
padding: 4px 10px; border-radius:
|
|
2418
|
+
padding: 4px 10px; border-radius: 0; cursor: pointer;
|
|
2338
2419
|
transition: background-color 0.15s, color 0.15s, border-color 0.15s;
|
|
2339
2420
|
}
|
|
2340
2421
|
.facet-chip:hover { color: var(--text); border-color: var(--border-strong); }
|
|
2341
2422
|
.facet-chip:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
|
|
2342
2423
|
.facet-chip[aria-checked="true"] {
|
|
2343
|
-
background: var(--accent-soft); color: var(--accent);
|
|
2424
|
+
background: var(--accent-soft); color: var(--accent-text);
|
|
2344
2425
|
border-color: var(--accent-strong);
|
|
2345
2426
|
}
|
|
2346
2427
|
.facet-chip[disabled] { opacity: 0.4; cursor: not-allowed; }
|
|
@@ -2352,12 +2433,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2352
2433
|
.fleet-sort select {
|
|
2353
2434
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2354
2435
|
color: var(--text); font-family: var(--mono); font-size: var(--fs-small);
|
|
2355
|
-
padding: 4px 8px; border-radius:
|
|
2436
|
+
padding: 4px 8px; border-radius: 0;
|
|
2356
2437
|
}
|
|
2357
2438
|
.fleet-sort button {
|
|
2358
2439
|
background: transparent; border: 1px solid var(--border);
|
|
2359
2440
|
color: var(--text2); font-size: var(--fs-caption); font-family: var(--mono);
|
|
2360
|
-
padding: 4px 8px; border-radius:
|
|
2441
|
+
padding: 4px 8px; border-radius: 0;
|
|
2361
2442
|
}
|
|
2362
2443
|
.fleet-sort button:hover { color: var(--text); border-color: var(--border-strong); }
|
|
2363
2444
|
|
|
@@ -2373,7 +2454,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2373
2454
|
width: 100%; min-height: 44px;
|
|
2374
2455
|
background: transparent; border: 1px solid transparent;
|
|
2375
2456
|
color: var(--text); text-align: left; font: inherit;
|
|
2376
|
-
padding: 10px 12px; border-radius:
|
|
2457
|
+
padding: 10px 12px; border-radius: 0; cursor: pointer;
|
|
2377
2458
|
transition: background-color 0.15s, border-color 0.15s;
|
|
2378
2459
|
}
|
|
2379
2460
|
.agent-row:hover { background: var(--bg-muted); border-color: var(--border); }
|
|
@@ -2396,12 +2477,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2396
2477
|
.agent-row .ar-name { font-family: var(--mono); font-size: var(--fs-body); color: var(--text); }
|
|
2397
2478
|
.agent-row .ar-meta { font-size: var(--fs-caption); color: var(--text3); display: flex; gap: 10px; flex-wrap: wrap; }
|
|
2398
2479
|
.agent-row .ar-meta .ar-domain {
|
|
2399
|
-
background: var(--bg-muted); padding: 1px 6px; border-radius:
|
|
2480
|
+
background: var(--bg-muted); padding: 1px 6px; border-radius: 0;
|
|
2400
2481
|
color: var(--text2); font-family: var(--mono); font-size: var(--fs-eyebrow);
|
|
2401
2482
|
}
|
|
2402
2483
|
.agent-row .ar-rail {
|
|
2403
2484
|
width: 80px; height: 4px;
|
|
2404
|
-
background: var(--rail-ok-bg); border-radius:
|
|
2485
|
+
background: var(--rail-ok-bg); border-radius: 0; overflow: hidden;
|
|
2405
2486
|
}
|
|
2406
2487
|
.agent-row.status-failing .ar-rail { background: var(--rail-fail-bg); }
|
|
2407
2488
|
.agent-row .ar-rail-fill {
|
|
@@ -2417,7 +2498,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2417
2498
|
.status-pill {
|
|
2418
2499
|
display: inline-flex; align-items: center; gap: 6px;
|
|
2419
2500
|
font-size: var(--fs-caption); font-family: var(--mono);
|
|
2420
|
-
padding: 2px 8px; border-radius:
|
|
2501
|
+
padding: 2px 8px; border-radius: 0;
|
|
2421
2502
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2422
2503
|
}
|
|
2423
2504
|
.status-pill .pill-dot {
|
|
@@ -2441,11 +2522,12 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2441
2522
|
width: 480px; max-width: 90vw;
|
|
2442
2523
|
background: var(--surface-drawer);
|
|
2443
2524
|
border-left: 1px solid var(--border-strong);
|
|
2444
|
-
box-shadow:
|
|
2525
|
+
box-shadow: none;
|
|
2445
2526
|
display: flex; flex-direction: column;
|
|
2446
2527
|
z-index: 10;
|
|
2447
2528
|
transform: translateX(100%);
|
|
2448
2529
|
transition: transform 200ms ease-out;
|
|
2530
|
+
border: 1px solid var(--border-strong);
|
|
2449
2531
|
}
|
|
2450
2532
|
.agent-drawer[open] { transform: translateX(0); }
|
|
2451
2533
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -2463,7 +2545,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2463
2545
|
.agent-drawer-close {
|
|
2464
2546
|
background: transparent; border: 1px solid var(--border);
|
|
2465
2547
|
color: var(--text2); font-size: var(--fs-title-m); line-height: 1;
|
|
2466
|
-
width: 28px; height: 28px; border-radius:
|
|
2548
|
+
width: 28px; height: 28px; border-radius: 0;
|
|
2467
2549
|
display: flex; align-items: center; justify-content: center;
|
|
2468
2550
|
}
|
|
2469
2551
|
.agent-drawer-close:hover { color: var(--text); border-color: var(--border-strong); }
|
|
@@ -2506,7 +2588,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2506
2588
|
.agent-actions button {
|
|
2507
2589
|
background: var(--bg-muted); border: 1px solid var(--border);
|
|
2508
2590
|
color: var(--text); font-family: var(--mono); font-size: var(--fs-small);
|
|
2509
|
-
padding: 6px 12px; border-radius:
|
|
2591
|
+
padding: 6px 12px; border-radius: 0;
|
|
2510
2592
|
}
|
|
2511
2593
|
.agent-actions button:hover { border-color: var(--border-strong); }
|
|
2512
2594
|
.agent-actions button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
|
|
@@ -2537,11 +2619,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2537
2619
|
width: 100%; max-width: 640px;
|
|
2538
2620
|
background: var(--bg-card);
|
|
2539
2621
|
border: 1px solid var(--border);
|
|
2540
|
-
border-radius:
|
|
2622
|
+
border-radius: 0;
|
|
2541
2623
|
padding: 32px;
|
|
2542
2624
|
}
|
|
2543
2625
|
.share-card h2:not(.section-title) {
|
|
2544
|
-
font-family: var(--
|
|
2626
|
+
font-family: var(--display); font-weight: 500;
|
|
2545
2627
|
font-size: 28px; letter-spacing: -0.01em;
|
|
2546
2628
|
margin: 0 0 6px;
|
|
2547
2629
|
}
|
|
@@ -2551,14 +2633,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2551
2633
|
padding: 16px 18px;
|
|
2552
2634
|
background: var(--bg-muted);
|
|
2553
2635
|
border: 1px solid var(--border);
|
|
2554
|
-
border-radius:
|
|
2636
|
+
border-radius: 0;
|
|
2555
2637
|
margin-bottom: 16px;
|
|
2556
2638
|
}
|
|
2557
2639
|
.share-toggle-row .label-grp { flex: 1; }
|
|
2558
2640
|
.share-toggle-row .label-grp .l { font-weight: 600; font-size: var(--fs-base); }
|
|
2559
2641
|
.share-toggle-row .label-grp .h { color: var(--text2); font-size: var(--fs-small); }
|
|
2560
2642
|
|
|
2561
|
-
.notif-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--bg-muted); border: 1px solid var(--border); border-radius:
|
|
2643
|
+
.notif-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--bg-muted); border: 1px solid var(--border); border-radius: 0; cursor: pointer; transition: border-color 0.15s; }
|
|
2562
2644
|
.notif-row:hover { border-color: var(--accent); }
|
|
2563
2645
|
.notif-row .emoji { font-size: 16px; flex-shrink: 0; }
|
|
2564
2646
|
.notif-row strong { color: var(--text); margin-right: 4px; }
|
|
@@ -2579,7 +2661,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2579
2661
|
background: var(--bg-card);
|
|
2580
2662
|
border-radius: 50%;
|
|
2581
2663
|
transition: transform 0.15s;
|
|
2582
|
-
box-shadow:
|
|
2664
|
+
box-shadow: none;
|
|
2583
2665
|
}
|
|
2584
2666
|
.toggle.on { background: var(--accent); }
|
|
2585
2667
|
.toggle.on::after { transform: translateX(16px); }
|
|
@@ -2590,7 +2672,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2590
2672
|
padding: 10px 12px;
|
|
2591
2673
|
background: var(--bg-card);
|
|
2592
2674
|
border: 1px solid var(--border-strong);
|
|
2593
|
-
border-radius:
|
|
2675
|
+
border-radius: 0;
|
|
2594
2676
|
margin-top: 16px;
|
|
2595
2677
|
}
|
|
2596
2678
|
.url-block .url-text {
|
|
@@ -2621,7 +2703,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2621
2703
|
display: flex; align-items: center; gap: 10px;
|
|
2622
2704
|
padding: 12px 16px;
|
|
2623
2705
|
background: var(--bg-muted);
|
|
2624
|
-
border-radius:
|
|
2706
|
+
border-radius: 0;
|
|
2625
2707
|
font-size: var(--fs-body); color: var(--text2);
|
|
2626
2708
|
margin-top: 12px;
|
|
2627
2709
|
}
|
|
@@ -2642,7 +2724,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2642
2724
|
text-transform: uppercase;
|
|
2643
2725
|
letter-spacing: 0.04em;
|
|
2644
2726
|
}
|
|
2645
|
-
.share-badge.on { background: rgba(22, 163, 74, 0.12); color: var(--status-review); }
|
|
2727
|
+
.share-badge.on { background: rgba(22, 163, 74, 0.12); color: var(--status-review-text); }
|
|
2646
2728
|
.share-badge.off { background: var(--bg-strong); color: var(--text3); }
|
|
2647
2729
|
.share-badge-dot {
|
|
2648
2730
|
width: 5px; height: 5px; border-radius: 50%;
|
|
@@ -2670,7 +2752,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2670
2752
|
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
|
2671
2753
|
}
|
|
2672
2754
|
.fchip:hover { background: var(--bg-elevated); color: var(--text); border-color: var(--border-strong); }
|
|
2673
|
-
.fchip.active { background: var(--accent); color: var(--on-
|
|
2755
|
+
.fchip.active { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
2674
2756
|
.fchip-agent.active { background: var(--chip-agent); }
|
|
2675
2757
|
.fchip-priority.active { background: var(--status-progress); color: var(--on-warm); }
|
|
2676
2758
|
.fchip-label.active { background: var(--accent); }
|
|
@@ -2699,8 +2781,8 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2699
2781
|
bottom: calc(100% + 4px); left: 0;
|
|
2700
2782
|
background: var(--bg-card);
|
|
2701
2783
|
border: 1px solid var(--border-strong);
|
|
2702
|
-
border-radius:
|
|
2703
|
-
box-shadow:
|
|
2784
|
+
border-radius: 0;
|
|
2785
|
+
box-shadow: none;
|
|
2704
2786
|
z-index: 500;
|
|
2705
2787
|
padding: 4px;
|
|
2706
2788
|
min-width: 130px;
|
|
@@ -2708,7 +2790,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2708
2790
|
.sp-item {
|
|
2709
2791
|
display: flex; align-items: center; gap: 8px;
|
|
2710
2792
|
padding: 6px 10px;
|
|
2711
|
-
border-radius:
|
|
2793
|
+
border-radius: 0;
|
|
2712
2794
|
font-size: var(--fs-small); font-weight: 500;
|
|
2713
2795
|
color: var(--text);
|
|
2714
2796
|
background: transparent; border: none;
|
|
@@ -2750,24 +2832,24 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2750
2832
|
margin-top: 4px; flex-shrink: 0;
|
|
2751
2833
|
}
|
|
2752
2834
|
.tl-content { flex: 1; display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px; }
|
|
2753
|
-
.tl-actor { font-family: var(--mono); font-size: var(--fs-caption); font-weight:
|
|
2835
|
+
.tl-actor { font-family: var(--mono); font-size: var(--fs-caption); font-weight: 500; color: var(--accent-text); }
|
|
2754
2836
|
.tl-action { color: var(--text2); }
|
|
2755
2837
|
.tl-change {
|
|
2756
2838
|
font-family: var(--mono); font-size: var(--fs-caption);
|
|
2757
2839
|
background: var(--bg-strong); color: var(--text);
|
|
2758
|
-
padding: 1px 6px; border-radius:
|
|
2840
|
+
padding: 1px 6px; border-radius: 0;
|
|
2759
2841
|
}
|
|
2760
2842
|
.tl-ts { margin-left: auto; font-family: var(--mono); font-size: var(--fs-eyebrow); color: var(--text3); }
|
|
2761
2843
|
.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; }
|
|
2762
2844
|
|
|
2763
2845
|
/* ── Security tab: hero KPI tile + activity feed ──────────────────────────── */
|
|
2764
2846
|
.kpi-hero .kpi-value-hero {
|
|
2765
|
-
font-family: var(--
|
|
2847
|
+
font-family: var(--display); font-weight: 500;
|
|
2766
2848
|
font-variant-numeric: tabular-nums;
|
|
2767
2849
|
font-size: var(--fs-num-l); line-height: 1.1; letter-spacing: -0.02em;
|
|
2768
2850
|
}
|
|
2769
2851
|
.kpi-hero .kpi-label { font-size: var(--fs-body); font-weight: 500; }
|
|
2770
|
-
.kpi-empty .kpi-value { color: var(--accent); font-size: var(--fs-title-l); }
|
|
2852
|
+
.kpi-empty .kpi-value { color: var(--accent-text); font-size: var(--fs-title-l); }
|
|
2771
2853
|
.kpi-empty .kpi-value::after {
|
|
2772
2854
|
content: ' all clear'; font-size: var(--fs-caption); color: var(--text3); font-weight: 400;
|
|
2773
2855
|
}
|
|
@@ -2793,7 +2875,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
2793
2875
|
it waits for the slide to finish (`0s linear 200ms`), so the panel does
|
|
2794
2876
|
not vanish mid-animation. */
|
|
2795
2877
|
transition: transform 200ms ease-out, visibility 0s linear 200ms;
|
|
2796
|
-
box-shadow:
|
|
2878
|
+
box-shadow: none;
|
|
2797
2879
|
}
|
|
2798
2880
|
.sidebar.open {
|
|
2799
2881
|
transform: none; visibility: visible;
|
|
@@ -3064,7 +3146,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3064
3146
|
</div>
|
|
3065
3147
|
</div>
|
|
3066
3148
|
</div>
|
|
3067
|
-
<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:
|
|
3149
|
+
<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:0;margin-bottom:10px;"></div>
|
|
3068
3150
|
|
|
3069
3151
|
<div class="inbox-section">
|
|
3070
3152
|
<div class="inbox-section-head">
|
|
@@ -3410,7 +3492,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3410
3492
|
<div id="notif-step-email">
|
|
3411
3493
|
<div style="font-size:var(--fs-caption);color:var(--text3);margin-bottom:4px;font-family:var(--mono);letter-spacing:.05em">RECIPIENT EMAIL</div>
|
|
3412
3494
|
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
3413
|
-
<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:
|
|
3495
|
+
<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:0;color:var(--text);font-size:var(--fs-base)" />
|
|
3414
3496
|
<button class="btn-black" onclick="sendVerifyCode()">Send code</button>
|
|
3415
3497
|
</div>
|
|
3416
3498
|
</div>
|
|
@@ -3419,15 +3501,15 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3419
3501
|
<div id="notif-step-code" style="display:none;margin-top:14px">
|
|
3420
3502
|
<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>
|
|
3421
3503
|
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
3422
|
-
<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:
|
|
3504
|
+
<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:0;color:var(--text);font-size:var(--fs-title-m);font-family:var(--mono);letter-spacing:.2em;text-align:center" />
|
|
3423
3505
|
<button class="btn-black" onclick="confirmVerifyCode()">Verify</button>
|
|
3424
3506
|
<button class="btn-ghost-sm" onclick="sendVerifyCode()">Resend code</button>
|
|
3425
3507
|
</div>
|
|
3426
3508
|
</div>
|
|
3427
3509
|
|
|
3428
3510
|
<!-- Step 3: verified -->
|
|
3429
|
-
<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:
|
|
3430
|
-
<span style="color:var(--accent);font-size:var(--fs-title-m)">✓</span>
|
|
3511
|
+
<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:0;display:none;align-items:center;gap:10px;font-size: var(--fs-body)">
|
|
3512
|
+
<span style="color:var(--accent-text);font-size:var(--fs-title-m)">✓</span>
|
|
3431
3513
|
<span><strong id="notif-verified-email">—</strong> verified · alerts delivered through <code>greatcto.systems/notify</code></span>
|
|
3432
3514
|
<button class="btn-ghost-sm" style="margin-left:auto" onclick="changeEmail()">Change</button>
|
|
3433
3515
|
</div>
|
|
@@ -3479,7 +3561,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
|
|
|
3479
3561
|
<h2 class="section-title">Recent notifications</h2>
|
|
3480
3562
|
<p class="sub">Last 100 alerts fired this session. Also accessible via the notification drawer (bell icon).</p>
|
|
3481
3563
|
<div id="notif-history-inline" style="display:flex;flex-direction:column;gap:0;margin-top:12px;
|
|
3482
|
-
border:1px solid var(--border);border-radius:
|
|
3564
|
+
border:1px solid var(--border);border-radius:0;overflow:hidden;font-size:var(--fs-body)">
|
|
3483
3565
|
<div id="notif-history-empty" style="padding:20px 16px;color:var(--text3);text-align:center">
|
|
3484
3566
|
No notifications yet this session.
|
|
3485
3567
|
</div>
|
|
@@ -3563,6 +3645,30 @@ function greetByHour() {
|
|
|
3563
3645
|
return 'Good evening.';
|
|
3564
3646
|
}
|
|
3565
3647
|
|
|
3648
|
+
// What is waiting on the person in the projects this screen is not showing.
|
|
3649
|
+
//
|
|
3650
|
+
// "Nothing is waiting on you" was written above a project inbox while two P0s
|
|
3651
|
+
// sat open in another project of the same operator's. True of the project,
|
|
3652
|
+
// false of the person — and the person is who reads it. The server now sends
|
|
3653
|
+
// `elsewhere` with the inbox; this turns it into the clause the headline needs.
|
|
3654
|
+
//
|
|
3655
|
+
// Three states. A payload without `elsewhere` (an older server) or one marked
|
|
3656
|
+
// unreadable says NOTHING about other projects, so the headline must not claim
|
|
3657
|
+
// "nothing elsewhere" — `known` is false and the clause is empty. Known and
|
|
3658
|
+
// empty is an honest zero. Known and non-empty is the sentence.
|
|
3659
|
+
function elsewhereClause(e) {
|
|
3660
|
+
if (!e || e.state === 'unreadable') return { known: false, text: '' };
|
|
3661
|
+
const p0 = Number(e.p0) || 0;
|
|
3662
|
+
const gates = Number(e.gates) || 0;
|
|
3663
|
+
const m = (e.projects || []).length;
|
|
3664
|
+
if (!p0 && !gates) return { known: true, text: '' };
|
|
3665
|
+
const parts = [];
|
|
3666
|
+
if (p0) parts.push(`${p0} P0`);
|
|
3667
|
+
if (gates) parts.push(`${gates} gate${gates > 1 ? 's' : ''}`);
|
|
3668
|
+
const verb = p0 + gates > 1 ? 'are' : 'is';
|
|
3669
|
+
return { known: true, text: `${parts.join(' and ')} in ${m} other project${m > 1 ? 's' : ''} ${verb} waiting on you.` };
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3566
3672
|
function renderInbox(d) {
|
|
3567
3673
|
// Kept so renderResume can ask what this section is already showing, rather
|
|
3568
3674
|
// than the two of them rendering the same task independently.
|
|
@@ -3573,8 +3679,20 @@ function renderInbox(d) {
|
|
|
3573
3679
|
// anything that matched two of them — a P0 that is also blocked read as two things
|
|
3574
3680
|
// needing attention when it was one. Older payloads have no `needs_you`, so the sum
|
|
3575
3681
|
// stays as the fallback rather than rendering nothing.
|
|
3576
|
-
|
|
3577
|
-
|
|
3682
|
+
// The badge counts the PERSON: this project's distinct needs, plus the P0s
|
|
3683
|
+
// and gates waiting in their other projects. A badge reading 0 beside two
|
|
3684
|
+
// open P0s one project over is the same lie as the headline was. When
|
|
3685
|
+
// `elsewhere` is unknown the badge shows what it knows and says so in its
|
|
3686
|
+
// title rather than adding a zero it never measured.
|
|
3687
|
+
const here = Number.isFinite(s.needs_you) ? s.needs_you : s.gates + s.p0 + s.blocked;
|
|
3688
|
+
const ew = d?.elsewhere;
|
|
3689
|
+
const ewKnown = ew && ew.state !== 'unreadable';
|
|
3690
|
+
const away = ewKnown ? (Number(ew.p0) || 0) + (Number(ew.gates) || 0) : 0;
|
|
3691
|
+
const badge = document.getElementById('nav-inbox-count');
|
|
3692
|
+
badge.textContent = here + away;
|
|
3693
|
+
badge.title = ewKnown
|
|
3694
|
+
? `${here} here · ${away} in other projects`
|
|
3695
|
+
: `${here} here · other projects not read`;
|
|
3578
3696
|
// Same rule as the all-clear card: "nothing urgent" is a claim about data we
|
|
3579
3697
|
// may not have. When a read failed, say that instead of reassuring the user.
|
|
3580
3698
|
// The headline states the SITUATION, not the time of day.
|
|
@@ -3609,9 +3727,14 @@ function renderInbox(d) {
|
|
|
3609
3727
|
greetEl.textContent = 'Some data could not be read — treat the counts below as incomplete.';
|
|
3610
3728
|
} else if (newestEventAgeH != null && newestEventAgeH > STALL_H) {
|
|
3611
3729
|
const days = Math.floor(newestEventAgeH / 24);
|
|
3730
|
+
// "Here", because the count above is this project's. What the person has
|
|
3731
|
+
// waiting in other projects is appended — or, when it could not be read,
|
|
3732
|
+
// nothing is claimed about them at all.
|
|
3733
|
+
const ew = elsewhereClause(d?.elsewhere);
|
|
3734
|
+
const tail = ew.text ? ` ${ew.text}` : (ew.known ? ' Nothing elsewhere either.' : '');
|
|
3612
3735
|
greetEl.textContent = needs
|
|
3613
|
-
? `Stalled — nothing has moved in ${days} days, and ${needs} item${needs > 1 ? 's' : ''} ${needs > 1 ? 'are' : 'is'} waiting on you
|
|
3614
|
-
: `Stalled — nothing has moved in ${days} days, and nothing is waiting on you
|
|
3736
|
+
? `Stalled — nothing has moved in ${days} days, and ${needs} item${needs > 1 ? 's' : ''} ${needs > 1 ? 'are' : 'is'} waiting on you here.${tail}`
|
|
3737
|
+
: `Stalled — nothing has moved in ${days} days, and nothing is waiting on you here.${tail}`;
|
|
3615
3738
|
} else if (needs) {
|
|
3616
3739
|
greetEl.textContent = "Here's what needs your decision.";
|
|
3617
3740
|
} else {
|
|
@@ -3653,14 +3776,22 @@ function renderInbox(d) {
|
|
|
3653
3776
|
const stalled = newestEventAgeH != null && newestEventAgeH > STALL_H;
|
|
3654
3777
|
const txt = ac.querySelector('.ac-text');
|
|
3655
3778
|
const mark = ac.querySelector('.ac-mark');
|
|
3779
|
+
// The card is about the person, so it may not say "all clear" while the
|
|
3780
|
+
// same person has a P0 open one project over. `elsewhere` arrives with the
|
|
3781
|
+
// inbox payload; unknown (older server, unreadable) claims nothing.
|
|
3782
|
+
const ew = elsewhereClause(window.__inboxData?.elsewhere);
|
|
3783
|
+
const elsewhereHtml = ew.text ? ` <b>${ew.text}</b>` : (ew.known ? ' Nothing elsewhere either.' : '');
|
|
3656
3784
|
if (stalled) {
|
|
3657
3785
|
const days = Math.floor(newestEventAgeH / 24);
|
|
3658
|
-
if (mark) mark.textContent = '
|
|
3659
|
-
if (txt) txt.innerHTML = `<b>Nothing is waiting on you.</b> Nothing has run in `
|
|
3660
|
-
+ `${days} days either — this project is idle, not finished
|
|
3786
|
+
if (mark) mark.textContent = ew.text ? '!' : '\u00b7';
|
|
3787
|
+
if (txt) txt.innerHTML = `<b>Nothing is waiting on you here.</b> Nothing has run in `
|
|
3788
|
+
+ `${days} days either — this project is idle, not finished.${elsewhereHtml}`;
|
|
3789
|
+
} else if (ew.text) {
|
|
3790
|
+
if (mark) mark.textContent = '!';
|
|
3791
|
+
if (txt) txt.innerHTML = `<b>Nothing needs your decision here.</b>${elsewhereHtml}`;
|
|
3661
3792
|
} else {
|
|
3662
3793
|
if (mark) mark.textContent = '\u2713';
|
|
3663
|
-
if (txt) txt.innerHTML =
|
|
3794
|
+
if (txt) txt.innerHTML = `<b>All clear.</b> Nothing needs your decision here — back to deep work.${elsewhereHtml}`;
|
|
3664
3795
|
}
|
|
3665
3796
|
}
|
|
3666
3797
|
}
|
|
@@ -3717,8 +3848,18 @@ function renderQueue(d) {
|
|
|
3717
3848
|
const queue = [
|
|
3718
3849
|
// An expired gate is still a gate, and saying so is the whole point: the row
|
|
3719
3850
|
// that lost its buttons for 699 hours looked like an ordinary P0.
|
|
3720
|
-
|
|
3721
|
-
|
|
3851
|
+
// ADR-009 puts gates on cost-of-undo, and the map obeys it — but until now
|
|
3852
|
+
// every gate reached this row as the same word. `gate:ship` to production and
|
|
3853
|
+
// a brief approval read identically at the moment somebody clicks, which is
|
|
3854
|
+
// the one place the doctrine had never been applied. The three states come
|
|
3855
|
+
// from the API; `unclassified` deliberately does NOT fall back to `gate`.
|
|
3856
|
+
...withReason(d?.pending_gates, (t) => {
|
|
3857
|
+
if (t.raw_status === 'blocked') return 'expired gate';
|
|
3858
|
+
const st = t.reversibility?.state;
|
|
3859
|
+
if (st === 'expensive') return 'gate · expensive to undo';
|
|
3860
|
+
if (st === 'unclassified') return 'gate · cost not classified';
|
|
3861
|
+
return 'gate';
|
|
3862
|
+
}),
|
|
3722
3863
|
...withReason(d?.p0_open, (t) => (t.is_gate ? 'expired gate' : 'P0')),
|
|
3723
3864
|
...withReason(d?.blocked, 'blocked'),
|
|
3724
3865
|
...withReason(d?.stale_in_progress, (t) => {
|
|
@@ -3865,7 +4006,7 @@ function freshnessBadge(doc) {
|
|
|
3865
4006
|
const unreadable = doc.freshnessBasis === 'unreadable';
|
|
3866
4007
|
const colour = v === 'stale' ? 'var(--status-gate)' : 'var(--text3)';
|
|
3867
4008
|
const label = v === 'stale' ? 'stale' : (unreadable ? 'could not read' : 'unknown');
|
|
3868
|
-
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:
|
|
4009
|
+
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:0;color:${colour}">${esc(label)}</span>`;
|
|
3869
4010
|
}
|
|
3870
4011
|
|
|
3871
4012
|
// Will this gate wait for you, or did the pipeline already go past it?
|
|
@@ -3900,7 +4041,7 @@ function gateTierNote(t) {
|
|
|
3900
4041
|
// It is the reading most worth a second look, so it reads differently.
|
|
3901
4042
|
const caveat = thin ? ' — one eval, coverage unmeasured' : '';
|
|
3902
4043
|
return `<div style="margin-top:3px;font-size:var(--fs-caption);color:${colour}">
|
|
3903
|
-
<span style="font-family:var(--mono);border:1px solid var(--border);border-radius:
|
|
4044
|
+
<span style="font-family:var(--mono);border:1px solid var(--border);border-radius:0;padding:0 4px">${esc(info.tier)}</span>
|
|
3904
4045
|
the pipeline did not wait for this${esc(caveat)} · <span title="${esc(info.why || '')}">${esc((info.why || '').slice(0, 90))}</span>
|
|
3905
4046
|
</div>`;
|
|
3906
4047
|
}
|
|
@@ -3939,7 +4080,7 @@ async function refreshStandDowns() {
|
|
|
3939
4080
|
// unmeasured coverage. That is the row most worth a second look, so it is
|
|
3940
4081
|
// the row that looks different.
|
|
3941
4082
|
const thin = r.tier === 'notify-thin';
|
|
3942
|
-
const tierTag = `<span style="font-family:var(--mono);font-size:var(--fs-eyebrow);padding:1px 5px;border-radius:
|
|
4083
|
+
const tierTag = `<span style="font-family:var(--mono);font-size:var(--fs-eyebrow);padding:1px 5px;border-radius:0;border:1px solid var(--border);color:${thin ? 'var(--status-gate)' : 'var(--text3)'}">${esc(r.tier || 'unknown')}</span>`;
|
|
3943
4084
|
return `
|
|
3944
4085
|
<div class="inbox-row">
|
|
3945
4086
|
<span class="id">gate:${esc(r.gate || '?')}</span>
|
|
@@ -4129,7 +4270,14 @@ function renderInboxList(rootId, items, countId, opts = {}) {
|
|
|
4129
4270
|
// carry this fact in their headings, where it was lost the moment an item moved
|
|
4130
4271
|
// between them — which is how an expired gate became indistinguishable from an
|
|
4131
4272
|
// ordinary P0 for 699 hours.
|
|
4132
|
-
const REASON_TONE = {
|
|
4273
|
+
const REASON_TONE = {
|
|
4274
|
+
'expired gate': 'gate', gate: 'gate', P0: 'p0', blocked: 'blocked',
|
|
4275
|
+
// The class is looked up HERE, not derived from the string — a reason with
|
|
4276
|
+
// no entry renders `class="why why-"` and is styled by nothing. Adding the
|
|
4277
|
+
// reason without adding the tone is how a new chip ships invisible.
|
|
4278
|
+
'gate · expensive to undo': 'gate-expensive',
|
|
4279
|
+
'gate · cost not classified': 'gate-unclassified',
|
|
4280
|
+
};
|
|
4133
4281
|
const tone = REASON_TONE[t.__reason] || (String(t.__reason || '').startsWith('stale') ? 'stale' : '');
|
|
4134
4282
|
const reasonChip = (opts.reasons && t.__reason)
|
|
4135
4283
|
? `<span class="why why-${tone}">${esc(t.__reason)}</span>`
|
|
@@ -4885,7 +5033,7 @@ function showVerdictDetail(idx) {
|
|
|
4885
5033
|
<div class="rdm-artefacts-title">Artefacts (click to open in editor)</div>
|
|
4886
5034
|
${artefactsHtml}
|
|
4887
5035
|
</div>
|
|
4888
|
-
<div class="rdm-foot">Press <kbd style="background:var(--bg-muted);border:1px solid var(--border);border-radius:
|
|
5036
|
+
<div class="rdm-foot">Press <kbd style="background:var(--bg-muted);border:1px solid var(--border);border-radius:0;padding:1px 5px;font-size:var(--fs-eyebrow);">Esc</kbd> or click outside to close</div>
|
|
4889
5037
|
</div>
|
|
4890
5038
|
`;
|
|
4891
5039
|
document.body.appendChild(modal);
|
|
@@ -5476,7 +5624,7 @@ async function loadDocs() {
|
|
|
5476
5624
|
const map = d.map ? `<div class="card"><b>Components</b>
|
|
5477
5625
|
<span class="muted">— derived ${ago(d.map.generatedAt)}, so it cannot disagree with the code</span>
|
|
5478
5626
|
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:10px">
|
|
5479
|
-
${d.map.nodes.map(n => `<div style="border:1px solid var(--border,#ddd);border-radius:
|
|
5627
|
+
${d.map.nodes.map(n => `<div style="border:1px solid var(--border,#ddd);border-radius:0;padding:8px 10px;min-width:120px">
|
|
5480
5628
|
<div><b>${esc(n.label)}</b> <span class="muted">${n.count}</span></div>
|
|
5481
5629
|
<div class="muted" style="font-size:var(--fs-caption)">${esc(n.what || '')}</div></div>`).join('')}
|
|
5482
5630
|
</div>
|
|
@@ -5615,9 +5763,9 @@ function renderDeclaredPipeline(mermaid) {
|
|
|
5615
5763
|
const name = (id) => id.replace(/_/g, '-');
|
|
5616
5764
|
return `<div style="margin-top:10px;font-size:var(--fs-small)">${edges.map(e => `
|
|
5617
5765
|
<div style="display:flex;align-items:center;gap:8px;padding:3px 0">
|
|
5618
|
-
<span style="border:1px solid var(--border,#ddd);border-radius:
|
|
5766
|
+
<span style="border:1px solid var(--border,#ddd);border-radius:0;padding:2px 8px">${esc(name(e.from))}</span>
|
|
5619
5767
|
<span class="muted">${e.join ? '⋯ join ⋯' : (e.gate ? `→ ${esc(e.gate)} →` : '→')}</span>
|
|
5620
|
-
<span style="border:1px solid var(--border,#ddd);border-radius:
|
|
5768
|
+
<span style="border:1px solid var(--border,#ddd);border-radius:0;padding:2px 8px">${esc(name(e.to))}</span>
|
|
5621
5769
|
</div>`).join('')}</div>`;
|
|
5622
5770
|
}
|
|
5623
5771
|
|
|
@@ -6242,12 +6390,71 @@ function renderDegradedBanner(id, reason, headline) {
|
|
|
6242
6390
|
/** True when any project-scoped read failed — used to suppress "all clear" claims. */
|
|
6243
6391
|
function anyDegraded() { return Object.keys(BOARD_DEGRADED).length > 0; }
|
|
6244
6392
|
|
|
6393
|
+
/**
|
|
6394
|
+
* The project the server actually served, when it is not the one that was asked
|
|
6395
|
+
* for. Null the rest of the time.
|
|
6396
|
+
*
|
|
6397
|
+
* `?project=<unknown>` falls back to the server's own directory and returns that
|
|
6398
|
+
* project's numbers — 242 tasks, $2.70, eighteen scores — byte-identical to a
|
|
6399
|
+
* real answer. The server has announced this since the BH-5 fix, in
|
|
6400
|
+
* `X-Project-Fallback`, and until now NOTHING IN THIS FILE READ THAT HEADER:
|
|
6401
|
+
* the announcement went to a channel with no listener, which is the same defect
|
|
6402
|
+
* as not announcing it, one layer further from where anyone would look.
|
|
6403
|
+
*
|
|
6404
|
+
* This is worse than the degraded case beside it. Degraded means a panel could
|
|
6405
|
+
* not be read and shows empty. This means every panel was read correctly, for
|
|
6406
|
+
* somebody else. For an operator running eighteen projects a mistyped or stale
|
|
6407
|
+
* slug — a bookmark, a shared link, a project dropped from the registry — puts
|
|
6408
|
+
* another client's spend on screen with nothing to distinguish it.
|
|
6409
|
+
*/
|
|
6410
|
+
let PROJECT_FALLBACK = null;
|
|
6411
|
+
|
|
6412
|
+
/** Announce, or clear, "these are not the numbers you asked for". */
|
|
6413
|
+
function renderProjectFallbackBanner() {
|
|
6414
|
+
const id = 'project-fallback-banner';
|
|
6415
|
+
const existing = document.getElementById(id);
|
|
6416
|
+
if (!PROJECT_FALLBACK) { if (existing) existing.remove(); return; }
|
|
6417
|
+
const { requested, serving } = PROJECT_FALLBACK;
|
|
6418
|
+
const el = existing || document.createElement('div');
|
|
6419
|
+
el.id = id;
|
|
6420
|
+
el.className = 'degraded-banner project-fallback-banner';
|
|
6421
|
+
el.setAttribute('role', 'alert');
|
|
6422
|
+
// Both names, always. "Project not found" would send the operator looking for
|
|
6423
|
+
// a missing project; the thing they need to know is whose figures they have
|
|
6424
|
+
// been reading while they looked.
|
|
6425
|
+
el.innerHTML = `<strong>${esc(requested)} was not found</strong>`
|
|
6426
|
+
+ `<span>Every panel below is showing <strong>${esc(serving || 'another project')}</strong>`
|
|
6427
|
+
+ ` — the board fell back to its own directory. Pick a project from the list to fix this.</span>`;
|
|
6428
|
+
if (!existing) {
|
|
6429
|
+
const host = document.querySelector('.workspace')
|
|
6430
|
+
|| (document.getElementById('kanban-board') || {}).parentNode;
|
|
6431
|
+
if (host) host.insertBefore(el, host.firstChild);
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6245
6435
|
function api(url, opts) {
|
|
6246
6436
|
const key = String(url).split('?')[0];
|
|
6247
6437
|
return fetch(url, opts).then(r => {
|
|
6248
6438
|
const d = r.headers && r.headers.get('X-Board-Degraded');
|
|
6249
6439
|
if (d) { try { BOARD_DEGRADED[key] = decodeURIComponent(d); } catch { BOARD_DEGRADED[key] = d; } }
|
|
6250
6440
|
else delete BOARD_DEGRADED[key];
|
|
6441
|
+
// Read on EVERY response, not once at boot: the operator switches projects
|
|
6442
|
+
// without a reload, so a banner raised by one slug has to come down when the
|
|
6443
|
+
// next one resolves. Driven by `X-Project-Resolved`, which is present on
|
|
6444
|
+
// every response — keying off the absence of X-Project-Fallback alone would
|
|
6445
|
+
// leave the banner up forever against a server too old to send either.
|
|
6446
|
+
if (r.headers) {
|
|
6447
|
+
const resolved = r.headers.get('X-Project-Resolved');
|
|
6448
|
+
if (resolved === 'fallback') {
|
|
6449
|
+
PROJECT_FALLBACK = {
|
|
6450
|
+
requested: (new URL(String(url), location.origin)).searchParams.get('project') || '(unnamed)',
|
|
6451
|
+
serving: r.headers.get('X-Project-Serving') || '',
|
|
6452
|
+
};
|
|
6453
|
+
} else if (resolved) {
|
|
6454
|
+
PROJECT_FALLBACK = null;
|
|
6455
|
+
}
|
|
6456
|
+
try { renderProjectFallbackBanner(); } catch { /* pre-DOM call */ }
|
|
6457
|
+
}
|
|
6251
6458
|
return r.json();
|
|
6252
6459
|
}).catch(() => {
|
|
6253
6460
|
// A request that never completed is the one case the degradation mechanism
|
|
@@ -7151,7 +7358,7 @@ function openSide(task) {
|
|
|
7151
7358
|
const docsSec = docRefs.length ? `
|
|
7152
7359
|
<div class="side-section"><div class="side-section-head">Documents</div>
|
|
7153
7360
|
${docRefs.map(p => `
|
|
7154
|
-
<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:
|
|
7361
|
+
<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:0;padding:6px 10px;margin:4px 0;cursor:pointer;font-family:var(--mono);font-size:var(--fs-small);color:inherit">
|
|
7155
7362
|
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(p)}</span>
|
|
7156
7363
|
<span style="color:var(--text3)">open →</span>
|
|
7157
7364
|
</button>`).join('')}
|
|
@@ -7572,7 +7779,7 @@ function renderLogDetail(log) {
|
|
|
7572
7779
|
el.innerHTML = `
|
|
7573
7780
|
<div class="log-detail-head">
|
|
7574
7781
|
<h3>${esc(log.title)}</h3>
|
|
7575
|
-
<div class="log-detail-meta">${esc(log.date)}${log.time ? ' at ' + esc(log.time) : ''}${log.duration ? ' · ' + esc(log.duration) : ''}${log.source === 'verdicts' ? ' · <span style="color:var(--accent)">auto-synthesized from verdicts</span>' : log.source === 'auto' ? ' · <span style="color:var(--accent)">auto-captured by SessionEnd hook</span>' : ''}</div>
|
|
7782
|
+
<div class="log-detail-meta">${esc(log.date)}${log.time ? ' at ' + esc(log.time) : ''}${log.duration ? ' · ' + esc(log.duration) : ''}${log.source === 'verdicts' ? ' · <span style="color:var(--accent-text)">auto-synthesized from verdicts</span>' : log.source === 'auto' ? ' · <span style="color:var(--accent-text)">auto-captured by SessionEnd hook</span>' : ''}</div>
|
|
7576
7783
|
</div>
|
|
7577
7784
|
<div class="log-section">
|
|
7578
7785
|
<div class="log-section-label">Done</div>
|
|
@@ -8303,7 +8510,7 @@ function renderNotifDrawer() {
|
|
|
8303
8510
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${esc(n.title)}</div>
|
|
8304
8511
|
<div style="font-size:var(--fs-small);color:var(--text3);margin-top:2px;line-height:1.4">${esc(n.body)}</div>
|
|
8305
8512
|
<div style="font-size:var(--fs-caption);color:var(--text3);margin-top:4px;font-family:var(--mono)">
|
|
8306
|
-
${n.project ? `<span style="color:var(--accent);margin-right:6px">${esc(n.project)}</span>` : ''}
|
|
8513
|
+
${n.project ? `<span style="color:var(--accent-text);margin-right:6px">${esc(n.project)}</span>` : ''}
|
|
8307
8514
|
${new Date(n.ts).toLocaleString()}
|
|
8308
8515
|
</div>
|
|
8309
8516
|
</div>
|
|
@@ -8481,8 +8688,7 @@ if (_costLbl) _costLbl.textContent = `Cost · last ${periodLabel(currentPeriod)}
|
|
|
8481
8688
|
<div id="notif-drawer" style="position:fixed;top:0;right:-400px;width:380px;height:100vh;
|
|
8482
8689
|
background:var(--bg-elevated);border-left:1px solid var(--border);z-index:9999;
|
|
8483
8690
|
transition:right .25s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;
|
|
8484
|
-
box-shadow
|
|
8485
|
-
<div style="padding:14px 16px;border-bottom:1px solid var(--border);
|
|
8691
|
+
box-shadow: none;border-bottom:1px solid var(--border);
|
|
8486
8692
|
display:flex;align-items:center;gap:8px;flex-shrink:0">
|
|
8487
8693
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"
|
|
8488
8694
|
style="width:16px;height:16px;color:var(--text2)">
|