fraim-framework 2.0.124 → 2.0.127

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 (46) hide show
  1. package/bin/fraim.js +1 -1
  2. package/dist/src/ai-hub/catalog.js +280 -44
  3. package/dist/src/ai-hub/desktop-main.js +2 -2
  4. package/dist/src/ai-hub/hosts.js +384 -10
  5. package/dist/src/ai-hub/server.js +255 -9
  6. package/dist/src/cli/commands/add-ide.js +4 -3
  7. package/dist/src/cli/commands/first-run.js +61 -0
  8. package/dist/src/cli/commands/hub.js +4 -4
  9. package/dist/src/cli/commands/init-project.js +4 -4
  10. package/dist/src/cli/commands/setup.js +4 -3
  11. package/dist/src/cli/commands/sync.js +21 -2
  12. package/dist/src/cli/doctor/checks/ide-config-checks.js +20 -2
  13. package/dist/src/cli/fraim.js +2 -0
  14. package/dist/src/cli/mcp/ide-formats.js +29 -1
  15. package/dist/src/cli/mcp/mcp-server-registry.js +1 -0
  16. package/dist/src/cli/setup/auto-mcp-setup.js +14 -8
  17. package/dist/src/cli/setup/ide-detector.js +32 -1
  18. package/dist/src/cli/setup/ide-global-integration.js +5 -1
  19. package/dist/src/cli/setup/ide-invocation-surfaces.js +70 -17
  20. package/dist/src/cli/setup/mcp-config-generator.js +12 -1
  21. package/dist/src/cli/utils/agent-adapters.js +12 -2
  22. package/dist/src/cli/utils/project-bootstrap.js +4 -3
  23. package/dist/src/core/quality-evidence.js +81 -8
  24. package/dist/src/core/utils/git-utils.js +32 -7
  25. package/dist/src/core/utils/job-aliases.js +47 -0
  26. package/dist/src/core/utils/workflow-parser.js +3 -5
  27. package/dist/src/first-run/install-state.js +68 -0
  28. package/dist/src/first-run/server.js +153 -0
  29. package/dist/src/first-run/session-service.js +302 -0
  30. package/dist/src/first-run/types.js +40 -0
  31. package/dist/src/local-mcp-server/agent-token-prices.js +114 -0
  32. package/dist/src/local-mcp-server/codex-token-adapter.js +232 -0
  33. package/dist/src/local-mcp-server/learning-context-builder.js +21 -8
  34. package/dist/src/local-mcp-server/otlp-metrics-receiver.js +7 -1
  35. package/dist/src/local-mcp-server/stdio-server.js +70 -17
  36. package/dist/src/local-mcp-server/token-adapter-registry.js +64 -0
  37. package/dist/src/local-mcp-server/usage-collector.js +25 -0
  38. package/index.js +83 -83
  39. package/package.json +7 -1
  40. package/public/ai-hub/index.html +149 -102
  41. package/public/ai-hub/script.js +1154 -271
  42. package/public/ai-hub/styles.css +753 -450
  43. package/public/first-run/index.html +221 -0
  44. package/public/first-run/script.js +361 -0
  45. package/dist/src/cli/services/device-flow-service.js +0 -83
  46. package/dist/src/local-mcp-server/prometheus-scraper.js +0 -152
@@ -1,568 +1,871 @@
1
1
  :root {
2
2
  color-scheme: light;
3
- --visa-blue: #1434cb;
4
- --visa-gold: #f7b600;
5
- --ink: #10233f;
6
- --muted: #607089;
3
+ --bg: #f4f6f2;
7
4
  --surface: #ffffff;
8
- --surface-muted: #f6f8fc;
9
- --surface-strong: #edf2ff;
10
- --surface-soft: rgba(255, 255, 255, 0.72);
11
- --line: #d8e1f0;
12
- --success: #12785e;
13
- --danger: #a22738;
14
- --shadow: 0 22px 48px rgba(16, 35, 63, 0.08);
15
- --shadow-soft: 0 12px 28px rgba(16, 35, 63, 0.06);
16
- }
17
-
18
- * {
19
- box-sizing: border-box;
20
- }
21
-
22
- html,
23
- body {
24
- min-height: 100%;
25
- }
26
-
5
+ --soft: #f7faf6;
6
+ --line: #e3e8df;
7
+ --text: #1f2a24;
8
+ --muted: #6b7a72;
9
+ --accent: #3d8a6e;
10
+ --accent-strong: #2f7359;
11
+ --accent-soft: #eaf3ee;
12
+ --warn: #b08442;
13
+ --warn-soft: #fffaf0;
14
+ --danger: #a24747;
15
+ --shadow: 0 1px 2px rgba(20, 40, 30, 0.04);
16
+ --shadow-lg: 0 12px 36px rgba(20, 40, 30, 0.18);
17
+ /* Issue #347 — picker group dots. Decorative only; the picker uses
18
+ these to scan groups by color without adding semantic weight. Kept
19
+ as variables so future themes flow through here. */
20
+ --picker-delegation: #5b7eb0;
21
+ --picker-learning: #8b5fbf;
22
+ }
23
+
24
+ * { box-sizing: border-box; }
25
+ html, body { margin: 0; padding: 0; }
26
+ [hidden] { display: none !important; }
27
+
28
+ html, body { height: 100%; }
27
29
  body {
28
- margin: 0;
29
- font-family: "Segoe UI", Inter, Arial, sans-serif;
30
- color: var(--ink);
31
- background:
32
- linear-gradient(180deg, rgba(20, 52, 203, 0.08), rgba(20, 52, 203, 0) 16%),
33
- linear-gradient(135deg, rgba(247, 182, 0, 0.12), rgba(247, 182, 0, 0) 22%),
34
- #f4f7fc;
35
- overflow-x: hidden;
36
- overflow-y: auto;
37
- }
38
-
39
- button,
40
- input,
41
- textarea {
42
- font: inherit;
30
+ font-family: -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
31
+ color: var(--text);
32
+ background: var(--bg);
33
+ line-height: 1.5;
34
+ font-size: 15px;
35
+ /* Issue #339 fix: bound the page to the viewport. The conversation
36
+ pane scrolls internally instead of growing the entire page. */
37
+ overflow: hidden;
43
38
  }
44
39
 
45
- a {
46
- color: var(--visa-blue);
47
- }
40
+ button { font: inherit; cursor: pointer; }
48
41
 
49
- .app-shell {
50
- max-width: 1480px;
42
+ .page {
43
+ max-width: 1080px;
51
44
  margin: 0 auto;
52
- padding: 18px 24px 28px;
53
- min-height: 100dvh;
54
- display: grid;
55
- grid-template-rows: auto 1fr;
56
- gap: 18px;
57
- }
58
-
59
- .app-header {
60
- display: grid;
61
- grid-template-columns: minmax(0, 1fr) minmax(360px, 440px);
62
- gap: 18px;
63
- align-items: stretch;
64
- }
65
-
66
- .brand-lockup {
67
- padding: 18px 0 8px;
68
- display: grid;
69
- gap: 4px;
70
- }
71
-
72
- .brand-title {
73
- font-size: 32px;
74
- line-height: 1.05;
75
- font-weight: 800;
76
- color: var(--ink);
45
+ padding: 24px;
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: 20px;
49
+ height: 100vh;
50
+ min-height: 0;
77
51
  }
78
52
 
79
- .brand-subtitle,
80
- .section-label,
81
- .helper-text,
82
- .muted {
83
- color: var(--muted);
53
+ /* Header */
54
+ .header {
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+ gap: 16px;
84
59
  }
85
-
86
- .section-label {
87
- font-size: 12px;
88
- font-weight: 700;
89
- letter-spacing: 0;
90
- text-transform: uppercase;
60
+ .header h1 {
61
+ font-size: 22px;
62
+ font-weight: 600;
63
+ margin: 0;
64
+ letter-spacing: -0.01em;
91
65
  }
92
-
93
- .section-label {
66
+ .project-button {
94
67
  display: inline-flex;
95
68
  align-items: center;
96
69
  gap: 8px;
97
- }
98
-
99
- h1,
100
- h2,
101
- p {
102
- margin: 0;
103
- }
104
-
105
- .topbar-card,
106
- .card {
107
- background: var(--surface-soft);
108
- backdrop-filter: blur(16px);
70
+ background: var(--surface);
109
71
  border: 1px solid var(--line);
110
- border-radius: 8px;
72
+ border-radius: 10px;
73
+ padding: 8px 14px;
74
+ color: var(--text);
111
75
  box-shadow: var(--shadow);
76
+ max-width: 360px;
112
77
  }
113
-
114
- .topbar-card {
115
- width: 100%;
116
- padding: 16px 18px;
117
- display: flex;
118
- gap: 16px;
119
- align-items: start;
120
- justify-content: space-between;
121
- }
122
-
123
- .workspace-card {
124
- align-self: stretch;
125
- }
126
-
127
- .workspace-section {
128
- display: grid;
129
- gap: 10px;
78
+ .project-button:hover { background: var(--soft); }
79
+ .project-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
80
+ .project-button .folder { color: var(--muted); font-size: 13px; }
81
+ .project-button strong {
82
+ font-weight: 600;
83
+ white-space: nowrap;
84
+ overflow: hidden;
85
+ text-overflow: ellipsis;
130
86
  }
131
87
 
132
- .workspace-section-inline {
133
- min-width: 0;
134
- flex: 1 1 auto;
88
+ /* Welcome line */
89
+ .welcome {
90
+ background: var(--surface);
91
+ border: 1px solid var(--line);
92
+ border-radius: 14px;
93
+ padding: 18px 24px;
94
+ box-shadow: var(--shadow);
95
+ font-size: 15px;
96
+ line-height: 1.7;
97
+ color: var(--muted);
135
98
  }
136
-
137
- .employee-picker,
138
- .category-picker,
139
- .template-chips,
140
- .project-actions,
141
- .composer-actions {
142
- display: flex;
143
- flex-wrap: wrap;
144
- gap: 10px;
99
+ .welcome strong.you { color: var(--text); font-weight: 600; }
100
+ .welcome .concept {
101
+ position: relative;
102
+ font-weight: 600;
103
+ color: var(--accent);
145
104
  }
146
-
147
- .employee-chip,
148
- .category-chip,
149
- .template-chip,
150
- .secondary-button,
151
- .primary-button {
152
- border-radius: 7px;
153
- border: 1px solid var(--line);
154
- padding: 10px 14px;
155
- background: rgba(255, 255, 255, 0.9);
156
- color: var(--ink);
105
+ .welcome .info {
106
+ display: inline-flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ width: 16px;
110
+ height: 16px;
111
+ border-radius: 50%;
112
+ background: var(--accent-soft);
113
+ color: var(--accent);
114
+ font-size: 11px;
115
+ font-weight: 700;
116
+ margin-left: 3px;
117
+ margin-right: 1px;
157
118
  cursor: pointer;
158
- transition: border-color 120ms ease, background 120ms ease, transform 120ms ease, box-shadow 120ms ease;
119
+ vertical-align: 1px;
120
+ border: none;
121
+ font-family: serif;
122
+ font-style: italic;
123
+ }
124
+ .welcome .info:hover { background: var(--accent); color: #fff; }
125
+ .welcome .info:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
126
+ .popover {
127
+ position: absolute;
128
+ top: calc(100% + 10px);
129
+ left: 0;
130
+ width: 320px;
131
+ /* Hard cap + internal scroll so a verbose explanation never clips off
132
+ the bottom of the viewport (was a real bug — popover content used to
133
+ run past the bottom edge with no way to read it). */
134
+ max-height: min(60vh, 480px);
135
+ overflow-y: auto;
136
+ background: var(--surface);
137
+ border: 1px solid var(--line);
138
+ border-radius: 12px;
139
+ padding: 14px 16px;
140
+ box-shadow: var(--shadow-lg);
141
+ z-index: 50;
142
+ font-weight: 400;
143
+ font-size: 14px;
144
+ color: var(--text);
145
+ line-height: 1.5;
146
+ display: none;
159
147
  }
160
-
161
- .employee-chip:hover,
162
- .category-chip:hover,
163
- .template-chip:hover,
164
- .secondary-button:hover,
165
- .primary-button:hover {
166
- transform: translateY(-1px);
148
+ .popover.open { display: block; }
149
+ .popover .pop-title {
150
+ display: block;
151
+ font-weight: 600;
152
+ color: var(--text);
153
+ margin-bottom: 6px;
167
154
  }
168
-
169
- .employee-chip.active,
170
- .category-chip.active,
171
- .template-chip.active {
172
- border-color: var(--visa-blue);
173
- background: var(--surface-strong);
174
- box-shadow: inset 0 0 0 1px rgba(20, 52, 203, 0.08);
155
+ .popover .pop-link {
156
+ display: inline-block;
157
+ margin-top: 10px;
158
+ color: var(--accent);
159
+ font-size: 13px;
160
+ text-decoration: none;
161
+ font-weight: 500;
175
162
  }
176
-
177
- .employee-chip.unavailable {
178
- opacity: 0.55;
163
+ .popover .pop-link:hover { text-decoration: underline; }
164
+ .popover .pop-jobs {
165
+ display: none;
166
+ margin-top: 10px;
167
+ padding-top: 10px;
168
+ border-top: 1px solid var(--line);
179
169
  }
180
-
181
- .primary-button {
182
- background: var(--visa-blue);
183
- border-color: var(--visa-blue);
184
- color: #fff;
185
- font-weight: 700;
186
- box-shadow: 0 12px 24px rgba(20, 52, 203, 0.2);
170
+ .popover .pop-jobs.open { display: grid; gap: 8px; }
171
+ .popover .pop-jobs .job-row {
172
+ display: grid;
173
+ gap: 1px;
174
+ font-size: 13px;
187
175
  }
188
-
189
- .primary-button:disabled,
190
- .secondary-button:disabled {
191
- opacity: 0.45;
192
- cursor: not-allowed;
176
+ .popover .pop-jobs .job-row strong {
177
+ color: var(--accent);
178
+ font-weight: 600;
193
179
  }
194
-
195
- .workspace {
196
- display: grid;
197
- grid-template-columns: 340px minmax(0, 1fr);
198
- gap: 18px;
199
- align-items: start;
180
+ .popover .pop-jobs .job-row span {
181
+ color: var(--muted);
200
182
  }
201
183
 
202
- .workspace-intro {
203
- grid-column: 1 / -1;
204
- padding: 18px 20px;
184
+ /* Layout */
185
+ .layout {
205
186
  display: grid;
206
- grid-template-columns: minmax(0, 1fr) auto;
207
- gap: 18px;
208
- align-items: end;
187
+ grid-template-columns: 260px minmax(0, 1fr);
188
+ gap: 20px;
189
+ align-items: stretch;
190
+ flex: 1;
191
+ min-height: 0;
209
192
  }
210
193
 
211
- .workspace-intro-copy,
212
- .workspace-intro-actions {
194
+ /* Left rail */
195
+ .rail {
213
196
  display: grid;
214
- gap: 10px;
197
+ grid-template-columns: minmax(0, 1fr);
198
+ grid-auto-rows: min-content;
199
+ gap: 8px;
200
+ min-width: 0;
201
+ align-self: start;
202
+ max-height: 100%;
203
+ overflow-y: auto;
215
204
  }
216
-
217
- .workspace-intro-actions {
218
- justify-items: end;
205
+ .new-conv {
206
+ width: 100%;
207
+ background: var(--accent);
208
+ color: #fff;
209
+ border: none;
210
+ border-radius: 10px;
211
+ padding: 10px 14px;
212
+ font-weight: 600;
213
+ box-shadow: var(--shadow);
219
214
  }
220
-
221
- .job-panel,
222
- .interaction-panel {
215
+ .new-conv:hover { background: var(--accent-strong); }
216
+ .new-conv:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
217
+ .conv-list {
223
218
  display: grid;
224
- gap: 18px;
219
+ grid-template-columns: minmax(0, 1fr);
220
+ gap: 4px;
221
+ margin-top: 4px;
222
+ min-width: 0;
225
223
  }
226
-
227
- .job-panel {
228
- align-content: start;
224
+ .conv-item {
225
+ width: 100%;
226
+ text-align: left;
227
+ background: transparent;
228
+ border: 1px solid transparent;
229
+ border-radius: 10px;
230
+ padding: 10px 12px;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: space-between;
234
+ gap: 8px;
235
+ color: var(--text);
229
236
  }
230
-
231
- .interaction-panel {
232
- align-content: start;
237
+ .conv-item:hover { background: var(--soft); }
238
+ .conv-item.active {
239
+ background: var(--surface);
240
+ border-color: var(--line);
241
+ box-shadow: var(--shadow);
233
242
  }
234
-
235
- .panel-head {
236
- display: grid;
237
- gap: 18px;
238
- padding: 18px;
243
+ .conv-title { font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
244
+ .conv-status {
245
+ font-size: 12px;
246
+ color: var(--muted);
247
+ font-weight: 500;
248
+ flex-shrink: 0;
239
249
  }
250
+ .conv-status.running { color: var(--accent); }
251
+ .conv-status.attention { color: var(--warn); }
252
+ .conv-status.failed { color: var(--danger); }
240
253
 
241
- .panel-copy {
254
+ /* Conversation pane — fills the layout column vertically. Inner regions
255
+ manage their own height; the .messages list is the only scrollable area. */
256
+ .conversation {
257
+ background: var(--surface);
258
+ border: 1px solid var(--line);
259
+ border-radius: 14px;
260
+ box-shadow: var(--shadow);
261
+ padding: 18px 20px;
262
+ display: flex;
263
+ flex-direction: column;
264
+ gap: 14px;
265
+ min-height: 0;
266
+ min-width: 0;
267
+ height: 100%;
268
+ overflow: hidden;
269
+ }
270
+ .empty-state {
242
271
  display: grid;
243
- gap: 10px;
272
+ gap: 8px;
273
+ place-items: center;
274
+ text-align: center;
275
+ color: var(--muted);
276
+ padding: 80px 16px;
244
277
  }
278
+ .empty-state h3 { margin: 0; color: var(--text); font-weight: 600; font-size: 17px; }
279
+ .empty-state p { margin: 0; max-width: 320px; }
245
280
 
246
- .panel-intro {
247
- font-size: 18px;
248
- line-height: 1.5;
249
- color: var(--ink);
281
+ #active-conv {
282
+ display: flex;
283
+ flex-direction: column;
284
+ gap: 12px;
285
+ min-width: 0;
286
+ flex: 1;
287
+ min-height: 0;
250
288
  }
251
289
 
252
- .panel-title {
253
- font-size: 24px;
254
- line-height: 1.1;
290
+ .conv-header,
291
+ .coach,
292
+ .micro,
293
+ .progress {
294
+ flex-shrink: 0;
295
+ }
296
+ .conv-header {
297
+ display: flex;
298
+ flex-wrap: wrap;
299
+ align-items: baseline;
300
+ column-gap: 12px;
301
+ row-gap: 4px;
255
302
  }
303
+ .conv-header h2 { flex: 1 1 auto; min-width: 0; }
304
+ #artifact-slot { flex: 0 0 auto; }
256
305
 
257
- .project-path {
258
- min-width: 230px;
259
- background: rgba(255, 255, 255, 0.78);
260
- border: 1px solid var(--line);
261
- border-radius: 7px;
262
- padding: 0 14px;
263
- box-shadow: none;
306
+ .conv-header h2 { margin: 0; font-size: 18px; font-weight: 600; }
307
+ .conv-job { color: var(--muted); font-size: 14px; margin-top: 2px; }
308
+
309
+ .section-title {
310
+ font-size: 13px;
311
+ font-weight: 600;
312
+ color: var(--muted);
313
+ text-transform: uppercase;
314
+ letter-spacing: 0.05em;
315
+ margin-bottom: 8px;
264
316
  }
265
317
 
266
- .project-path summary {
267
- list-style: none;
318
+ /* Progress is now a single-line status strip (was a tall card) so the
319
+ messages region gets the vertical space it needs. */
320
+ .progress {
321
+ background: var(--soft);
322
+ border: 1px solid var(--line);
323
+ border-radius: 8px;
324
+ padding: 8px 12px;
325
+ font-size: 13px;
268
326
  display: flex;
269
327
  align-items: center;
270
- justify-content: space-between;
271
- gap: 12px;
272
- padding: 12px 0;
273
- cursor: pointer;
274
- font-weight: 600;
328
+ gap: 10px;
329
+ min-width: 0;
330
+ overflow: hidden;
275
331
  }
276
-
277
- .project-path summary::-webkit-details-marker {
278
- display: none;
332
+ .progress .stage {
333
+ font-weight: 600;
334
+ color: var(--text);
335
+ flex-shrink: 0;
279
336
  }
280
-
281
- .project-path-preview {
282
- min-width: 0;
283
- max-width: 180px;
337
+ .progress .latest {
338
+ color: var(--muted);
339
+ white-space: nowrap;
284
340
  overflow: hidden;
285
341
  text-overflow: ellipsis;
286
- white-space: nowrap;
287
- color: var(--muted);
288
- font-size: 13px;
289
- font-weight: 500;
342
+ min-width: 0;
290
343
  }
291
-
292
- .project-path-body {
344
+ .progress .stage::before {
345
+ content: "";
346
+ display: inline-block;
347
+ width: 7px;
348
+ height: 7px;
349
+ border-radius: 50%;
350
+ background: var(--accent);
351
+ margin-right: 6px;
352
+ vertical-align: 1px;
353
+ animation: pulse 1.4s infinite;
354
+ }
355
+ .progress.done .stage::before { background: #6cb39a; animation: none; }
356
+ .progress.attention .stage::before { background: var(--warn); animation: none; }
357
+ .progress.failed .stage::before { background: var(--danger); animation: none; }
358
+ @keyframes pulse {
359
+ 0%, 100% { opacity: 1; transform: scale(1); }
360
+ 50% { opacity: 0.5; transform: scale(0.8); }
361
+ }
362
+
363
+ .messages {
293
364
  display: grid;
294
- gap: 12px;
295
- padding: 0 0 16px;
365
+ grid-template-columns: minmax(0, 1fr);
366
+ gap: 10px;
367
+ margin-top: 12px;
368
+ min-width: 0;
369
+ /* Scroll messages here, not the page. */
370
+ flex: 1;
371
+ min-height: 0;
372
+ overflow-y: auto;
373
+ padding-right: 4px;
296
374
  }
297
-
298
- .text-input,
299
- .composer {
300
- width: 100%;
375
+ .message { word-wrap: break-word; overflow-wrap: anywhere; }
376
+ .message {
377
+ border-radius: 10px;
378
+ padding: 10px 14px;
379
+ font-size: 14px;
380
+ line-height: 1.5;
381
+ animation: slidein 280ms ease;
382
+ }
383
+ @keyframes slidein {
384
+ from { opacity: 0; transform: translateY(6px); }
385
+ to { opacity: 1; transform: translateY(0); }
386
+ }
387
+ .message.manager {
388
+ background: var(--accent-soft);
389
+ border: 1px solid #c5dcd0;
390
+ color: var(--text);
391
+ }
392
+ .message.employee {
393
+ background: var(--soft);
301
394
  border: 1px solid var(--line);
302
- border-radius: 7px;
303
- background: var(--surface-muted);
304
- padding: 11px 12px;
305
- color: var(--ink);
395
+ color: var(--text);
306
396
  }
307
-
308
- .composer {
309
- min-height: 140px;
310
- resize: vertical;
311
- line-height: 1.5;
397
+ .message .who {
398
+ font-size: 11px;
399
+ font-weight: 600;
400
+ text-transform: uppercase;
401
+ letter-spacing: 0.04em;
402
+ color: var(--muted);
403
+ display: block;
404
+ margin-bottom: 4px;
312
405
  }
313
406
 
314
- .job-list-card {
315
- padding: 16px;
316
- display: grid;
317
- gap: 14px;
407
+ /* Inline artifact pill that sits in the .conv-header next to the title.
408
+ Compact so it never crowds Coach or Micro-manage. Hover reveals the
409
+ path so the user can locate the file. */
410
+ .artifact {
411
+ display: inline-flex;
412
+ align-items: center;
413
+ gap: 6px;
414
+ background: var(--warn-soft);
415
+ border: 1px solid #ecd9b0;
416
+ border-radius: 999px;
417
+ padding: 4px 10px;
418
+ font-size: 12px;
419
+ color: var(--text);
420
+ max-width: 320px;
421
+ animation: slidein 280ms ease;
422
+ }
423
+ .artifact-dot {
424
+ width: 6px;
425
+ height: 6px;
426
+ border-radius: 50%;
427
+ background: var(--warn);
428
+ flex-shrink: 0;
429
+ }
430
+ .artifact-label {
431
+ font-weight: 600;
432
+ color: var(--warn);
433
+ text-transform: uppercase;
434
+ letter-spacing: 0.04em;
435
+ font-size: 10px;
436
+ margin-right: 2px;
318
437
  }
319
-
320
- .job-list {
321
- display: grid;
322
- gap: 8px;
323
- overflow: visible;
324
- align-content: start;
438
+ .artifact-name {
439
+ font-weight: 500;
440
+ white-space: nowrap;
441
+ overflow: hidden;
442
+ text-overflow: ellipsis;
443
+ min-width: 0;
325
444
  }
445
+ .artifact-where { display: none; } /* full path lives in tooltip via title attr */
326
446
 
327
- .job-list button {
447
+ .coach textarea {
328
448
  width: 100%;
329
- text-align: left;
330
- border-radius: 7px;
449
+ min-height: 56px;
450
+ max-height: 30vh;
451
+ resize: vertical;
331
452
  border: 1px solid var(--line);
332
- background: rgba(255, 255, 255, 0.88);
333
- padding: 13px 14px;
334
- cursor: pointer;
335
- box-shadow: none;
336
- transition: border-color 120ms ease, transform 120ms ease, background 120ms ease;
337
- }
338
-
339
- .job-list button:hover {
340
- transform: translateY(-1px);
341
- border-color: rgba(20, 52, 203, 0.32);
453
+ border-radius: 10px;
454
+ padding: 10px 14px;
455
+ font: inherit;
456
+ color: var(--text);
457
+ background: var(--surface);
342
458
  }
343
-
344
- .job-list button.active {
345
- border-color: var(--visa-blue);
346
- background: var(--surface-strong);
459
+ .coach textarea:focus { outline: none; border-color: var(--accent); }
460
+ .coach-actions { display: flex; justify-content: flex-end; margin-top: 10px; }
461
+ .send-button {
462
+ background: var(--accent);
463
+ color: #fff;
464
+ border: none;
465
+ border-radius: 10px;
466
+ padding: 10px 18px;
467
+ font-weight: 600;
347
468
  }
469
+ .send-button:hover { background: var(--accent-strong); }
470
+ .send-button:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
471
+ .send-button:disabled { background: #c5d2cb; cursor: not-allowed; }
348
472
 
349
- .job-title {
350
- font-weight: 700;
351
- margin-bottom: 5px;
473
+ .micro {
474
+ border-top: 1px solid var(--line);
475
+ padding-top: 16px;
352
476
  }
353
-
354
- .job-intent {
477
+ .micro summary {
478
+ cursor: pointer;
355
479
  color: var(--muted);
356
- line-height: 1.45;
357
480
  font-size: 14px;
481
+ list-style: none;
482
+ display: flex;
483
+ align-items: center;
484
+ gap: 6px;
485
+ }
486
+ .micro summary::-webkit-details-marker { display: none; }
487
+ .micro summary::before {
488
+ content: "▸";
489
+ font-size: 11px;
490
+ transition: transform 100ms;
491
+ }
492
+ .micro[open] summary::before { transform: rotate(90deg); }
493
+ .micro-log {
494
+ margin-top: 12px;
495
+ background: #1f2a24;
496
+ color: #c5d2cb;
497
+ border-radius: 8px;
498
+ padding: 12px 14px;
499
+ font-family: Consolas, Menlo, monospace;
500
+ font-size: 12px;
501
+ line-height: 1.6;
502
+ white-space: pre-wrap;
503
+ max-height: 220px;
504
+ overflow-y: auto;
358
505
  }
359
506
 
360
- .job-outcomes {
361
- display: grid;
362
- gap: 8px;
507
+ .status-line {
363
508
  margin: 0;
364
- padding-left: 18px;
365
509
  color: var(--muted);
510
+ font-size: 13px;
511
+ text-align: center;
512
+ min-height: 1.2em;
366
513
  }
514
+ .status-line.error { color: var(--danger); }
367
515
 
368
- .interaction-card {
369
- padding: 18px;
370
- display: grid;
371
- gap: 18px;
372
- }
373
-
374
- .run-toolbar {
516
+ /* Modal */
517
+ .modal-backdrop {
518
+ position: fixed;
519
+ inset: 0;
520
+ background: rgba(20, 40, 30, 0.36);
521
+ display: none;
522
+ align-items: center;
523
+ justify-content: center;
524
+ z-index: 100;
525
+ padding: 20px;
526
+ animation: fade 180ms ease;
527
+ }
528
+ .modal-backdrop.open { display: flex; }
529
+ @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
530
+
531
+ .modal {
532
+ background: var(--surface);
533
+ border-radius: 16px;
534
+ box-shadow: var(--shadow-lg);
535
+ width: 100%;
536
+ max-width: 540px;
537
+ max-height: 90vh;
375
538
  display: flex;
376
- align-items: start;
377
- justify-content: space-between;
378
- gap: 18px;
379
- padding-bottom: 18px;
380
- border-bottom: 1px solid rgba(216, 225, 240, 0.8);
539
+ flex-direction: column;
540
+ overflow: hidden;
541
+ animation: rise 220ms ease;
542
+ min-height: 0;
381
543
  }
382
-
383
- .selected-job-summary {
384
- display: grid;
385
- gap: 14px;
386
- min-width: 0;
544
+ /* Each step IS the modal's main content — make it a flex column too so
545
+ .modal-body can actually scroll inside the bounded .modal height
546
+ instead of pushing the modal past 90vh. */
547
+ #step1, #step2 {
548
+ display: flex;
549
+ flex-direction: column;
550
+ flex: 1;
551
+ min-height: 0;
552
+ overflow: hidden;
387
553
  }
388
-
389
- .run-toolbar-actions {
554
+ @keyframes rise {
555
+ from { opacity: 0; transform: translateY(12px); }
556
+ to { opacity: 1; transform: translateY(0); }
557
+ }
558
+ .modal-header { padding: 22px 24px 12px; border-bottom: 1px solid var(--line); }
559
+ .modal-header h2 { margin: 0; font-size: 19px; font-weight: 600; }
560
+ .modal-header p { margin: 4px 0 0; color: var(--muted); font-size: 14px; }
561
+ .modal-body { padding: 18px 24px; overflow-y: auto; flex: 1; }
562
+ .modal-footer {
563
+ padding: 14px 24px 18px;
564
+ border-top: 1px solid var(--line);
390
565
  display: flex;
566
+ justify-content: space-between;
391
567
  align-items: center;
568
+ gap: 12px;
392
569
  }
570
+ .modal-footer .left { color: var(--muted); font-size: 13px; }
571
+ .modal-footer .right { display: flex; gap: 10px; }
393
572
 
394
- .interaction-body {
395
- display: grid;
396
- grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.9fr);
397
- gap: 18px;
398
- align-items: start;
573
+ .ghost {
574
+ background: transparent;
575
+ border: 1px solid var(--line);
576
+ border-radius: 10px;
577
+ padding: 9px 14px;
578
+ color: var(--text);
579
+ font-weight: 500;
399
580
  }
581
+ .ghost:hover { background: var(--soft); }
582
+ .ghost:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
400
583
 
401
- .conversation-column {
402
- display: grid;
403
- gap: 14px;
584
+ .search {
585
+ width: 100%;
586
+ border: 1px solid var(--line);
587
+ border-radius: 10px;
588
+ padding: 10px 14px;
589
+ font: inherit;
590
+ margin-bottom: 14px;
404
591
  }
592
+ .search:focus { outline: none; border-color: var(--accent); }
405
593
 
406
- .interaction-head {
407
- display: grid;
408
- gap: 18px;
594
+ .job-category { margin-bottom: 16px; }
595
+ .job-category h4 {
596
+ font-size: 12px;
597
+ font-weight: 600;
598
+ color: var(--muted);
599
+ text-transform: uppercase;
600
+ letter-spacing: 0.05em;
601
+ margin: 0 0 8px;
409
602
  }
410
-
411
- .timeline {
412
- min-height: 420px;
413
- max-height: 540px;
414
- overflow: auto;
415
- display: grid;
416
- gap: 12px;
417
- padding: 4px;
418
- align-content: start;
603
+ .job-option {
604
+ width: 100%;
605
+ text-align: left;
606
+ background: transparent;
419
607
  border: 1px solid var(--line);
420
- border-radius: 8px;
421
- background: rgba(247, 249, 253, 0.72);
608
+ border-radius: 10px;
609
+ padding: 10px 14px;
610
+ margin-bottom: 6px;
611
+ display: grid;
612
+ gap: 2px;
613
+ color: var(--text);
614
+ }
615
+ .job-option:hover { background: var(--soft); border-color: var(--accent); }
616
+ .job-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
617
+ .job-option.selected { background: var(--accent-soft); border-color: var(--accent); }
618
+ .job-option strong { font-weight: 600; }
619
+ .job-option span { color: var(--muted); font-size: 13px; }
620
+
621
+ .step2 .assigned-job {
622
+ background: var(--accent-soft);
623
+ border: 1px solid #c5dcd0;
624
+ border-radius: 10px;
625
+ padding: 12px 14px;
626
+ margin-bottom: 14px;
627
+ }
628
+ .step2 .assigned-job .label {
629
+ font-size: 11px;
630
+ font-weight: 600;
631
+ color: var(--accent);
632
+ text-transform: uppercase;
633
+ letter-spacing: 0.04em;
422
634
  }
423
-
424
- .message {
635
+ .step2 .assigned-job .name { font-weight: 600; margin-top: 2px; }
636
+ .step2 .assigned-job .desc { color: var(--muted); font-size: 13px; margin-top: 2px; }
637
+ .step2 textarea {
638
+ width: 100%;
639
+ min-height: 140px;
640
+ resize: vertical;
425
641
  border: 1px solid var(--line);
426
- border-radius: 7px;
427
- padding: 14px 16px;
428
- background: rgba(255, 255, 255, 0.88);
642
+ border-radius: 10px;
643
+ padding: 12px 14px;
644
+ font: inherit;
429
645
  }
646
+ .step2 textarea:focus { outline: none; border-color: var(--accent); }
430
647
 
431
- .message.manager {
432
- background: #eef3ff;
433
- border-color: #c9d8ff;
648
+ .employee-line {
649
+ display: flex;
650
+ align-items: center;
651
+ gap: 10px;
652
+ margin-top: 12px;
653
+ font-size: 13px;
654
+ color: var(--muted);
434
655
  }
435
-
436
- .message.employee {
437
- background: #ffffff;
656
+ .employee-select {
657
+ border: 1px solid var(--line);
658
+ border-radius: 8px;
659
+ padding: 6px 10px;
660
+ background: var(--surface);
661
+ color: var(--text);
662
+ }
663
+ .employee-select:focus { outline: none; border-color: var(--accent); }
664
+
665
+ @media (max-width: 820px) {
666
+ /* Single-column reflow — the rigid 100vh layout doesn't make sense at
667
+ mobile width because the rail stacks above the conversation. Let the
668
+ page scroll naturally and bound the messages section to keep the
669
+ coach + micro-manage reachable at the bottom. */
670
+ html, body { height: auto; }
671
+ body { overflow: auto; }
672
+ .page { height: auto; min-height: 100vh; }
673
+ .layout { grid-template-columns: 1fr; flex: initial; }
674
+ .rail { max-height: none; overflow-y: visible; }
675
+ .conversation { height: auto; min-height: 60vh; overflow: visible; }
676
+ #active-conv { flex: initial; }
677
+ .conv-stream { flex: initial; }
678
+ .messages { flex: initial; max-height: 60vh; }
679
+ .header { flex-wrap: wrap; }
680
+ .popover { width: min(320px, calc(100vw - 60px)); }
681
+ }
682
+
683
+ /* ---------------------------------------------------------------- */
684
+ /* Issue #347 — Pizza tracker (R1), template picker (R2), totals (R4) */
685
+ /* ---------------------------------------------------------------- */
686
+
687
+ .tracker {
688
+ padding: 12px 4px 14px;
689
+ border-top: 1px solid var(--line);
690
+ border-bottom: 1px solid var(--line);
691
+ }
692
+ .tracker-rows {
693
+ display: flex;
694
+ flex-direction: column;
695
+ gap: 14px;
438
696
  }
439
-
440
- .message.system {
441
- background: #f7f9fd;
697
+ .tracker-row {
698
+ display: flex;
699
+ align-items: center;
700
+ gap: 0;
701
+ position: relative;
442
702
  }
443
-
444
- .message-meta {
703
+ .tracker .stage {
445
704
  display: flex;
446
- justify-content: space-between;
447
- gap: 12px;
448
- margin-bottom: 8px;
449
- font-size: 12px;
705
+ flex-direction: column;
706
+ align-items: center;
707
+ gap: 6px;
708
+ flex: 1 1 0;
709
+ min-width: 0;
710
+ position: relative;
711
+ z-index: 2;
712
+ }
713
+ /* Connector segment to the previous stage in the same row. The fill
714
+ color flips green when the previous stage is done or the current
715
+ stage is current — that produces the "fill bar threads through
716
+ completed stages" effect without an SVG path. */
717
+ .tracker .stage::before {
718
+ content: '';
719
+ position: absolute;
720
+ top: 13px;
721
+ left: -50%;
722
+ right: 50%;
723
+ height: 3px;
724
+ background: var(--line);
725
+ z-index: 0;
726
+ }
727
+ .tracker .tracker-row .stage:first-child::before { display: none; }
728
+ .tracker .stage.done::before,
729
+ .tracker .stage.current::before { background: var(--accent); }
730
+ .tracker .stage-circle {
731
+ width: 26px; height: 26px;
732
+ border-radius: 50%;
733
+ background: var(--surface);
734
+ border: 2px solid var(--line);
735
+ display: inline-flex;
736
+ align-items: center;
737
+ justify-content: center;
738
+ font-size: 11px;
450
739
  color: var(--muted);
740
+ font-weight: 600;
741
+ position: relative;
742
+ z-index: 2;
451
743
  }
452
-
453
- .message-role {
454
- font-weight: 700;
455
- color: var(--visa-blue);
456
- }
457
-
458
- .message-text {
459
- white-space: pre-wrap;
460
- line-height: 1.55;
744
+ .tracker .stage.done .stage-circle {
745
+ background: var(--accent);
746
+ border-color: var(--accent);
747
+ color: #fff;
461
748
  }
462
-
463
- .manager-tools {
464
- display: grid;
465
- gap: 14px;
466
- padding: 16px;
467
- border-radius: 8px;
468
- background: rgba(247, 249, 253, 0.72);
469
- border: 1px solid var(--line);
749
+ .tracker .stage.current .stage-circle {
750
+ background: var(--accent-soft);
751
+ border-color: var(--accent);
752
+ color: var(--accent-strong);
753
+ box-shadow: 0 0 0 4px rgba(61, 138, 110, 0.10);
470
754
  }
471
-
472
- .template-row {
473
- display: grid;
474
- gap: 8px;
755
+ .tracker .stage-label {
756
+ font-size: 11px;
757
+ color: var(--muted);
758
+ text-align: center;
759
+ white-space: nowrap;
760
+ overflow: hidden;
761
+ text-overflow: ellipsis;
762
+ max-width: 100%;
475
763
  }
476
-
477
- .composer-label {
764
+ .tracker .stage.current .stage-label {
765
+ color: var(--text);
478
766
  font-weight: 600;
479
767
  }
480
-
481
- .micro-manage {
482
- padding: 0 18px;
483
- box-shadow: var(--shadow-soft);
484
- }
485
-
486
- .micro-manage summary {
487
- padding: 16px 0;
488
- cursor: pointer;
489
- font-weight: 700;
490
- }
491
-
492
- .micro-manage-body {
493
- padding: 0 0 18px;
494
- max-height: 240px;
495
- overflow: auto;
496
- display: grid;
497
- gap: 8px;
498
- font-family: Consolas, "Courier New", monospace;
768
+ .tracker-note {
769
+ margin-top: 10px;
499
770
  font-size: 12px;
500
771
  color: var(--muted);
772
+ text-align: center;
501
773
  }
774
+ .tracker-note .glyph { color: var(--accent); margin-right: 4px; }
502
775
 
503
- .micro-row {
504
- padding: 10px 12px;
505
- border-radius: 7px;
506
- background: var(--surface-muted);
776
+ /* Active-stage label that lives outside the rows. Always rendered by
777
+ JS; hidden by default and shown only at narrow viewports where the
778
+ per-stage labels are too cramped to read (spec R1.4 acceptance). */
779
+ .tracker-active-label {
780
+ display: none;
781
+ margin-top: 8px;
782
+ font-size: 12px;
783
+ color: var(--text);
784
+ font-weight: 600;
785
+ text-align: center;
786
+ }
787
+
788
+ /* Narrow-viewport label collapse (spec R1.4 acceptance). At ≤ 640px,
789
+ per-stage labels disappear; the standalone active label below the
790
+ rows tells the manager which stage is current. Numbered circles
791
+ keep the journey visible. */
792
+ @media (max-width: 640px) {
793
+ .tracker .stage .stage-label { display: none; }
794
+ .tracker-active-label { display: block; }
795
+ }
796
+
797
+ /* Template picker popover (R2). Absolute-positioned inside .coach-actions
798
+ so opening it does not push the textarea down. */
799
+ .coach-actions { position: relative; }
800
+ .template-popover {
801
+ position: absolute;
802
+ bottom: 44px;
803
+ right: 0;
804
+ width: 320px;
805
+ max-height: 360px;
806
+ overflow: auto;
807
+ background: var(--surface);
507
808
  border: 1px solid var(--line);
508
- white-space: pre-wrap;
509
- }
510
-
511
- @media (max-width: 1080px) {
512
- html,
513
- body {
514
- min-height: 100%;
515
- }
516
-
517
- .app-shell {
518
- min-height: 100dvh;
519
- }
520
-
521
- .workspace,
522
- .app-header,
523
- .interaction-body,
524
- .workspace-intro {
525
- grid-template-columns: 1fr;
526
- }
527
-
528
- .topbar-card,
529
- .run-toolbar {
530
- display: grid;
531
- }
532
-
533
- .project-path {
534
- min-width: 0;
535
- }
536
-
537
- .workspace-intro-actions {
538
- justify-items: start;
539
- }
809
+ border-radius: 12px;
810
+ box-shadow: var(--shadow-lg);
811
+ padding: 12px;
812
+ z-index: 10;
813
+ }
814
+ .template-popover .group {
815
+ padding: 6px 0;
816
+ border-bottom: 1px solid var(--line);
817
+ }
818
+ .template-popover .group:last-child { border-bottom: none; }
819
+ .template-popover .group-label {
820
+ font-size: 11px;
821
+ color: var(--accent-strong);
822
+ text-transform: uppercase;
823
+ letter-spacing: 0.06em;
824
+ font-weight: 600;
825
+ margin: 0 0 4px 0;
826
+ padding: 4px 10px;
827
+ display: flex;
828
+ align-items: center;
829
+ gap: 6px;
830
+ }
831
+ .template-popover .group-label .group-dot {
832
+ width: 8px; height: 8px;
833
+ border-radius: 50%;
834
+ background: var(--accent);
835
+ display: inline-block;
836
+ }
837
+ .template-popover .group.delegation .group-dot { background: var(--picker-delegation); }
838
+ .template-popover .group.coaching .group-dot { background: var(--accent); }
839
+ .template-popover .group.verification .group-dot { background: var(--warn); }
840
+ .template-popover .group.learning .group-dot { background: var(--picker-learning); }
841
+ .template-popover .template-row {
842
+ display: flex;
843
+ flex-direction: column;
844
+ gap: 2px;
845
+ padding: 8px 10px;
846
+ border-radius: 8px;
847
+ cursor: pointer;
848
+ background: none;
849
+ border: none;
850
+ text-align: left;
851
+ width: 100%;
852
+ font: inherit;
853
+ color: var(--text);
540
854
  }
855
+ .template-popover .template-row:hover { background: var(--accent-soft); }
856
+ .template-popover .template-row strong { font-size: 13px; font-weight: 500; }
857
+ .template-popover .template-row span { font-size: 12px; color: var(--muted); }
541
858
 
542
- @media (max-width: 768px) {
543
- .app-shell {
544
- padding: 18px 16px 16px;
545
- }
546
-
547
- .brand-title {
548
- font-size: 28px;
549
- }
550
-
551
- .interaction-head {
552
- display: grid;
553
- }
554
-
555
- .workspace,
556
- .app-header {
557
- gap: 18px;
558
- }
559
-
560
- .timeline {
561
- min-height: 280px;
562
- max-height: 380px;
563
- }
564
-
565
- .panel-intro {
566
- font-size: 16px;
567
- }
859
+ /* Totals line (R4). 12px muted, no border, single row that wraps if it
860
+ absolutely must. Discoverable via hover tooltips per spec R4.4. */
861
+ .totals {
862
+ font-size: 12px;
863
+ color: var(--muted);
864
+ padding-top: 4px;
865
+ display: flex;
866
+ gap: 14px;
867
+ flex-wrap: wrap;
568
868
  }
869
+ .totals span { cursor: help; }
870
+ .totals .sep { color: var(--line); }
871
+ .totals strong { color: var(--text); font-weight: 600; }