penthera 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +158 -301
  3. package/bin/scan.js +2 -2
  4. package/docs/METHODOLOGY.md +236 -182
  5. package/docs/commands/README.md +2 -1
  6. package/docs/commands/init.md +114 -0
  7. package/docs/commands/scan.md +1 -1
  8. package/docs/compare/github.md +1 -1
  9. package/docs/dashboard.md +95 -152
  10. package/docs/examples/github-actions-scan.yml +1 -1
  11. package/docs/internal-audit.md +1 -1
  12. package/docs/plan.md +3 -2
  13. package/docs/references.bib +43 -33
  14. package/docs/references.md +92 -51
  15. package/lib/whitebox/frameworks.js +36 -0
  16. package/package.json +1 -1
  17. package/skills/penthera/SKILL.md +19 -5
  18. package/src/cli/deps.js +1 -1
  19. package/src/cli/detect.js +125 -0
  20. package/src/cli/onboarding.js +428 -6
  21. package/src/cli/prompt.js +42 -0
  22. package/src/cli.js +18 -5
  23. package/src/ui/api/data.js +30 -0
  24. package/src/ui/api/demo.js +13 -0
  25. package/src/ui/api/routes.js +1 -0
  26. package/src/ui/static/app.css +286 -560
  27. package/src/ui/static/index.html +8 -39
  28. package/src/ui/static/js/app.js +105 -107
  29. package/src/ui/static/js/center.js +388 -0
  30. package/src/ui/static/js/copy.js +36 -62
  31. package/src/ui/static/js/dom.js +0 -38
  32. package/src/ui/static/js/left.js +83 -0
  33. package/src/ui/static/js/right.js +270 -0
  34. package/src/ui/static/js/state.js +35 -0
  35. package/src/ui/static/js/views/ai.js +0 -68
  36. package/src/ui/static/js/views/deps.js +0 -96
  37. package/src/ui/static/js/views/evidence.js +0 -115
  38. package/src/ui/static/js/views/findings.js +0 -165
  39. package/src/ui/static/js/views/history.js +0 -92
  40. package/src/ui/static/js/views/overview.js +0 -184
  41. package/src/ui/static/js/views/run.js +0 -349
  42. package/src/ui/static/js/views/settings.js +0 -67
@@ -1,11 +1,11 @@
1
1
  /*
2
- * penthera ui :: a 1-bit desktop with a terminal inside.
2
+ * penthera ui :: one view, three columns, a terminal inside.
3
3
  *
4
- * Register: System 7 era Mac (menu bar, pinstriped title bars, dithered
5
- * desktop, rounded push buttons, alert dialogs), worn the way a calm modern
6
- * product UI wears it: pinstripes only on real windows and dialogs, hairline
7
- * group boxes, one stat strip, figure captions that say where a number came
8
- * from. The CLI's voice and its #0000ed wordmark run through all of it.
4
+ * Layout: like the Claude Code desktop app. A left sidebar of projects and
5
+ * runs, one scrolling centre (a project, or a run as a transcript), and a
6
+ * right panel of stacked cards. Chrome is calm: hairlines, not boxes. The 1-bit
7
+ * register stays in the details: pattern swatches for severity, shade bars,
8
+ * the pinstriped approval dialog, the #0000ed wordmark on the empty state.
9
9
  * Black, white and patterns do the work; #0000ed is the one accent; green is
10
10
  * kept for the cursor, the prompt and "OK".
11
11
  *
@@ -79,371 +79,104 @@
79
79
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace;
80
80
  --fs: 13px;
81
81
  --gap: 16px;
82
- --radius-btn: 6px; /* the one radius: classic push buttons. Windows stay square */
82
+ --radius-btn: 6px; /* the one radius: push buttons and the prompt bar. Panels stay square */
83
+ --left-w: 288px;
84
+ --right-w: 380px;
83
85
  --target: 28px; /* WCAG 2.5.8 floor is 24; coarse pointers get 44 below */
84
86
  }
85
87
 
86
88
  * { box-sizing: border-box; }
87
89
  [hidden] { display: none !important; }
90
+ html, body { height: 100%; }
88
91
  html { background: var(--ground); }
89
92
  body {
90
93
  margin: 0;
91
- min-height: 100dvh;
92
94
  color: var(--text-2);
95
+ background: var(--ground);
93
96
  font: var(--fs) / 1.5 var(--mono);
94
97
  font-variant-numeric: tabular-nums lining-nums;
95
98
  -webkit-font-smoothing: antialiased;
96
- /* The desktop: fine 45-degree lines, the pattern behind every window. */
97
- background:
98
- repeating-linear-gradient(45deg, var(--dot) 0 1px, transparent 1px 4px),
99
- var(--ground);
99
+ overflow: hidden;
100
100
  }
101
-
102
101
  ::selection { background: var(--accent); color: var(--on-accent); }
103
- a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
102
+ a { color: var(--accent-ink); text-underline-offset: 3px; }
104
103
  a:hover { background: var(--accent); color: var(--on-accent); text-decoration: none; }
105
104
  :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
106
105
  .sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
107
106
  .skip { position: absolute; left: 8px; top: -40px; z-index: 100; background: var(--accent); color: var(--on-accent); padding: 4px 8px; }
108
107
  .skip:focus { top: 8px; }
108
+ .pad { padding: 16px; }
109
109
 
110
- /* ── Menu bar ──────────────────────────────────────────────────────────── */
111
-
112
- .menubar {
113
- position: sticky;
114
- top: 0;
115
- z-index: 20;
116
- display: flex;
117
- align-items: stretch;
118
- gap: 4px;
119
- min-height: 30px;
120
- padding: 0 10px;
121
- background: var(--ground);
122
- border-bottom: 1px solid var(--frame);
123
- }
124
- .mb-mark { display: flex; align-items: center; gap: 8px; padding: 0 10px 0 2px; color: var(--text-1); font-weight: 700; white-space: nowrap; }
125
- .mb-mark svg { display: block; }
126
- .mb-mark .px { fill: var(--accent); }
127
- :root[data-theme="dark"] .mb-mark .px { fill: var(--accent-ink); }
128
- .menubar nav { display: flex; min-width: 0; }
129
- .menubar ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
130
- .menubar nav a {
131
- display: flex;
132
- align-items: center;
133
- height: 100%;
134
- min-height: var(--target);
135
- padding: 0 10px;
136
- color: var(--text-1);
137
- text-decoration: none;
138
- white-space: nowrap;
139
- }
140
- .menubar nav a .key { color: var(--text-3); margin-right: 6px; font-size: 11px; }
141
- .menubar nav a:hover { background: var(--frame); color: var(--ground); }
142
- .menubar nav a:hover .key { color: inherit; }
143
- .menubar nav a[aria-current="page"] { background: var(--accent); color: var(--on-accent); }
144
- .menubar nav a[aria-current="page"] .key { color: var(--on-accent); }
145
- .mb-right { margin-left: auto; display: flex; align-items: center; gap: 8px; padding-left: 8px; white-space: nowrap; }
146
- #mb-chips { display: flex; gap: 6px; }
147
- @media (max-width: 1320px) { .menubar nav a .key { display: none; } .menubar nav a { padding: 0 8px; } }
148
- .num .row-btn { text-align: right; }
149
- .mb-clock { color: var(--text-1); min-width: 5ch; text-align: right; }
150
-
151
- /* ── The window ────────────────────────────────────────────────────────── */
110
+ /* ── The three columns ─────────────────────────────────────────────────── */
152
111
 
153
- .desktop { padding: 20px 22px 30px; }
154
- .window {
155
- position: relative;
156
- max-width: 1440px;
157
- margin: 0 auto;
158
- background: var(--ground);
159
- border: 1px solid var(--frame);
160
- box-shadow: 2px 2px 0 var(--frame); /* the hard 1-bit drop shadow */
161
- }
162
- /* Registration crosshairs at the window's corners. */
163
- .window::before, .window::after {
164
- content: "+";
165
- position: absolute;
166
- color: var(--text-3);
167
- font-size: 13px;
168
- line-height: 1;
169
- pointer-events: none;
112
+ .app {
113
+ display: grid;
114
+ grid-template-columns: var(--left-w) minmax(0, 1fr) var(--right-w);
115
+ height: 100dvh;
170
116
  }
171
- .window::before { top: -15px; left: -14px; }
172
- .window::after { bottom: -16px; right: -15px; }
173
-
174
- .titlebar {
175
- position: relative;
176
- display: flex;
117
+ .app.no-left { grid-template-columns: 0 minmax(0, 1fr) var(--right-w); }
118
+ .app.no-right { grid-template-columns: var(--left-w) minmax(0, 1fr) 0; }
119
+ .app.no-left.no-right { grid-template-columns: 0 minmax(0, 1fr) 0; }
120
+ .app.no-left .left, .app.no-right .right { visibility: hidden; }
121
+ .left, .right { min-width: 0; overflow-y: auto; overflow-x: hidden; background: var(--ground); }
122
+ .left { border-right: 1px solid var(--rule); display: flex; flex-direction: column; }
123
+ .right { border-left: 1px solid var(--rule); }
124
+ .center { min-width: 0; display: flex; flex-direction: column; outline: none; overflow: hidden; }
125
+ .scrim { display: none; }
126
+
127
+ /* ── Left sidebar ──────────────────────────────────────────────────────── */
128
+
129
+ .brand { display: flex; align-items: center; gap: 8px; padding: 12px 12px 10px 14px; color: var(--text-1); font-weight: 700; }
130
+ .brand svg { display: block; flex: none; }
131
+ .brand .px { fill: var(--accent-ink); }
132
+ .brand .grow { flex: 1; }
133
+ .new-scan { margin: 2px 12px 12px; justify-content: center; }
134
+ .side-label { margin: 14px 14px 6px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-3); display: flex; justify-content: space-between; }
135
+ .side-list { list-style: none; margin: 0; padding: 0 6px; display: grid; gap: 1px; }
136
+ .item {
137
+ all: unset;
138
+ box-sizing: border-box;
139
+ display: grid;
140
+ grid-template-columns: auto minmax(0, 1fr) auto;
141
+ gap: 2px 10px;
177
142
  align-items: center;
178
- justify-content: center;
179
- height: 22px;
180
- margin: 0;
181
- padding: 0 8px;
182
- border-bottom: 1px solid var(--frame);
183
- /* Pinstripes: 1px lines on a 2px rhythm, inset from every edge. */
184
- background:
185
- repeating-linear-gradient(to bottom, var(--frame) 0 1px, transparent 1px 2px) 4px 4px / calc(100% - 8px) 13px no-repeat,
186
- var(--ground);
187
- }
188
- .titlebar .box {
189
- position: absolute;
190
- top: 4px;
191
- width: 13px;
192
- height: 13px;
193
- border: 1px solid var(--frame);
194
- background: var(--ground);
195
- box-shadow: 0 0 0 2px var(--ground);
196
- }
197
- .titlebar .box.close { left: 10px; }
198
- .titlebar .box.zoom { right: 10px; }
199
- .titlebar .box.zoom::after { content: ""; position: absolute; left: 0; top: 0; width: 6px; height: 6px; border-right: 1px solid var(--frame); border-bottom: 1px solid var(--frame); }
200
- .titlebar .title {
201
- padding: 0 10px;
202
- background: var(--ground);
143
+ width: 100%;
144
+ min-height: 40px;
145
+ padding: 6px 8px;
146
+ border-left: 2px solid transparent;
147
+ cursor: pointer;
203
148
  color: var(--text-1);
204
- font-weight: 700;
205
- font-size: 12px;
206
- white-space: nowrap;
207
- overflow: hidden;
208
- text-overflow: ellipsis;
209
- max-width: calc(100% - 64px);
210
- }
211
-
212
- .prompt {
213
- padding: 8px 20px;
214
- border-bottom: 1px solid var(--hair);
215
- color: var(--text-2);
216
- white-space: nowrap;
217
- overflow: hidden;
218
- text-overflow: ellipsis;
219
- }
220
- .prompt .user { color: var(--ok); }
221
- .prompt .path { color: var(--accent-ink); }
222
-
223
- .main { padding: 24px 20px 28px; min-width: 0; outline: none; }
224
-
225
- .status {
226
- display: flex;
227
- flex-wrap: wrap;
228
- gap: 6px 18px;
229
- padding: 6px 20px;
230
- border-top: 1px solid var(--frame);
231
- color: var(--text-3);
232
- font-size: 11px;
233
- }
234
- .status kbd { color: var(--text-1); }
235
-
236
- .cursor {
237
- display: inline-block;
238
- width: 1ch;
239
- height: 1.15em;
240
- vertical-align: text-bottom;
241
- background: var(--ok);
242
- box-shadow: var(--glow);
243
- margin-left: 1px;
244
- animation: blink 1s steps(1, end) infinite;
245
- }
246
- @keyframes blink { 50% { opacity: 0; } }
247
-
248
- /* ── Type ──────────────────────────────────────────────────────────────── */
249
-
250
- h1, h2, h3 { color: var(--text-1); font-weight: 700; letter-spacing: 0; }
251
- h1 { font-size: 17px; margin: 0 0 4px; }
252
- h2 { font-size: 13px; margin: 0 0 8px; }
253
- h3 { font-size: 13px; margin: 0 0 6px; }
254
- .lede { color: var(--text-3); margin: 0 0 22px; max-width: 78ch; }
255
- .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-3); margin: 14px 0 6px; }
256
- .t1 { color: var(--text-1); }
257
- .muted, .faint { color: var(--text-3); }
258
- .ok { color: var(--ok); }
259
- .bad { color: var(--text-1); font-weight: 700; }
260
- .warn { color: var(--text-1); }
261
- .blue { color: var(--accent-ink); }
262
- .num { text-align: right; font-variant-numeric: tabular-nums; }
263
- .strong { color: var(--text-1); font-weight: 700; }
264
- kbd { font: inherit; border: 1px solid var(--rule); border-radius: 3px; padding: 0 4px; }
265
- code { color: var(--text-1); }
266
-
267
- /* ── Group boxes ───────────────────────────────────────────────────────── */
268
-
269
- .grid { display: grid; gap: 24px var(--gap); grid-template-columns: repeat(12, minmax(0, 1fr)); }
270
- .span-12 { grid-column: span 12; }
271
- .span-8 { grid-column: span 8; }
272
- .span-7 { grid-column: span 7; }
273
- .span-6 { grid-column: span 6; }
274
- .span-5 { grid-column: span 5; }
275
- .span-4 { grid-column: span 4; }
276
-
277
- /* Every section is a small window: 1px frame, hard shadow, pinstriped bar. */
278
- .panel {
279
- position: relative;
280
- min-width: 0;
281
- border: 1px solid var(--frame);
282
- box-shadow: 2px 2px 0 var(--frame);
283
- background: var(--ground);
284
149
  }
285
- .panel-bar {
286
- position: relative;
287
- display: flex;
288
- align-items: center;
289
- gap: 10px;
290
- height: 20px;
150
+ .item:hover { background: var(--wash); }
151
+ .item:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
152
+ .item[aria-current="true"] { background: var(--wash); border-left-color: var(--accent-ink); }
153
+ .item .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
154
+ .item .sub { grid-column: 2 / -1; font-size: 11px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
155
+ .item .count { font-size: 12px; color: var(--text-2); }
156
+ .item .dot { width: 8px; height: 8px; border: 1px solid var(--frame); }
157
+ .item .dot.live { background: var(--ok); border-color: var(--ok); box-shadow: var(--glow); }
158
+ .item .dot.done { background: var(--frame); }
159
+ .group-h { padding: 8px 10px 2px; font-size: 11px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
160
+ .side-foot { margin-top: auto; display: flex; align-items: center; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--rule); font-size: 11px; color: var(--text-3); }
161
+ .side-foot .grow { flex: 1; }
162
+
163
+ /* ── Small controls ────────────────────────────────────────────────────── */
164
+
165
+ .icon {
166
+ font: inherit;
167
+ display: inline-grid;
168
+ place-items: center;
169
+ min-width: 28px;
170
+ min-height: 28px;
291
171
  padding: 0 6px;
292
- border-bottom: 1px solid var(--frame);
293
- background:
294
- repeating-linear-gradient(to bottom, var(--frame) 0 1px, transparent 1px 2px) 0 4px / 100% 11px no-repeat,
295
- var(--ground);
296
- }
297
- .panel-bar .box { width: 11px; height: 11px; flex: none; border: 1px solid var(--frame); background: var(--ground); box-shadow: 0 0 0 2px var(--ground); }
298
- .panel-h {
299
- margin: 0 auto;
300
- padding: 0 8px;
301
- background: var(--ground);
302
- color: var(--text-1);
303
- font-size: 12px;
304
- font-weight: 700;
305
- white-space: nowrap;
306
- min-width: 0;
307
- overflow: hidden;
308
- text-overflow: ellipsis;
309
- }
310
- .panel-meta { padding: 0 6px; background: var(--ground); color: var(--text-2); font-size: 11px; white-space: nowrap; }
311
- .panel-bar .grip { width: 9px; height: 11px; flex: none; background: repeating-linear-gradient(to bottom, var(--frame) 0 1px, var(--ground) 1px 3px); box-shadow: 0 0 0 2px var(--ground); }
312
- .panel-body { padding: 12px 14px 14px; min-width: 0; }
313
-
314
- /* Columns of short lists, NOBSUI-style: bold heading, a rule, then rows. */
315
- .cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
316
- .cols > div { padding: 10px 14px 12px; min-width: 0; border-left: 1px solid var(--frame); }
317
- .cols > div:first-child { border-left: 0; }
318
- .cols h3 { margin: 0 0 8px; padding-bottom: 6px; border-bottom: 1px solid var(--frame); font-size: 12px; }
319
- .rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
320
- .rows li { display: grid; grid-template-columns: 3ch minmax(0, 1fr); gap: 8px; align-items: baseline; }
321
- .rows .code { color: var(--text-3); font-size: 11px; }
322
- .rows .t { color: var(--text-1); overflow-wrap: anywhere; }
323
- .rows .t.b { font-weight: 700; }
324
- .rows .t .where { color: var(--text-3); font-weight: 400; }
325
-
326
- /* Gantt: finding lifetimes between scans. */
327
- .gantt .lane-label { fill: var(--text-1); font: 11px var(--mono); }
328
- .gantt .lane-label.b { font-weight: 700; }
329
- .gantt .lane-code { fill: var(--text-3); font: 10px var(--mono); }
330
- .gantt .bar { fill: var(--ground); stroke: var(--frame); stroke-width: 1; }
331
- .gantt .bar.open { fill: url(#pt-hatch-ink); }
332
- .gantt .bar.crit { fill: var(--frame); }
333
- .gantt .scan-line { stroke: var(--frame); stroke-width: 1; stroke-dasharray: 3 3; }
334
- .gantt .now-line { stroke: var(--frame); stroke-width: 1.5; }
335
- .gantt .chip { fill: var(--frame); }
336
- .gantt .chip-t { fill: var(--ground); font: 700 10px var(--mono); }
337
- .gantt .axis-t { fill: var(--text-2); font: 10px var(--mono); }
338
- .hatch-ink { stroke: var(--frame); stroke-width: 1; }
339
-
340
- /* Figure caption: where a number came from, set like a drawing's title block. */
341
- .fig {
342
- display: flex;
343
- justify-content: space-between;
344
- gap: 12px;
345
- margin: 12px 0 0;
346
- padding-top: 6px;
347
- border-top: 1px dotted var(--rule);
348
- font-size: 10.5px;
349
- letter-spacing: 0.1em;
350
- text-transform: uppercase;
351
- color: var(--text-3);
352
- }
353
- .panel-note { color: var(--text-3); font-size: 11px; margin: 10px 0 0; }
354
-
355
- /* A window inside the window: pinstriped title, hard shadow. Dialogs and the terminal only. */
356
- .subwin { border: 1px solid var(--frame); box-shadow: 2px 2px 0 var(--frame); background: var(--ground); min-width: 0; }
357
- .subwin > .titlebar { height: 20px; background:
358
- repeating-linear-gradient(to bottom, var(--frame) 0 1px, transparent 1px 2px) 4px 4px / calc(100% - 8px) 11px no-repeat, var(--ground); }
359
- .subwin > .titlebar .box { top: 3px; width: 11px; height: 11px; }
360
- .subwin-body { padding: 14px; }
361
-
362
- /* ── About window: the wordmark (the one bold move) ────────────────────── */
363
-
364
- .about { margin-bottom: 28px; }
365
- .about .subwin-body { display: grid; gap: 4px; padding: 22px 24px 18px; }
366
- .wordmark {
367
- margin: 0 0 8px;
368
- font-size: clamp(5px, 1.3vw, 12.5px);
369
- line-height: 1.08;
370
- white-space: pre;
371
- user-select: none;
372
- overflow: hidden;
373
- }
374
- .wordmark .fill { color: var(--accent); text-shadow: var(--logo-glow); }
375
- .wordmark .edge { color: var(--accent-ink); }
376
- :root[data-theme="light"] .wordmark .edge { color: var(--text-1); }
377
- .about .tagline { color: var(--text-1); }
378
- .about .facts { display: flex; flex-wrap: wrap; gap: 4px 22px; margin-top: 10px; color: var(--text-3); font-size: 12px; }
379
- .about .facts b { color: var(--text-1); font-weight: 700; }
380
-
381
- /* ── Stat strip ────────────────────────────────────────────────────────── */
382
-
383
- .strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); border: 1px solid var(--frame); margin-bottom: 28px; }
384
- .strip .cell { padding: 10px 14px 12px; min-width: 0; border-left: 1px solid var(--rule); }
385
- .strip .cell:first-child { border-left: 0; }
386
- .strip .label { margin: 0 0 4px; display: block; }
387
- .strip .big { font-size: 26px; line-height: 1.15; color: var(--text-1); font-weight: 700; }
388
- .strip .big .unit { font-size: 13px; font-weight: 400; color: var(--text-3); margin-left: 2px; }
389
- .strip .big.ok { color: var(--ok); }
390
- .strip .sub { color: var(--text-3); font-size: 11px; margin-top: 2px; }
391
- .big { font-size: 22px; line-height: 1.2; color: var(--text-1); font-weight: 700; }
392
-
393
- /* ── Severity: 1-bit. Word first, then a pattern swatch, never colour alone ── */
394
-
395
- .sev { white-space: nowrap; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; display: inline-flex; align-items: center; gap: 6px; color: var(--text-1); }
396
- .sev::before { content: ""; display: inline-block; width: 10px; height: 10px; border: 1px solid var(--frame); flex: none; }
397
- .sev-critical::before { background: var(--frame); }
398
- .sev-high::before { background: repeating-linear-gradient(45deg, var(--frame) 0 1.5px, transparent 1.5px 3px); }
399
- .sev-medium::before { background: conic-gradient(from 90deg at 1px 1px, transparent 50%, var(--frame) 0 75%, transparent 0) 0 0 / 2px 2px, conic-gradient(var(--frame) 25%, transparent 0) 0 0 / 2px 2px; }
400
- .sev-low::before { background: conic-gradient(from 90deg at 1px 1px, transparent 75%, var(--frame) 0) 0 0 / 3px 3px; }
401
- .sev-info::before { background: transparent; }
402
- .sev-critical { font-weight: 700; }
403
- .sev-low, .sev-info { color: var(--text-2); }
404
-
405
- /* Shade characters for text bars: █ ▓ ▒ ░ read as 1-bit patterns. */
406
- .shade { color: var(--text-1); }
407
- .shade.track { color: var(--text-4); }
408
- .bars { display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; gap: 3px 12px; align-items: baseline; }
409
- .bars .bar { white-space: pre; overflow: hidden; letter-spacing: -0.04em; }
410
- .stackbar { white-space: pre; overflow: hidden; letter-spacing: -0.04em; color: var(--text-1); line-height: 1.2; margin: 2px 0 10px; }
411
- td .stackbar { margin: 0; max-width: 14ch; }
412
-
413
- /* ── Grade ─────────────────────────────────────────────────────────────── */
414
-
415
- .grade { display: inline-grid; place-items: center; min-width: 26px; height: 22px; border: 1px solid var(--frame); color: var(--text-1); font-weight: 700; }
416
- .grade-F, .grade-D { background: var(--frame); color: var(--ground); }
417
- .grade-big { margin: 0 16px 0 0; font-size: 22px; line-height: 1; white-space: pre; color: var(--text-1); }
418
- .grade-big.grade-F, .grade-big.grade-D { background: none; color: var(--text-1); }
419
-
420
- /* ── Tables ────────────────────────────────────────────────────────────── */
421
-
422
- .table-wrap { overflow-x: auto; }
423
- table { width: 100%; border-collapse: collapse; }
424
- th {
425
- text-align: left;
426
- font-weight: 400;
427
- font-size: 11px;
428
- text-transform: uppercase;
429
- letter-spacing: 0.08em;
430
- color: var(--text-3);
431
- border-bottom: 1px solid var(--frame);
432
- padding: 4px 10px 6px 0;
433
- background: var(--ground);
434
- white-space: nowrap;
172
+ border: 1px solid transparent;
173
+ border-radius: var(--radius-btn);
174
+ background: none;
175
+ color: var(--text-2);
176
+ cursor: pointer;
435
177
  }
436
- td { padding: 7px 10px 7px 0; border-bottom: 1px dotted var(--rule); vertical-align: top; }
437
- tr.group td { padding: 4px 8px; background: var(--wash); color: var(--text-1); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; border-bottom: 1px solid var(--rule); }
438
- tr.sel td { background: var(--accent); color: var(--on-accent); }
439
- tr.sel td * { color: var(--on-accent) !important; }
440
- tr.sel .sev::before { border-color: var(--on-accent); }
441
- .row-btn { all: unset; cursor: pointer; color: var(--text-1); display: block; min-height: 20px; }
442
- .row-btn:hover { text-decoration: underline; }
443
- .row-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
444
- .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40ch; }
445
-
446
- /* ── Push buttons ──────────────────────────────────────────────────────── */
178
+ .icon:hover { background: var(--wash); color: var(--text-1); border-color: var(--rule); }
179
+ .icon[aria-pressed="true"] { color: var(--text-1); border-color: var(--rule); }
447
180
 
448
181
  .btn {
449
182
  font: inherit;
@@ -451,23 +184,23 @@ tr.sel .sev::before { border-color: var(--on-accent); }
451
184
  align-items: center;
452
185
  gap: 6px;
453
186
  min-height: var(--target);
454
- padding: 3px 14px;
187
+ padding: 3px 12px;
455
188
  color: var(--text-1);
456
189
  background: var(--ground);
457
- border: 1px solid var(--frame);
190
+ border: 1px solid var(--rule);
458
191
  border-radius: var(--radius-btn);
459
192
  cursor: pointer;
460
193
  white-space: nowrap;
461
194
  text-decoration: none;
462
195
  }
463
- .btn:hover { background: var(--wash); color: var(--text-1); }
464
- .btn:active { background: var(--frame); color: var(--ground); } /* classic press: invert */
465
- .btn[disabled] { color: var(--text-4); border-color: var(--text-4); cursor: not-allowed; background: var(--ground); }
466
- /* The default button: blue face, the thick outer ring. */
467
- .btn.primary { background: var(--accent); color: var(--on-accent); box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--frame); margin: 4px; }
196
+ .btn:hover { background: var(--wash); color: var(--text-1); border-color: var(--frame); }
197
+ .btn:active { background: var(--frame); color: var(--ground); }
198
+ .btn[disabled] { color: var(--text-4); border-color: var(--hair); cursor: not-allowed; background: var(--ground); }
199
+ .btn.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
468
200
  .btn.primary:hover { background: var(--accent-hover); color: var(--on-accent); }
469
201
  .btn.primary:active { background: var(--frame); color: var(--ground); }
470
- .btn.primary[disabled] { background: var(--ground); color: var(--text-4); box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--text-4); }
202
+ .btn.primary[disabled] { background: var(--ground); color: var(--text-4); border-color: var(--hair); }
203
+ .btn.small { min-height: 24px; padding: 0 8px; font-size: 12px; }
471
204
  .btn .k { opacity: 0.8; font-size: 11px; }
472
205
 
473
206
  .chip { display: inline-block; border: 1px solid var(--rule); padding: 0 6px; font-size: 11px; color: var(--text-2); white-space: nowrap; }
@@ -475,249 +208,242 @@ tr.sel .sev::before { border-color: var(--on-accent); }
475
208
  .chip.inv { background: var(--frame); color: var(--ground); border-color: var(--frame); }
476
209
  .chip.blue { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
477
210
 
478
- .toggle { font: inherit; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; min-height: 24px; padding: 2px 8px; border: 1px solid var(--frame); border-radius: var(--radius-btn); background: var(--ground); color: var(--text-2); cursor: pointer; }
479
- .toggle[aria-pressed="true"] { background: var(--frame); color: var(--ground); }
480
-
481
- .themebtn { font: inherit; font-size: 12px; min-height: 22px; padding: 0 8px; background: var(--ground); color: var(--text-1); border: 1px solid var(--frame); border-radius: var(--radius-btn); cursor: pointer; white-space: nowrap; }
482
- .themebtn:active { background: var(--frame); color: var(--ground); }
483
-
484
- /* ── Fields ────────────────────────────────────────────────────────────── */
485
-
486
- .field { display: grid; gap: 4px; min-width: 0; }
487
- .field > span { font-size: 12px; color: var(--text-1); font-weight: 700; }
488
- .field .hint { font-size: 11px; color: var(--text-3); font-weight: 400; }
489
- input[type="text"], input[type="search"], input[type="number"], select {
211
+ input[type="text"], input[type="search"], select {
490
212
  font: inherit;
491
213
  color: var(--text-1);
492
214
  background: var(--ground);
493
- border: 1px solid var(--frame);
494
- box-shadow: inset 1px 1px 0 var(--rule);
495
- padding: 5px 8px;
215
+ border: 1px solid var(--rule);
216
+ border-radius: var(--radius-btn);
217
+ padding: 4px 8px;
496
218
  min-height: var(--target);
497
219
  min-width: 0;
498
- width: 100%;
499
- border-radius: 0;
500
220
  }
501
221
  input::placeholder { color: var(--text-3); }
502
- input:focus-visible, select:focus-visible { outline: 2px solid var(--focus); outline-offset: 0; }
503
- input[aria-invalid="true"] { border-width: 2px; }
222
+ input:focus-visible, select:focus-visible { outline: 2px solid var(--focus); outline-offset: 0; border-color: transparent; }
504
223
  input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 14px; height: 14px; margin: 0 6px 0 0; vertical-align: -2px; }
505
- .check { display: inline-flex; align-items: center; cursor: pointer; color: var(--text-1); min-height: 24px; }
506
- .filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 14px; }
507
- .filters .filters { margin: 0; }
508
- .filters .search { flex: 1 1 220px; max-width: 360px; width: auto; }
509
- .filters select { width: auto; max-width: 280px; }
510
-
511
- .form-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 12px; align-items: end; }
512
- .c12 { grid-column: span 12; }
513
- .c6 { grid-column: span 6; }
514
- .c4 { grid-column: span 4; }
515
- .c2 { grid-column: span 2; }
516
-
517
- /* Radio group as a strip of push buttons. */
518
- .seg { display: inline-flex; flex-wrap: wrap; border: 1px solid var(--frame); border-radius: var(--radius-btn); overflow: hidden; }
519
- .seg label { position: relative; cursor: pointer; }
520
- .seg label + label { border-left: 1px solid var(--frame); }
521
- .seg input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
522
- .seg span { display: flex; align-items: center; min-height: var(--target); padding: 0 12px; color: var(--text-1); }
523
- .seg input:checked + span { background: var(--frame); color: var(--ground); }
524
- .seg input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: -4px; }
525
-
526
- .switch { font: inherit; display: inline-flex; align-items: center; gap: 10px; min-height: var(--target); padding: 0 12px; background: var(--ground); color: var(--text-1); border: 1px solid var(--frame); border-radius: var(--radius-btn); cursor: pointer; }
527
- .switch .state { font-weight: 700; }
528
- .switch[aria-checked="true"] .state { color: var(--ok); }
529
-
530
- .error { color: var(--text-1); margin: 10px 0 0; padding: 6px 10px; border: 2px solid var(--frame); }
531
- .error::before { content: "[!] "; font-weight: 700; }
532
- .empty { color: var(--text-3); padding: 6px 0; margin: 0; }
533
- .notice { border: 1px dashed var(--rule); padding: 12px 14px; color: var(--text-2); }
534
-
535
- .kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 3px 16px; margin: 0; }
536
- .kv dt { color: var(--text-3); }
537
- .kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; color: var(--text-1); }
538
-
539
- .list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
540
- .list li { display: flex; gap: 10px; align-items: baseline; min-width: 0; }
541
- .list li .grow { flex: 1; min-width: 0; overflow-wrap: anywhere; }
542
- .list a { display: inline-block; min-height: 24px; line-height: 24px; }
543
- .bullets { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
544
- .bullets li::before { content: "▸ "; color: var(--text-3); }
545
224
 
546
- /* "What next?" :: the CLI's numbered choices, as buttons. */
547
- .next { margin-top: 28px; padding-top: 14px; border-top: 1px solid var(--rule); }
548
- .next ol { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; counter-reset: n; }
549
- .next li { counter-increment: n; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
550
- .next li::before { content: counter(n) "."; color: var(--text-3); min-width: 2ch; text-align: right; }
551
- .next button { all: unset; cursor: pointer; color: var(--text-1); min-height: 22px; padding: 0 2px; }
552
- .next button:hover { background: var(--frame); color: var(--ground); }
553
- .next button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
554
- .next .why { color: var(--text-3); }
225
+ /* ── Severity, grade, bars (the 1-bit details) ─────────────────────────── */
555
226
 
556
- /* Start here (first run) */
557
- .steps { display: grid; gap: 20px; }
558
- .step h3 { margin: 0 0 4px; }
559
- .step h3 .n { color: var(--text-3); font-weight: 400; margin-right: 8px; }
560
- .step p { margin: 0 0 8px; max-width: 72ch; }
227
+ .sev { white-space: nowrap; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; display: inline-flex; align-items: center; gap: 6px; color: var(--text-1); }
228
+ .sev::before { content: ""; display: inline-block; width: 9px; height: 9px; border: 1px solid var(--frame); flex: none; }
229
+ .sev-critical::before { background: var(--frame); }
230
+ .sev-high::before { background: repeating-linear-gradient(45deg, var(--frame) 0 1.5px, transparent 1.5px 3px); }
231
+ .sev-medium::before { background: conic-gradient(from 90deg at 1px 1px, transparent 50%, var(--frame) 0 75%, transparent 0) 0 0 / 2px 2px, conic-gradient(var(--frame) 25%, transparent 0) 0 0 / 2px 2px; }
232
+ .sev-low::before { background: conic-gradient(from 90deg at 1px 1px, transparent 75%, var(--frame) 0) 0 0 / 3px 3px; }
233
+ .sev-info::before { background: transparent; }
234
+ .sev-critical { font-weight: 700; }
235
+ .sev-low, .sev-info { color: var(--text-2); }
236
+ .sw { display: inline-block; width: 9px; height: 9px; border: 1px solid var(--frame); }
237
+ .sw.sev-critical { background: var(--frame); }
561
238
 
562
- /* ── Charts ────────────────────────────────────────────────────────────── */
239
+ .grade { display: inline-grid; place-items: center; min-width: 22px; height: 20px; padding: 0 3px; border: 1px solid var(--frame); color: var(--text-1); font-weight: 700; font-size: 12px; }
240
+ .grade-F, .grade-D { background: var(--frame); color: var(--ground); }
241
+ .grade.lg { min-width: 40px; height: 36px; font-size: 20px; }
563
242
 
564
- svg.chart { display: block; width: 100%; height: auto; overflow: visible; }
565
- svg.spark { display: block; width: 100%; max-width: 140px; height: 24px; }
566
- .chart .axis { stroke: var(--frame); stroke-width: 1; }
243
+ .shade { color: var(--text-1); }
244
+ .shade.track { color: var(--text-4); }
245
+ .bars { display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; gap: 3px 10px; align-items: baseline; }
246
+ .bars .bar { white-space: pre; overflow: hidden; letter-spacing: -0.04em; }
247
+ .stackbar { white-space: pre; overflow: hidden; letter-spacing: -0.04em; color: var(--text-1); line-height: 1.2; }
248
+ svg.spark { display: block; width: 140px; max-width: 100%; height: 26px; }
249
+ .spark .s-line { fill: none; stroke: var(--series); stroke-width: 1.5; }
250
+ .spark .s-dot { fill: var(--series); }
567
251
  .chart .grid-line { stroke: var(--hair); stroke-width: 1; }
568
- .chart .tick { fill: var(--text-3); font: 10px var(--mono); }
569
- .chart .line-total { fill: none; stroke: var(--series); stroke-width: 2; }
570
- .chart .area-total { fill: url(#pt-hatch); }
571
252
  .hatch-line { stroke: var(--series); stroke-width: 1; opacity: 0.55; }
572
- .chart .line-severe { fill: none; stroke: var(--text-1); stroke-width: 1.2; stroke-dasharray: 3 3; }
573
- .chart .dot { fill: var(--ground); stroke: var(--series); stroke-width: 1.5; }
574
- .chart .col { fill: url(#pt-dither); stroke: var(--frame); stroke-width: 1; }
575
253
  .dither-dot { fill: var(--frame); }
576
- .chart .col-label { fill: var(--text-3); font: 10px var(--mono); }
577
- .chart .col-value { fill: var(--text-1); font: 11px var(--mono); }
578
- .spark .s-line { fill: none; stroke: var(--series); stroke-width: 1.5; }
579
- .spark .s-dot { fill: var(--series); }
580
- .legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11px; color: var(--text-3); margin-top: 8px; }
581
- .legend .sw { display: inline-block; width: 14px; height: 0; vertical-align: middle; margin-right: 6px; border-top: 2px solid var(--series); }
582
- .legend .sw-severe { border-top: 2px dashed var(--text-1); }
254
+ .hatch-ink { stroke: var(--frame); stroke-width: 1; }
583
255
 
584
- /* ── Findings split view ───────────────────────────────────────────────── */
256
+ /* ── Centre ────────────────────────────────────────────────────────────── */
585
257
 
586
- .split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr); gap: var(--gap); align-items: start; }
587
- .detail { position: sticky; top: 44px; max-height: calc(100dvh - 64px); overflow-y: auto; padding-top: 10px; }
588
- .detail h2:not(.panel-h) { font-size: 15px; margin: 6px 0 10px; }
589
- .detail .panel-body > section { margin-top: 16px; }
590
- .plain { border: 1px solid var(--frame); padding: 10px 12px; margin-top: 12px; display: grid; gap: 6px; }
591
- .plain p { margin: 0; }
592
- pre.block {
593
- margin: 6px 0 0;
594
- padding: 10px 12px;
595
- border: 1px solid var(--rule);
596
- background: var(--wash);
597
- white-space: pre-wrap;
598
- overflow-wrap: anywhere;
258
+ .toolbar {
259
+ display: flex;
260
+ align-items: center;
261
+ gap: 8px;
262
+ min-height: 48px;
263
+ padding: 8px 14px;
264
+ border-bottom: 1px solid var(--rule);
265
+ }
266
+ .toolbar .title { color: var(--text-1); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
267
+ .toolbar .grow { flex: 1; }
268
+ .toolbar input[type="search"] { width: 220px; }
269
+ .stream { flex: 1; overflow-y: auto; }
270
+ .col { max-width: 920px; margin: 0 auto; padding: 22px 24px 40px; }
271
+
272
+ .head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px 16px; align-items: center; margin-bottom: 18px; }
273
+ .head h1 { margin: 0; font-size: 16px; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
274
+ .head .meta { grid-column: 2; color: var(--text-3); font-size: 12px; }
275
+ .head .right-bits { grid-row: 1 / span 2; grid-column: 3; display: grid; justify-items: end; gap: 4px; }
276
+ .head .counts { color: var(--text-2); font-size: 12px; }
277
+ .head .counts b { color: var(--text-1); }
278
+
279
+ .section { margin-top: 22px; }
280
+ .section > h2 { margin: 0 0 8px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-3); display: flex; justify-content: space-between; gap: 12px; }
281
+ .rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--hair); }
282
+ .rows > li { border-bottom: 1px solid var(--hair); }
283
+ .row {
284
+ all: unset;
285
+ box-sizing: border-box;
286
+ display: grid;
287
+ grid-template-columns: 9.5em minmax(0, 1fr) auto;
288
+ gap: 2px 12px;
289
+ align-items: baseline;
290
+ width: 100%;
291
+ padding: 8px 8px;
292
+ cursor: pointer;
599
293
  color: var(--text-1);
600
- max-height: 340px;
601
- overflow: auto;
602
294
  }
295
+ .row:hover { background: var(--wash); }
296
+ .row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
297
+ .row[aria-current="true"] { background: var(--accent); color: var(--on-accent); }
298
+ .row[aria-current="true"] * { color: var(--on-accent) !important; }
299
+ .row[aria-current="true"] .sev::before { border-color: var(--on-accent); }
300
+ .row .where { grid-column: 2 / -1; color: var(--text-3); font-size: 12px; overflow-wrap: anywhere; }
301
+ .row .tag { color: var(--text-3); font-size: 11px; }
302
+ .row.b .t { font-weight: 700; }
303
+ .cols2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; }
304
+ .mini { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
305
+ .mini li { display: flex; gap: 8px; align-items: baseline; min-width: 0; color: var(--text-1); }
306
+ .mini li .grow { flex: 1; min-width: 0; overflow-wrap: anywhere; }
307
+ .muted { color: var(--text-3); }
308
+ .t1 { color: var(--text-1); }
309
+ .ok { color: var(--ok); }
310
+ .bad { color: var(--text-1); font-weight: 700; }
311
+ .blue { color: var(--accent-ink); }
312
+ .empty { color: var(--text-3); margin: 6px 0; }
313
+
314
+ /* The bold move: the CLI's blue wordmark, on the empty state only. */
315
+ .wordmark { margin: 0 0 12px; font-size: clamp(5px, 1.1vw, 11px); line-height: 1.08; white-space: pre; user-select: none; overflow: hidden; }
316
+ .wordmark .fill { color: var(--accent); text-shadow: var(--logo-glow); }
317
+ .wordmark .edge { color: var(--accent-ink); }
318
+ :root[data-theme="light"] .wordmark .edge { color: var(--text-1); }
319
+ .start h1 { margin: 0 0 6px; font-size: 16px; color: var(--text-1); }
320
+ .start .steps { display: grid; gap: 14px; margin-top: 18px; }
321
+ .start .step { display: grid; grid-template-columns: 3ch minmax(0, 1fr) auto; gap: 2px 12px; align-items: baseline; padding: 12px 0; border-top: 1px solid var(--hair); }
322
+ .start .step .n { color: var(--text-3); }
323
+ .start .step h3 { margin: 0; font-size: 13px; color: var(--text-1); }
324
+ .start .step p { grid-column: 2; margin: 0; color: var(--text-3); }
325
+
326
+ /* Transcript: a run as a stream of tool output. */
327
+ .transcript { font-size: 12.5px; line-height: 1.55; }
328
+ .transcript .ln { white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.55em; color: var(--text-2); }
329
+ .transcript .ln.cmd { color: var(--text-1); font-weight: 700; margin-top: 4px; }
330
+ .transcript .ln.cmd::before { content: "$ "; color: var(--ok); }
331
+ .transcript .ln.dim::before { content: "▸ "; color: var(--text-3); }
332
+ .transcript .ln.ok { color: var(--ok); }
333
+ .transcript .ln.bad { color: var(--text-1); font-weight: 700; }
334
+ .transcript .ln.bad::before { content: "[!] "; }
335
+ .transcript .ln.hl { color: var(--text-1); font-weight: 700; margin-top: 10px; }
336
+ .cursor { display: inline-block; width: 1ch; height: 1.15em; vertical-align: text-bottom; background: var(--ok); box-shadow: var(--glow); margin-left: 1px; animation: blink 1s steps(1, end) infinite; }
337
+ @keyframes blink { 50% { opacity: 0; } }
338
+ .run-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
339
+ .run-head h1 { margin: 0; font-size: 15px; color: var(--text-1); }
340
+
341
+ /* The approval: the one pinstriped window left, inline in the transcript. */
342
+ .dialog { margin: 14px 0; border: 1px solid var(--frame); box-shadow: 2px 2px 0 var(--frame); background: var(--ground); }
343
+ .dialog-bar { height: 18px; border-bottom: 1px solid var(--frame); display: flex; justify-content: center; align-items: center;
344
+ background: repeating-linear-gradient(to bottom, var(--frame) 0 1px, transparent 1px 2px) 0 3px / 100% 11px no-repeat, var(--ground); }
345
+ .dialog-bar span { padding: 0 8px; background: var(--ground); color: var(--text-1); font-size: 11px; font-weight: 700; }
346
+ .dialog-body { padding: 12px 14px; display: grid; gap: 8px; }
347
+ .dialog-body h3 { margin: 0; font-size: 13px; color: var(--text-1); }
348
+ .dialog-body p { margin: 0; }
349
+ .actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
350
+ .kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 3px 14px; margin: 0; }
351
+ .kv dt { color: var(--text-3); }
352
+ .kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; color: var(--text-1); }
353
+ pre.block { margin: 6px 0 0; padding: 10px 12px; border: 1px solid var(--hair); background: var(--wash); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text-1); max-height: 320px; overflow: auto; font: inherit; font-size: 12px; }
603
354
  pre.diff { white-space: pre; overflow-x: auto; }
604
355
  .diff .add { color: var(--text-1); font-weight: 700; }
605
356
  .diff .del { color: var(--text-3); text-decoration: line-through; text-decoration-color: var(--text-4); }
606
357
  .diff .hunk { color: var(--accent-ink); }
607
358
  .diff .meta { color: var(--text-3); }
608
359
 
609
- /* ── Terminal (Run) ────────────────────────────────────────────────────── */
610
-
611
- .term {
612
- min-height: 340px;
613
- max-height: 60dvh;
614
- overflow-y: auto;
615
- padding: 12px 14px;
616
- font-size: 12.5px;
617
- line-height: 1.55;
618
- color: var(--text-2);
619
- background:
620
- repeating-linear-gradient(to bottom, var(--scan) 0 1px, transparent 1px 3px),
621
- var(--ground);
622
- }
623
- .term .ln { white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.55em; }
624
- .term .ln.cmd { color: var(--text-1); font-weight: 700; }
625
- .term .ln.cmd::before { content: "$ "; color: var(--ok); }
626
- .term .ln.dim::before { content: "▸ "; color: var(--text-3); }
627
- .term .ln.ok { color: var(--ok); }
628
- .term .ln.bad { color: var(--text-1); font-weight: 700; }
629
- .term .ln.bad::before { content: "[!] "; }
630
- .term .ln.hl { color: var(--text-1); font-weight: 700; margin-top: 6px; }
631
- .term-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; padding: 8px 14px; border-bottom: 1px solid var(--hair); flex-wrap: wrap; }
632
-
633
- /* The approval: a classic alert dialog. Double frame, icon, default button right. */
634
- .alert {
635
- margin: 14px;
636
- padding: 16px 18px;
637
- border: 1px solid var(--frame);
638
- box-shadow: inset 0 0 0 3px var(--ground), inset 0 0 0 4px var(--frame), 2px 2px 0 var(--frame);
639
- background: var(--ground);
640
- display: grid;
641
- grid-template-columns: auto minmax(0, 1fr);
642
- gap: 4px 14px;
643
- }
644
- .alert .icon { grid-row: span 3; width: 34px; height: 34px; display: grid; place-items: center; border: 2px solid var(--frame); font-weight: 700; color: var(--text-1); }
645
- .alert h3 { margin: 2px 0 2px; }
646
- .alert p { margin: 0; }
647
- .alert .body { grid-column: 1 / -1; min-width: 0; }
648
- .alert .actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; align-items: center; margin-top: 10px; }
649
-
650
- .timeline { list-style: none; margin: 0; padding: 0; }
651
- .timeline > li { padding-left: 18px; position: relative; padding-bottom: 12px; }
652
- .timeline > li::before { content: ""; position: absolute; left: 0; top: 5px; width: 8px; height: 8px; background: var(--frame); }
653
- .timeline > li::after { content: ""; position: absolute; left: 4px; top: 16px; bottom: 0; border-left: 1px dotted var(--rule); }
654
- .timeline > li:last-child::after { display: none; }
655
- .timeline .fixes { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 3px; }
656
- .timeline .fixes li { display: flex; gap: 8px; min-width: 0; }
657
- .timeline .fixes .t { flex: 1; min-width: 0; overflow-wrap: anywhere; }
658
-
659
- /* History: three columns, new / fixed / still open. */
660
- .cmp { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px var(--gap); }
360
+ /* The prompt bar: one primary action. */
361
+ .bar { border-top: 1px solid var(--rule); padding: 10px 14px 12px; background: var(--ground); }
362
+ .bar-inner { max-width: 920px; margin: 0 auto; }
363
+ .bar form { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; border: 1px solid var(--frame); border-radius: var(--radius-btn); padding: 6px 6px 6px 10px; }
364
+ .bar form:focus-within { outline: 2px solid var(--focus); outline-offset: 1px; }
365
+ .bar input[type="text"] { border: 0; padding: 4px 0; min-height: 28px; width: 100%; background: transparent; }
366
+ .bar input[type="text"]:focus-visible { outline: none; }
367
+ .bar .modes { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; align-items: center; }
368
+ .mode { font: inherit; font-size: 12px; min-height: 26px; padding: 0 10px; border: 1px solid var(--rule); border-radius: var(--radius-btn); background: var(--ground); color: var(--text-2); cursor: pointer; }
369
+ .mode:hover { color: var(--text-1); border-color: var(--frame); }
370
+ .mode[aria-pressed="true"] { background: var(--frame); color: var(--ground); border-color: var(--frame); }
371
+ .bar .note { font-size: 11px; color: var(--text-3); margin-left: auto; }
372
+ .bar .confirm { display: flex; gap: 8px; align-items: center; margin-top: 8px; font-size: 12px; color: var(--text-1); flex-wrap: wrap; }
373
+ .bar .confirm input { flex: 1; min-width: 160px; }
374
+ .bar .err { margin: 8px 0 0; color: var(--text-1); font-size: 12px; }
375
+ .bar .err::before { content: "[!] "; font-weight: 700; }
376
+
377
+ /* ── Right panel: stacked cards ────────────────────────────────────────── */
378
+
379
+ .cards { display: grid; }
380
+ .card { border-bottom: 1px solid var(--rule); }
381
+ .card > summary { list-style: none; display: flex; align-items: center; gap: 8px; padding: 12px 14px; cursor: pointer; color: var(--text-1); font-weight: 700; font-size: 12px; }
382
+ .card > summary::-webkit-details-marker { display: none; }
383
+ .card > summary::before { content: "▸"; color: var(--text-3); font-weight: 400; }
384
+ .card[open] > summary::before { content: "▾"; }
385
+ .card > summary .aside { margin-left: auto; font-weight: 400; color: var(--text-3); font-size: 11px; }
386
+ .card > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
387
+ .card-body { padding: 0 14px 14px; display: grid; gap: 8px; min-width: 0; }
388
+ .card-body .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-3); margin: 6px 0 0; }
389
+ .detail-head { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--rule); }
390
+ .detail-head .grow { flex: 1; }
391
+ .detail { padding: 14px; display: grid; gap: 12px; }
392
+ .detail h2 { margin: 0; font-size: 14px; color: var(--text-1); }
393
+ .detail section > .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-3); margin: 0 0 4px; }
394
+ .plain { border: 1px solid var(--frame); padding: 10px 12px; display: grid; gap: 6px; }
395
+ .plain p { margin: 0; }
396
+ .plain b { color: var(--text-1); }
397
+ .seg { display: inline-flex; border: 1px solid var(--rule); border-radius: var(--radius-btn); overflow: hidden; }
398
+ .seg label { position: relative; cursor: pointer; }
399
+ .seg label + label { border-left: 1px solid var(--rule); }
400
+ .seg input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
401
+ .seg span { display: flex; align-items: center; min-height: 28px; padding: 0 12px; color: var(--text-1); }
402
+ .seg input:checked + span { background: var(--frame); color: var(--ground); }
403
+ .seg input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: -4px; }
404
+ .switch { font: inherit; display: inline-flex; gap: 10px; align-items: center; min-height: 28px; padding: 0 12px; background: var(--ground); color: var(--text-1); border: 1px solid var(--rule); border-radius: var(--radius-btn); cursor: pointer; }
405
+ .switch[aria-checked="true"] .state { color: var(--ok); font-weight: 700; }
406
+ kbd { font: inherit; font-size: 11px; border: 1px solid var(--rule); border-radius: 3px; padding: 0 4px; color: var(--text-1); }
661
407
 
662
- /* Scrollbars, 1-bit. */
663
- * { scrollbar-color: var(--frame) var(--ground); scrollbar-width: thin; }
408
+ * { scrollbar-color: var(--rule) var(--ground); scrollbar-width: thin; }
664
409
 
665
- /* ── Responsive ────────────────────────────────────────────────────────── */
410
+ /* ── Narrow: side panels become drawers ────────────────────────────────── */
666
411
 
667
412
  @media (max-width: 1180px) {
668
- .span-8, .span-7, .span-6, .span-5, .span-4 { grid-column: span 12; }
669
- .split { grid-template-columns: minmax(0, 1fr); }
670
- .detail { position: static; max-height: none; }
671
- .cmp { grid-template-columns: minmax(0, 1fr); }
672
- .cols { grid-template-columns: minmax(0, 1fr); }
673
- .cols > div { border-left: 0; border-top: 1px solid var(--frame); }
674
- .cols > div:first-child { border-top: 0; }
413
+ .app, .app.no-right { grid-template-columns: var(--left-w) minmax(0, 1fr); }
414
+ .app.no-left, .app.no-left.no-right { grid-template-columns: 0 minmax(0, 1fr); }
415
+ .right { position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 92vw); z-index: 30; box-shadow: -2px 0 0 var(--frame); }
416
+ .app.no-right .right { display: none; }
417
+ .app:not(.no-right) .scrim { display: block; position: fixed; inset: 0; z-index: 25; background: rgba(0, 0, 0, 0.45); }
675
418
  }
676
-
677
- @media (max-width: 820px) {
678
- .menubar { flex-wrap: wrap; padding: 0 6px; position: static; }
679
- .menubar nav { order: 3; width: 100%; border-top: 1px solid var(--hair); }
680
- .menubar nav a { padding: 0 8px; }
681
- .menubar nav a .key { display: none; }
682
- #mb-chips { display: flex; gap: 6px; }
683
- @media (max-width: 1320px) { .menubar nav a .key { display: none; } .menubar nav a { padding: 0 8px; } }
684
- .num .row-btn { text-align: right; }
685
- .mb-clock { display: none; }
686
- .desktop { padding: 12px 8px 20px; }
687
- .window::before, .window::after { display: none; }
688
- .main { padding: 18px 12px 20px; }
689
- .prompt { padding: 8px 12px; }
690
- .status { display: none; }
691
- .c6, .c4, .c2 { grid-column: span 12; }
692
- .strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
693
- .strip .cell:nth-child(3) { border-left: 0; }
694
- .strip .cell:nth-child(n + 3) { border-top: 1px solid var(--rule); }
695
- .strip .big { font-size: 20px; }
696
- .cols { grid-template-columns: minmax(0, 1fr); }
697
- .cols > div { border-left: 0; border-top: 1px solid var(--frame); }
698
- .cols > div:first-child { border-top: 0; }
699
- .hide-narrow { display: none; }
700
- .trunc { max-width: 22ch; }
701
- td svg.spark { min-width: 64px; }
419
+ @media (max-width: 760px) {
420
+ .app, .app.no-left, .app.no-right, .app.no-left.no-right { grid-template-columns: minmax(0, 1fr); }
421
+ .left { position: fixed; top: 0; left: 0; bottom: 0; width: min(320px, 88vw); z-index: 30; box-shadow: 2px 0 0 var(--frame); }
422
+ .app.no-left .left { display: none; }
423
+ .app:not(.no-left) .scrim { display: block; position: fixed; inset: 0; z-index: 25; background: rgba(0, 0, 0, 0.45); }
424
+ .col { padding: 16px 14px 28px; }
425
+ .toolbar { padding: 8px 10px; }
426
+ .toolbar input[type="search"] { width: 120px; }
427
+ .row { grid-template-columns: minmax(0, 1fr) auto; }
428
+ .row .sev { grid-column: 1 / -1; }
429
+ .row .where { grid-column: 1 / -1; }
430
+ .cols2 { grid-template-columns: minmax(0, 1fr); }
431
+ .head { grid-template-columns: auto minmax(0, 1fr); }
432
+ .head .right-bits { grid-row: auto; grid-column: 1 / -1; justify-items: start; }
433
+ .bar .note { display: none; }
702
434
  }
703
-
704
435
  @media (pointer: coarse) {
705
436
  :root { --target: 44px; }
706
- .menubar nav a { min-height: 44px; }
707
- .row-btn, .next button { min-height: 32px; }
437
+ .item, .row { min-height: 44px; }
708
438
  }
709
-
710
- /* ── Reduced motion: no typing, no blink. Content is never hidden. ──────── */
711
-
712
439
  @media (prefers-reduced-motion: reduce) {
713
440
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
714
441
  .cursor { opacity: 1; }
715
442
  }
716
- .week { margin-bottom: 26px; }
717
- .week .panel-body { padding: 0; }
718
- .cols .row-btn { display: inline; min-height: 0; }
719
- .panel-body > .cols { margin: -12px -14px -14px; }
720
- .stack { margin-top: 24px; }
721
- .week .panel-body > .cols { margin: 0; }
722
- svg.chart.gantt { min-width: 760px; }
723
- @media (max-width: 520px) { .panel-meta { display: none; } }
443
+ .run-head .grow, .actions .grow { flex: 1; }
444
+ .card-body .head { margin: 0; grid-template-columns: auto minmax(0, 1fr); }
445
+ .card-body, .detail, .dialog-body { grid-template-columns: minmax(0, 1fr); }
446
+ .card-body > *, .detail > *, .dialog-body > * { min-width: 0; }
447
+ .mini li.stacked { display: grid; gap: 3px; }
448
+ .chips { display: flex; flex-wrap: wrap; gap: 3px; }
449
+ .bars .bar { line-height: 1.3; max-height: 1.3em; }