@floless/app 0.5.1

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.
@@ -0,0 +1,2129 @@
1
+ /* ===== floless.app skin — extracted verbatim from aware-demo.web.html (approved 2026-05-20) ===== */
2
+ /* Block 1: base tokens + full component styling */
3
+ * { box-sizing: border-box; margin: 0; padding: 0; }
4
+
5
+ :root {
6
+ --bg: #070a0f;
7
+ --surface: #0f141c;
8
+ --surface-2: #161d28;
9
+ --surface-3: #1c2535;
10
+ --border: #1c2535;
11
+ --border-strong: #2a3548;
12
+ --text: #e7ecf3;
13
+ --text-muted: #9aa5b6;
14
+ --text-dim: #5b6677;
15
+ --accent: #4a9eff;
16
+ --accent-bright: #67b3ff;
17
+ --accent-dim: #2c5f9e;
18
+ --accent-glow: rgba(74, 158, 255, 0.45);
19
+ --accent-soft: rgba(74, 158, 255, 0.1);
20
+ --star: #fbbf24;
21
+ --ok: #4ade80;
22
+ --warn: #fbbf24;
23
+ --err: #f87171;
24
+ --info: #67b3ff;
25
+ --mono: "JetBrains Mono", "SF Mono", "Consolas", "Monaco", monospace;
26
+ --ui: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif;
27
+ }
28
+
29
+ html, body { height: 100%; overflow: hidden; }
30
+ body {
31
+ background: var(--bg);
32
+ color: var(--text);
33
+ font-family: var(--ui);
34
+ font-size: 13px;
35
+ line-height: 1.5;
36
+ background-image:
37
+ linear-gradient(rgba(74, 158, 255, 0.025) 1px, transparent 1px),
38
+ linear-gradient(90deg, rgba(74, 158, 255, 0.025) 1px, transparent 1px);
39
+ background-size: 36px 36px;
40
+ background-position: -1px -1px;
41
+ }
42
+
43
+ /* ========== LAYOUT ========== */
44
+ .app {
45
+ display: grid;
46
+ grid-template-rows: 60px 1fr 44px;
47
+ --left-width: 340px;
48
+ --right-width: 420px;
49
+ grid-template-columns: var(--left-width) 1fr var(--right-width);
50
+ grid-template-areas:
51
+ "header header header"
52
+ "chat canvas inspect"
53
+ "footer footer footer";
54
+ height: 100vh;
55
+ gap: 1px;
56
+ background: var(--border);
57
+ }
58
+ .app.resizing { user-select: none; }
59
+ .app:not(.resizing) { transition: grid-template-columns 0.25s ease; }
60
+ .app.left-collapsed { --left-width: 48px !important; }
61
+ .app.right-collapsed { --right-width: 48px !important; }
62
+
63
+ /* ========== RESIZE HANDLES ========== */
64
+ .resize-handle {
65
+ position: absolute;
66
+ top: 0;
67
+ bottom: 0;
68
+ width: 5px;
69
+ cursor: ew-resize;
70
+ z-index: 8;
71
+ background: transparent;
72
+ transition: background 0.15s;
73
+ }
74
+ .resize-handle::after {
75
+ content: "";
76
+ position: absolute;
77
+ top: 50%;
78
+ left: 50%;
79
+ transform: translate(-50%, -50%);
80
+ width: 2px;
81
+ height: 30px;
82
+ background: var(--border-strong);
83
+ border-radius: 1px;
84
+ opacity: 0;
85
+ transition: opacity 0.15s;
86
+ }
87
+ .resize-handle:hover { background: rgba(74, 158, 255, 0.15); }
88
+ .resize-handle:hover::after { opacity: 1; background: var(--accent); }
89
+ .resize-handle.dragging { background: var(--accent-soft); }
90
+ .resize-handle.dragging::after { opacity: 1; background: var(--accent); }
91
+ .resize-handle-right { right: -3px; }
92
+ .resize-handle-left { left: -3px; }
93
+ .app.left-collapsed .chat .resize-handle,
94
+ .app.right-collapsed .inspect .resize-handle { display: none; }
95
+ body.resizing { cursor: ew-resize !important; }
96
+ body.resizing * { cursor: ew-resize !important; }
97
+
98
+ /* ========== HEADER ========== */
99
+ header {
100
+ grid-area: header;
101
+ background: var(--surface);
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: space-between;
105
+ padding: 0 18px;
106
+ border-bottom: 1px solid var(--border);
107
+ }
108
+ .header-left { display: flex; align-items: center; gap: 10px; }
109
+ .brand { display: flex; align-items: center; gap: 12px; }
110
+ .brand .mark {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ color: var(--accent);
114
+ width: 30px;
115
+ height: 28px;
116
+ filter: drop-shadow(0 0 6px var(--accent-glow));
117
+ }
118
+ .brand .mark svg { width: 100%; height: 100%; display: block; }
119
+ .brand .mark .mark-node { fill: var(--bg); }
120
+ .brand .name { font-size: 15px; font-weight: 700; letter-spacing: 0.08em; }
121
+ .brand .tag { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.12em; }
122
+ .controls { display: flex; align-items: center; gap: 10px; }
123
+ /* Vertical divider separating "which workflow + reference" from the run spine
124
+ (state → Compile → Simulate → Run). Uses the existing border token. */
125
+ .ctl-sep { width: 1px; height: 20px; background: var(--border-strong); flex: none; }
126
+ .controls label { color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; }
127
+
128
+ select, button {
129
+ font-family: var(--ui);
130
+ font-size: 12px;
131
+ background: var(--surface-2);
132
+ color: var(--text);
133
+ border: 1px solid var(--border-strong);
134
+ padding: 7px 12px;
135
+ border-radius: 4px;
136
+ outline: none;
137
+ cursor: pointer;
138
+ transition: all 0.15s;
139
+ }
140
+ select {
141
+ min-width: 360px;
142
+ appearance: none;
143
+ -webkit-appearance: none;
144
+ padding-right: 28px;
145
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%239aa5b6' d='M0 0l5 6 5-6z'/></svg>");
146
+ background-repeat: no-repeat;
147
+ background-position: right 10px center;
148
+ }
149
+ select:hover, button:hover { border-color: var(--accent-dim); }
150
+ /* Keyboard focus ring (the global outline:none above kills it for mouse users;
151
+ restore it for keyboard nav across every control, incl. JS-built buttons). */
152
+ button:focus-visible, select:focus-visible, a:focus-visible,
153
+ [role="option"]:focus-visible, [tabindex]:focus-visible {
154
+ outline: 2px solid var(--accent);
155
+ outline-offset: 2px;
156
+ }
157
+ /* Agents = utility (opens a reference modal, not a workflow step): ghost button,
158
+ quietest tier. Label kept — an unlabeled icon would be an unclear affordance. */
159
+ #browse-btn { background: transparent; border-color: transparent; color: var(--text-dim); }
160
+ #browse-btn:hover { background: var(--surface-2); border-color: var(--border-strong); color: var(--text); }
161
+ #sim-btn { background: var(--surface-2); color: var(--text-muted); }
162
+ #sim-btn:hover { color: var(--text); }
163
+ #sim-btn:disabled { opacity: 0.5; cursor: not-allowed; }
164
+ #run-btn {
165
+ background: var(--accent);
166
+ color: #ffffff;
167
+ border-color: var(--accent);
168
+ font-weight: 600;
169
+ padding: 7px 16px;
170
+ letter-spacing: 0.04em;
171
+ }
172
+ #run-btn:hover {
173
+ background: var(--accent-bright);
174
+ border-color: var(--accent-bright);
175
+ box-shadow: 0 0 18px var(--accent-glow);
176
+ }
177
+ #run-btn:disabled {
178
+ background: var(--surface-2);
179
+ color: var(--text-dim);
180
+ border-color: var(--border-strong);
181
+ cursor: wait;
182
+ box-shadow: none;
183
+ }
184
+
185
+ /* ========== PANEL LABELS ========== */
186
+ .panel-label {
187
+ padding: 12px 14px 8px;
188
+ font-size: 10px;
189
+ color: var(--text-dim);
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.18em;
192
+ display: flex;
193
+ justify-content: space-between;
194
+ align-items: center;
195
+ }
196
+ .panel-label .role {
197
+ font-style: italic;
198
+ text-transform: none;
199
+ letter-spacing: normal;
200
+ font-size: 10px;
201
+ }
202
+ .panel-label .label-end {
203
+ display: flex;
204
+ align-items: center;
205
+ gap: 10px;
206
+ }
207
+ .panel-toggle {
208
+ background: transparent;
209
+ border: 1px solid var(--border-strong);
210
+ color: var(--text-dim);
211
+ width: 22px;
212
+ height: 22px;
213
+ font-size: 13px;
214
+ line-height: 1;
215
+ padding: 0;
216
+ display: flex;
217
+ align-items: center;
218
+ justify-content: center;
219
+ cursor: pointer;
220
+ border-radius: 3px;
221
+ font-family: var(--ui);
222
+ }
223
+ .panel-toggle:hover {
224
+ color: var(--accent);
225
+ border-color: var(--accent-dim);
226
+ background: var(--accent-soft);
227
+ }
228
+
229
+ /* ========== CHAT (LEFT) ========== */
230
+ .chat {
231
+ grid-area: chat;
232
+ background: var(--surface);
233
+ display: flex;
234
+ flex-direction: column;
235
+ overflow: hidden;
236
+ position: relative;
237
+ }
238
+ .host-banner {
239
+ margin: 0 14px 14px;
240
+ padding: 10px 12px;
241
+ background: #050810;
242
+ border: 1px solid var(--border);
243
+ border-radius: 4px;
244
+ font-family: var(--mono);
245
+ font-size: 11px;
246
+ line-height: 1.65;
247
+ }
248
+ .host-line { color: var(--text-muted); }
249
+ .host-line.host-ok { color: var(--ok); }
250
+ .host-line.host-dim { color: var(--text-dim); }
251
+ .host-dim { color: var(--text-dim); }
252
+
253
+ .messages {
254
+ flex: 1;
255
+ overflow-y: auto;
256
+ padding: 0 14px 16px;
257
+ display: flex;
258
+ flex-direction: column;
259
+ gap: 14px;
260
+ }
261
+ .msg { display: flex; flex-direction: column; gap: 4px; animation: msg-in 0.32s ease-out; }
262
+ .msg .who { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.14em; display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
263
+ .msg.user .who { color: var(--accent); }
264
+ .msg .who-time { font-family: var(--mono); letter-spacing: 0; text-transform: none; color: var(--text-muted); font-size: 9.5px; flex-shrink: 0; }
265
+ .msg .body {
266
+ background: var(--surface-2);
267
+ padding: 10px 12px;
268
+ border-radius: 6px;
269
+ border-left: 2px solid var(--border-strong);
270
+ font-size: 13px;
271
+ line-height: 1.55;
272
+ }
273
+ .msg.user .body {
274
+ border-left-color: var(--accent);
275
+ background: linear-gradient(180deg, var(--accent-soft), var(--surface-2));
276
+ }
277
+ .msg.aware .body { border-left-color: var(--text-dim); }
278
+ @keyframes msg-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
279
+
280
+ /* ========== COLLAPSED STUBS ========== */
281
+ .collapsed-stub {
282
+ display: none;
283
+ flex: 1;
284
+ flex-direction: column;
285
+ align-items: center;
286
+ padding: 14px 0;
287
+ gap: 14px;
288
+ }
289
+ .app.left-collapsed .chat .collapsed-stub,
290
+ .app.right-collapsed .inspect .collapsed-stub { display: flex; }
291
+ /* When collapsed, the stub IS the rail (expand toggle + vertical label + icon).
292
+ Hide every other child — including the full panel-label: keeping it caused a
293
+ redundant second toggle and let the Inspect header's action buttons
294
+ (Debug in VS / Tweak) clip into the narrow strip. */
295
+ .app.left-collapsed .chat > *:not(.collapsed-stub),
296
+ .app.right-collapsed .inspect > *:not(.collapsed-stub) { display: none; }
297
+ .collapsed-stub .vertical-label {
298
+ writing-mode: vertical-rl;
299
+ transform: rotate(180deg);
300
+ font-size: 10px;
301
+ color: var(--text-dim);
302
+ text-transform: uppercase;
303
+ letter-spacing: 0.2em;
304
+ user-select: none;
305
+ }
306
+ .collapsed-stub .stub-icon {
307
+ width: 30px;
308
+ height: 30px;
309
+ border: 1px solid var(--border-strong);
310
+ border-radius: 4px;
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: center;
314
+ color: var(--text-muted);
315
+ font-size: 14px;
316
+ background: var(--surface-2);
317
+ }
318
+
319
+ /* ========== CANVAS ========== */
320
+ .canvas {
321
+ grid-area: canvas;
322
+ background: var(--bg);
323
+ display: flex;
324
+ flex-direction: column;
325
+ overflow: hidden;
326
+ position: relative;
327
+ }
328
+ /* While middle-mouse panning, force the grab cursor over the whole canvas. */
329
+ .canvas.panning, .canvas.panning * { cursor: grabbing !important; }
330
+ .topology {
331
+ flex: 1;
332
+ display: flex;
333
+ align-items: center;
334
+ justify-content: center;
335
+ gap: 0;
336
+ padding: 24px 36px;
337
+ position: relative;
338
+ min-height: 0;
339
+ overflow: auto;
340
+ }
341
+
342
+ /* LINEAR */
343
+ .topology:not(.dag) .agent-card { flex: 0 0 210px; }
344
+
345
+ /* DAG */
346
+ .topology.dag {
347
+ display: grid;
348
+ padding: 28px 36px;
349
+ gap: 24px 80px;
350
+ align-items: center;
351
+ justify-items: center;
352
+ }
353
+ .topology.dag .agent-card {
354
+ flex: none;
355
+ width: 180px;
356
+ position: relative;
357
+ z-index: 2;
358
+ }
359
+ .topology.dag .agent-card .title { font-size: 13px; }
360
+ .topology.dag .agent-card .blurb { font-size: 11px; -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
361
+
362
+ /* PLACEHOLDER — honest canvas state when there's no workflow to draw
363
+ (no workflow open / server offline / loading). Never fake nodes. */
364
+ .topology.placeholder { display: flex; align-items: center; justify-content: center; }
365
+ .canvas-empty {
366
+ max-width: 380px;
367
+ text-align: center;
368
+ color: var(--text-dim);
369
+ display: flex;
370
+ flex-direction: column;
371
+ align-items: center;
372
+ gap: 10px;
373
+ padding: 24px;
374
+ user-select: none;
375
+ }
376
+ .canvas-empty-icon { font-size: 34px; line-height: 1; opacity: 0.7; }
377
+ .canvas-empty-title { font-size: 15px; font-weight: 600; color: var(--text); }
378
+ .canvas-empty-body { font-size: 12.5px; line-height: 1.55; }
379
+ .canvas-empty-body code { font-family: var(--mono); background: var(--surface-2); padding: 1px 5px; border-radius: 3px; color: var(--text-muted); }
380
+ .canvas-empty[data-kind="offline"] .canvas-empty-icon { color: var(--err); opacity: 1; }
381
+ .canvas-empty-actions { margin-top: 6px; }
382
+ .canvas-empty-actions:empty { display: none; }
383
+
384
+ /* First-run onboarding panel (the 'empty' placeholder). A calm, left-aligned
385
+ card that orients a newcomer — not a splash. All tokens are existing. */
386
+ .canvas-empty.onboarding {
387
+ max-width: 440px; text-align: left; align-items: stretch; gap: 0;
388
+ background: var(--surface); border: 1px solid var(--border-strong);
389
+ border-radius: 8px; padding: 26px 26px 22px; color: var(--text-muted);
390
+ }
391
+ .onb-headline { font-size: 14px; font-weight: 600; color: var(--text); line-height: 1.45; }
392
+ .onb-sub { font-size: 12px; color: var(--text-muted); margin-top: 8px; line-height: 1.55; }
393
+ .onb-divider { border-top: 1px solid var(--border); margin: 18px 0 14px; }
394
+ .onb-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
395
+ .onb-steps li { display: flex; gap: 11px; align-items: baseline; }
396
+ .onb-num { font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--accent); min-width: 14px; }
397
+ .onb-step-label { font-size: 12px; color: var(--text); }
398
+ .onb-step-note { font-size: 11px; color: var(--text-dim); font-style: italic; }
399
+ .onb-cmd-label { font-size: 11px; color: var(--text-dim); margin: 18px 0 6px; }
400
+ .onb-cmd {
401
+ display: flex; justify-content: space-between; align-items: center; gap: 10px;
402
+ background: var(--bg); border: 1px solid var(--border-strong); border-radius: 4px;
403
+ padding: 9px 12px;
404
+ }
405
+ .onb-cmd code { font-family: var(--mono); font-size: 12px; color: var(--text-muted); }
406
+ .onb-copy {
407
+ background: transparent; border: none; color: var(--text-dim);
408
+ font-size: 14px; line-height: 1; cursor: pointer; padding: 2px 4px; border-radius: 4px;
409
+ }
410
+ .onb-copy:hover { color: var(--text); }
411
+
412
+ .agent-card {
413
+ background: var(--surface);
414
+ border: 1px solid var(--border-strong);
415
+ border-radius: 8px;
416
+ padding: 14px;
417
+ cursor: pointer;
418
+ transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
419
+ position: relative;
420
+ overflow: hidden;
421
+ }
422
+ .agent-card::before {
423
+ content: "";
424
+ position: absolute;
425
+ inset: 0;
426
+ background: linear-gradient(135deg, var(--accent-soft), transparent 60%);
427
+ opacity: 0;
428
+ transition: opacity 0.25s;
429
+ pointer-events: none;
430
+ }
431
+ .agent-card:hover {
432
+ border-color: var(--accent-dim);
433
+ transform: translateY(-2px);
434
+ }
435
+ .agent-card.selected {
436
+ border-color: var(--accent);
437
+ box-shadow: 0 0 0 1px var(--accent), 0 8px 32px -8px var(--accent-glow);
438
+ }
439
+ .agent-card.selected::before { opacity: 1; }
440
+ .agent-card.pulsing {
441
+ border-color: var(--accent-bright);
442
+ animation: card-pulse 0.6s ease-in-out;
443
+ }
444
+ .agent-card[draggable="true"] { cursor: grab; }
445
+ .agent-card.dragging { opacity: 0.5; cursor: grabbing; }
446
+
447
+ @keyframes card-pulse {
448
+ 0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
449
+ 50% { box-shadow: 0 0 32px 4px var(--accent-glow); }
450
+ }
451
+
452
+ .agent-card .head {
453
+ display: flex;
454
+ align-items: center;
455
+ justify-content: space-between;
456
+ margin-bottom: 10px;
457
+ gap: 6px;
458
+ padding-right: 28px; /* reserve clearance for the absolute-positioned star button */
459
+ }
460
+ .agent-card .head-left {
461
+ display: flex;
462
+ align-items: center;
463
+ gap: 8px;
464
+ min-width: 0;
465
+ }
466
+ .agent-card .kind {
467
+ font-size: 9px;
468
+ color: var(--accent);
469
+ text-transform: uppercase;
470
+ letter-spacing: 0.16em;
471
+ font-weight: 600;
472
+ white-space: nowrap;
473
+ overflow: hidden;
474
+ text-overflow: ellipsis;
475
+ }
476
+ .agent-card .ver {
477
+ font-family: var(--mono);
478
+ font-size: 9px;
479
+ color: var(--text-dim);
480
+ background: var(--surface-2);
481
+ padding: 1px 5px;
482
+ border-radius: 3px;
483
+ flex-shrink: 0;
484
+ }
485
+ .agent-card .icon {
486
+ width: 22px;
487
+ height: 22px;
488
+ border: 1px solid var(--accent-dim);
489
+ border-radius: 4px;
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ color: var(--accent);
494
+ font-size: 12px;
495
+ background: var(--accent-soft);
496
+ flex-shrink: 0;
497
+ }
498
+ .agent-card .fav-btn {
499
+ position: absolute;
500
+ top: 8px;
501
+ right: 8px;
502
+ background: transparent;
503
+ border: none;
504
+ padding: 4px 6px;
505
+ cursor: pointer;
506
+ color: var(--text-dim);
507
+ font-size: 14px;
508
+ line-height: 1;
509
+ border-radius: 3px;
510
+ z-index: 3;
511
+ transition: all 0.15s;
512
+ }
513
+ .agent-card .fav-btn:hover {
514
+ color: var(--star);
515
+ background: var(--accent-soft);
516
+ transform: scale(1.15);
517
+ }
518
+ .agent-card .fav-btn.faved { color: var(--star); }
519
+ .agent-card .title { font-size: 14px; font-weight: 600; margin-bottom: 4px; letter-spacing: -0.01em; padding-right: 30px; }
520
+ .agent-card .subtitle {
521
+ font-family: var(--mono);
522
+ font-size: 10px;
523
+ color: var(--text-dim);
524
+ line-height: 1.45;
525
+ margin-bottom: 10px;
526
+ word-break: break-word;
527
+ }
528
+ .agent-card .blurb { font-size: 11.5px; color: var(--text-muted); line-height: 1.5; margin-bottom: 10px; }
529
+ .agent-card .footer-row {
530
+ display: flex;
531
+ justify-content: space-between;
532
+ align-items: center;
533
+ font-size: 10px;
534
+ color: var(--text-dim);
535
+ border-top: 1px dashed var(--border-strong);
536
+ padding-top: 8px;
537
+ }
538
+ .agent-card .inspect-hint { color: var(--accent); opacity: 0.6; }
539
+ .agent-card.selected .inspect-hint { opacity: 1; }
540
+
541
+ /* LINEAR WIRES */
542
+ .wire-wrap {
543
+ flex: 0 0 88px;
544
+ display: flex;
545
+ flex-direction: column;
546
+ align-items: stretch;
547
+ gap: 4px;
548
+ margin: 0 -1px;
549
+ }
550
+ .wire-label {
551
+ font-family: var(--mono);
552
+ font-size: 9px;
553
+ color: var(--text-dim);
554
+ text-align: center;
555
+ white-space: nowrap;
556
+ overflow: hidden;
557
+ text-overflow: ellipsis;
558
+ }
559
+ .wire {
560
+ height: 2px;
561
+ background: var(--border-strong);
562
+ position: relative;
563
+ margin-top: 4px;
564
+ }
565
+ .wire::after {
566
+ content: "";
567
+ position: absolute;
568
+ right: -1px;
569
+ top: 50%;
570
+ transform: translateY(-50%);
571
+ width: 0;
572
+ height: 0;
573
+ border-left: 7px solid var(--border-strong);
574
+ border-top: 5px solid transparent;
575
+ border-bottom: 5px solid transparent;
576
+ }
577
+ .wire .pulse {
578
+ position: absolute;
579
+ top: 50%;
580
+ left: 0;
581
+ width: 10px;
582
+ height: 10px;
583
+ border-radius: 50%;
584
+ background: var(--accent-bright);
585
+ box-shadow: 0 0 14px 2px var(--accent-glow);
586
+ transform: translate(-50%, -50%);
587
+ opacity: 0;
588
+ pointer-events: none;
589
+ }
590
+ .wire.active { background: var(--accent); }
591
+ .wire.active::after { border-left-color: var(--accent); }
592
+ .wire.firing .pulse { animation: pulse-travel 0.7s ease-in-out forwards; }
593
+ @keyframes pulse-travel {
594
+ 0% { left: 0%; opacity: 0; }
595
+ 15% { opacity: 1; }
596
+ 85% { opacity: 1; }
597
+ 100% { left: 100%; opacity: 0; }
598
+ }
599
+
600
+ /* DAG WIRES (SVG) */
601
+ .wires-svg {
602
+ position: absolute;
603
+ inset: 0;
604
+ width: 100%;
605
+ height: 100%;
606
+ pointer-events: none;
607
+ z-index: 1;
608
+ overflow: visible;
609
+ }
610
+ .dag-wire {
611
+ stroke: var(--border-strong);
612
+ fill: none;
613
+ stroke-width: 2;
614
+ transition: stroke 0.25s, filter 0.25s;
615
+ }
616
+ .dag-wire.active { stroke: var(--accent); }
617
+ .dag-wire.firing {
618
+ stroke: var(--accent-bright);
619
+ stroke-dasharray: 8 6;
620
+ animation: dash-flow 0.7s linear forwards;
621
+ filter: drop-shadow(0 0 4px var(--accent-glow));
622
+ }
623
+ @keyframes dash-flow {
624
+ from { stroke-dashoffset: 28; }
625
+ to { stroke-dashoffset: 0; }
626
+ }
627
+ .dag-arrow {
628
+ fill: var(--border-strong);
629
+ transition: fill 0.25s;
630
+ }
631
+ .dag-arrow.active { fill: var(--accent); }
632
+ .dag-label-bg {
633
+ fill: var(--bg);
634
+ stroke: var(--border);
635
+ stroke-width: 1;
636
+ }
637
+ .dag-label {
638
+ font-family: var(--mono);
639
+ font-size: 9px;
640
+ fill: var(--text-muted);
641
+ user-select: none;
642
+ }
643
+
644
+ .hint {
645
+ text-align: center;
646
+ color: var(--text-dim);
647
+ font-size: 11px;
648
+ padding: 0 20px 12px;
649
+ font-style: italic;
650
+ }
651
+
652
+ /* ========== FAVORITES BAR ========== */
653
+ .fav-bar {
654
+ border-top: 1px solid var(--border);
655
+ background: var(--surface);
656
+ padding: 9px 16px;
657
+ display: flex;
658
+ align-items: center;
659
+ gap: 12px;
660
+ min-height: 46px;
661
+ overflow-x: auto;
662
+ transition: background 0.15s, border-top-color 0.15s;
663
+ }
664
+ .fav-bar-label {
665
+ font-size: 10px;
666
+ text-transform: uppercase;
667
+ letter-spacing: 0.14em;
668
+ color: var(--text-dim);
669
+ flex-shrink: 0;
670
+ display: flex;
671
+ align-items: center;
672
+ gap: 6px;
673
+ }
674
+ .fav-bar-label .star { color: var(--star); font-size: 12px; }
675
+ .fav-bar-empty { font-style: italic; font-size: 11px; color: var(--text-dim); }
676
+ .fav-chip-row { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
677
+ .fav-bar.drag-over {
678
+ background: var(--accent-soft);
679
+ border-top-color: var(--accent);
680
+ box-shadow: inset 0 1px 0 var(--accent);
681
+ }
682
+ .fav-chip {
683
+ display: inline-flex;
684
+ align-items: center;
685
+ gap: 6px;
686
+ background: var(--surface-2);
687
+ border: 1px solid var(--border-strong);
688
+ border-radius: 4px;
689
+ padding: 4px 4px 4px 10px;
690
+ font-size: 11px;
691
+ color: var(--text);
692
+ cursor: pointer;
693
+ flex-shrink: 0;
694
+ transition: all 0.15s;
695
+ user-select: none;
696
+ }
697
+ .fav-chip:hover {
698
+ border-color: var(--accent);
699
+ transform: translateY(-1px);
700
+ }
701
+ .fav-chip .cat {
702
+ color: var(--accent);
703
+ font-size: 9px;
704
+ text-transform: uppercase;
705
+ letter-spacing: 0.1em;
706
+ padding-right: 8px;
707
+ margin-right: 2px;
708
+ border-right: 1px solid var(--border-strong);
709
+ }
710
+ .fav-chip .del {
711
+ color: var(--text-dim);
712
+ opacity: 0.4;
713
+ cursor: pointer;
714
+ padding: 4px 6px;
715
+ line-height: 1;
716
+ transition: all 0.15s;
717
+ border-radius: 2px;
718
+ }
719
+ .fav-chip:hover .del { opacity: 1; }
720
+ .fav-chip .del:hover { color: var(--err); background: rgba(248, 113, 113, 0.1); }
721
+
722
+ /* ========== INSPECT ========== */
723
+ .inspect {
724
+ grid-area: inspect;
725
+ background: var(--surface);
726
+ display: flex;
727
+ flex-direction: column;
728
+ overflow: hidden;
729
+ position: relative;
730
+ }
731
+ .tabs {
732
+ display: flex;
733
+ border-bottom: 1px solid var(--border);
734
+ background: var(--surface);
735
+ }
736
+ .tabs button {
737
+ flex: 1;
738
+ background: transparent;
739
+ border: none;
740
+ border-radius: 0;
741
+ padding: 10px 0;
742
+ color: var(--text-dim);
743
+ font-size: 11px;
744
+ text-transform: uppercase;
745
+ letter-spacing: 0.12em;
746
+ border-bottom: 2px solid transparent;
747
+ cursor: pointer;
748
+ transition: all 0.15s;
749
+ }
750
+ .tabs button:hover { color: var(--text-muted); }
751
+ .tabs button.active { color: var(--accent); border-bottom-color: var(--accent); }
752
+
753
+ .inspect-body { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 16px 18px 20px; }
754
+ /* Pinned title/meta + an internally-scrolling body region, so the Code tab's
755
+ horizontal scrollbar stays at the panel's visible bottom — not the bottom of the
756
+ full, tall <pre>. Natural-size when it fits; shrink + scroll when it doesn't. */
757
+ .inspect-head-row, .inspect-meta { flex: 0 0 auto; }
758
+ .inspect-content, pre.code, .trace { flex: 0 1 auto; min-height: 0; overflow: auto; }
759
+ .inspect-body .empty-state { flex: 1 1 0; min-height: 0; display: flex; align-items: center; justify-content: center; }
760
+ .inspect-head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
761
+ .inspect-title { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
762
+ /* Copy-node-id chip — the node's real id, click to copy. Quiet, mono. */
763
+ .inspect-id {
764
+ flex: 0 0 auto; font-family: var(--mono); font-size: 10px;
765
+ background: var(--surface-2); color: var(--text-dim);
766
+ border: 1px solid var(--border); border-radius: 4px;
767
+ padding: 2px 7px; cursor: pointer; white-space: nowrap;
768
+ transition: color 0.12s ease, border-color 0.12s ease;
769
+ }
770
+ .inspect-id:hover { color: var(--text); border-color: var(--accent-dim); }
771
+ .inspect-meta {
772
+ font-family: var(--mono);
773
+ font-size: 10px;
774
+ color: var(--text-dim);
775
+ margin-bottom: 16px;
776
+ padding-bottom: 12px;
777
+ border-bottom: 1px solid var(--border);
778
+ }
779
+ .inspect-content { font-size: 12.5px; line-height: 1.65; color: var(--text-muted); }
780
+ .inspect-content p { margin-bottom: 12px; }
781
+ .inspect-content strong { color: var(--text); font-weight: 600; }
782
+ .inspect-content code {
783
+ font-family: var(--mono);
784
+ font-size: 11px;
785
+ background: var(--surface-2);
786
+ padding: 1px 6px;
787
+ border-radius: 3px;
788
+ color: var(--accent-bright);
789
+ }
790
+ .inspect-content h3 {
791
+ font-size: 12px;
792
+ color: var(--text);
793
+ text-transform: uppercase;
794
+ letter-spacing: 0.1em;
795
+ margin: 16px 0 8px;
796
+ font-weight: 600;
797
+ }
798
+ .inspect-content ul { margin: 6px 0 12px 18px; }
799
+ .inspect-content li { margin-bottom: 4px; }
800
+
801
+ pre.code {
802
+ background: #050810;
803
+ border: 1px solid var(--border);
804
+ border-radius: 6px;
805
+ padding: 12px 14px;
806
+ overflow-x: auto;
807
+ font-family: var(--mono);
808
+ font-size: 11.5px;
809
+ line-height: 1.6;
810
+ color: var(--text);
811
+ }
812
+ pre.code .kw { color: #c084fc; }
813
+ pre.code .ty { color: var(--accent-bright); }
814
+ pre.code .st { color: #4ade80; }
815
+ pre.code .cm { color: var(--text-dim); font-style: italic; }
816
+ pre.code .fn { color: #fbbf24; }
817
+ pre.code .nm { color: #f0abfc; }
818
+ pre.code .nu { color: #93c5fd; }
819
+
820
+ /* Code tab toolbar: find-in-code (left) + Wrap toggle (right). */
821
+ .code-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 6px; }
822
+ .code-find { display: flex; align-items: center; gap: 8px; min-width: 0; }
823
+ .code-find-input {
824
+ font-family: var(--ui); font-size: 11px; line-height: 1.5;
825
+ background: var(--surface-2); color: var(--text);
826
+ border: 1px solid var(--border-strong); border-radius: 3px;
827
+ padding: 2px 8px; width: 160px; max-width: 40vw;
828
+ }
829
+ .code-find-input::placeholder { color: var(--text-dim); }
830
+ .code-find-input:focus { border-color: var(--accent-dim); outline: none; }
831
+ .code-find-count { font-family: var(--mono); font-size: 10px; color: var(--text-dim); white-space: nowrap; }
832
+ /* Find matches highlighted in-place (no DOM mutation) via the Highlight API. */
833
+ ::highlight(code-find) { background: color-mix(in srgb, var(--accent) 28%, transparent); }
834
+ ::highlight(code-find-active) { background: var(--accent); color: var(--bg); }
835
+ .code-wrap-btn {
836
+ font-family: var(--ui); font-size: 10px; letter-spacing: 0.04em; line-height: 1.6;
837
+ color: var(--text-dim); background: transparent;
838
+ border: 1px solid var(--border-strong); border-radius: 3px;
839
+ padding: 2px 7px; cursor: pointer;
840
+ }
841
+ .code-wrap-btn:hover { color: var(--text-muted); border-color: var(--accent-dim); }
842
+ .code-wrap-btn.active { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-dim); }
843
+ .code-wrap-btn.active:hover { border-color: var(--accent); }
844
+ pre.code.wrap { white-space: pre-wrap; word-break: break-word; overflow-x: hidden; }
845
+
846
+ /* Execution-trace node filter chips (shown only when a run touched >1 node). */
847
+ .trace-filter { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; flex: 0 0 auto; }
848
+ .tf-chip {
849
+ font-family: var(--mono); font-size: 10px; line-height: 1.5;
850
+ background: var(--surface-2); color: var(--text-dim);
851
+ border: 1px solid var(--border-strong); border-radius: 999px;
852
+ padding: 2px 9px; cursor: pointer;
853
+ transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
854
+ }
855
+ .tf-chip:hover { color: var(--text-muted); border-color: var(--accent-dim); }
856
+ .tf-chip.active { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-dim); }
857
+
858
+ .trace {
859
+ font-family: var(--mono);
860
+ font-size: 11.5px;
861
+ background: #050810;
862
+ border: 1px solid var(--border);
863
+ border-radius: 6px;
864
+ padding: 10px 12px;
865
+ }
866
+ .trace .row {
867
+ display: grid;
868
+ grid-template-columns: 60px 56px 1fr;
869
+ gap: 10px;
870
+ padding: 3px 0;
871
+ align-items: baseline;
872
+ }
873
+ .trace .ts { color: var(--text-dim); }
874
+ .trace .lvl { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; text-align: right; }
875
+ .trace .lvl.info { color: var(--info); }
876
+ .trace .lvl.event { color: #c084fc; }
877
+ .trace .lvl.pass { color: var(--ok); }
878
+ .trace .lvl.ok { color: var(--ok); }
879
+ .trace .lvl.warn { color: var(--warn); }
880
+ .trace .lvl.err { color: var(--err); }
881
+ .trace .msg { color: var(--text-muted); }
882
+ .empty-state {
883
+ color: var(--text-dim);
884
+ font-style: italic;
885
+ font-size: 12px;
886
+ padding: 20px 0;
887
+ text-align: center;
888
+ }
889
+
890
+ /* ========== MODAL ========== */
891
+ .modal-backdrop {
892
+ position: fixed;
893
+ inset: 0;
894
+ background: rgba(7, 10, 15, 0.78);
895
+ backdrop-filter: blur(4px);
896
+ -webkit-backdrop-filter: blur(4px);
897
+ z-index: 100;
898
+ display: none;
899
+ align-items: center;
900
+ justify-content: center;
901
+ }
902
+ .modal-backdrop.show { display: flex; animation: fade-in 0.15s ease-out; }
903
+ @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
904
+ .modal {
905
+ background: var(--surface);
906
+ border: 1px solid var(--border-strong);
907
+ border-radius: 8px;
908
+ width: 480px;
909
+ max-width: 92vw;
910
+ padding: 22px;
911
+ box-shadow: 0 24px 80px rgba(0,0,0,0.6);
912
+ animation: pop-in 0.18s ease-out;
913
+ }
914
+ @keyframes pop-in {
915
+ from { opacity: 0; transform: scale(0.96); }
916
+ to { opacity: 1; transform: scale(1); }
917
+ }
918
+ .modal-title { font-size: 15px; font-weight: 700; margin-bottom: 4px; }
919
+ .modal-sub { font-size: 11px; color: var(--text-dim); margin-bottom: 18px; }
920
+ .modal-field { margin-bottom: 12px; }
921
+ .modal-field label {
922
+ display: block;
923
+ font-size: 10px;
924
+ text-transform: uppercase;
925
+ letter-spacing: 0.14em;
926
+ color: var(--text-muted);
927
+ margin-bottom: 6px;
928
+ }
929
+ .modal-field input,
930
+ .modal-field textarea {
931
+ width: 100%;
932
+ background: var(--surface-2);
933
+ border: 1px solid var(--border-strong);
934
+ color: var(--text);
935
+ padding: 9px 11px;
936
+ font-family: var(--ui);
937
+ font-size: 13px;
938
+ border-radius: 4px;
939
+ outline: none;
940
+ transition: border-color 0.15s;
941
+ }
942
+ .modal-field textarea { resize: vertical; min-height: 76px; line-height: 1.5; }
943
+ .modal-field input:focus,
944
+ .modal-field textarea:focus { border-color: var(--accent); }
945
+ .modal-categories { display: flex; gap: 5px; margin-top: 7px; flex-wrap: wrap; }
946
+ .modal-categories button {
947
+ background: var(--surface-2);
948
+ border: 1px solid var(--border-strong);
949
+ color: var(--text-muted);
950
+ font-size: 10px;
951
+ padding: 3px 8px;
952
+ border-radius: 3px;
953
+ cursor: pointer;
954
+ text-transform: uppercase;
955
+ letter-spacing: 0.08em;
956
+ }
957
+ .modal-categories button:hover {
958
+ border-color: var(--accent-dim);
959
+ color: var(--text);
960
+ }
961
+ .modal-actions {
962
+ display: flex;
963
+ justify-content: flex-end;
964
+ gap: 8px;
965
+ margin-top: 18px;
966
+ }
967
+ .modal-actions button { padding: 7px 16px; }
968
+ .modal-actions button.primary {
969
+ background: var(--accent);
970
+ color: white;
971
+ border-color: var(--accent);
972
+ font-weight: 600;
973
+ }
974
+ .modal-actions button.primary:hover {
975
+ background: var(--accent-bright);
976
+ box-shadow: 0 0 14px var(--accent-glow);
977
+ }
978
+ .modal.library {
979
+ width: 720px;
980
+ max-height: 82vh;
981
+ display: flex;
982
+ flex-direction: column;
983
+ }
984
+ .lib-search {
985
+ width: 100%;
986
+ background: var(--surface-2);
987
+ border: 1px solid var(--border-strong);
988
+ color: var(--text);
989
+ padding: 9px 11px;
990
+ font-family: var(--ui);
991
+ font-size: 13px;
992
+ border-radius: 4px;
993
+ outline: none;
994
+ margin-bottom: 12px;
995
+ }
996
+ .lib-search:focus { border-color: var(--accent); }
997
+ .lib-list {
998
+ flex: 1;
999
+ overflow-y: auto;
1000
+ display: grid;
1001
+ grid-template-columns: 1fr 1fr;
1002
+ gap: 6px;
1003
+ padding-right: 4px;
1004
+ }
1005
+ .lib-item {
1006
+ background: var(--surface-2);
1007
+ border: 1px solid var(--border-strong);
1008
+ border-radius: 6px;
1009
+ padding: 10px 12px;
1010
+ display: flex;
1011
+ justify-content: space-between;
1012
+ align-items: flex-start;
1013
+ gap: 10px;
1014
+ transition: border-color 0.15s;
1015
+ }
1016
+ .lib-item:hover { border-color: var(--accent-dim); }
1017
+ .lib-item .info { flex: 1; min-width: 0; }
1018
+ .lib-item .info .name { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
1019
+ .lib-item .info .meta {
1020
+ font-family: var(--mono);
1021
+ font-size: 10px;
1022
+ color: var(--text-dim);
1023
+ margin-bottom: 4px;
1024
+ overflow: hidden;
1025
+ text-overflow: ellipsis;
1026
+ white-space: nowrap;
1027
+ }
1028
+ .lib-item .info .blurb { font-size: 11px; color: var(--text-muted); line-height: 1.4; }
1029
+ .lib-item .lib-fav {
1030
+ background: transparent;
1031
+ border: 1px solid var(--border-strong);
1032
+ padding: 4px 9px;
1033
+ color: var(--text-dim);
1034
+ font-size: 13px;
1035
+ cursor: pointer;
1036
+ border-radius: 3px;
1037
+ flex-shrink: 0;
1038
+ line-height: 1;
1039
+ }
1040
+ .lib-item .lib-fav.faved { color: var(--star); border-color: var(--star); }
1041
+
1042
+ /* ========== INTEGRATIONS MODAL ========== */
1043
+ .modal.integrations { width: 760px; max-height: 86vh; display: flex; flex-direction: column; }
1044
+ .integrations-hint {
1045
+ font-size: 11px;
1046
+ color: var(--text-dim);
1047
+ background: var(--surface-2);
1048
+ border: 1px solid var(--border);
1049
+ border-radius: 4px;
1050
+ padding: 8px 12px;
1051
+ margin-bottom: 10px;
1052
+ line-height: 1.5;
1053
+ }
1054
+ .integrations-hint code {
1055
+ font-family: var(--mono);
1056
+ font-size: 10px;
1057
+ background: var(--bg);
1058
+ color: var(--accent);
1059
+ padding: 1px 5px;
1060
+ border-radius: 3px;
1061
+ border: 1px solid var(--border);
1062
+ }
1063
+ .integrations-list {
1064
+ display: grid;
1065
+ grid-template-columns: 1fr 1fr;
1066
+ gap: 8px;
1067
+ overflow-y: auto;
1068
+ flex: 1;
1069
+ padding-right: 4px;
1070
+ }
1071
+ .int-item {
1072
+ background: var(--surface-2);
1073
+ border: 1px solid var(--border-strong);
1074
+ border-radius: 6px;
1075
+ padding: 12px;
1076
+ display: grid;
1077
+ grid-template-columns: 38px 1fr;
1078
+ gap: 10px;
1079
+ align-items: flex-start;
1080
+ transition: border-color 0.15s;
1081
+ }
1082
+ .int-item:hover { border-color: var(--accent-dim); }
1083
+ .int-item.int-connected { border-left: 3px solid var(--ok); }
1084
+ .int-item.int-expired { border-left: 3px solid var(--warn); }
1085
+ .int-item.int-disconnected { opacity: 0.72; }
1086
+ .int-icon {
1087
+ width: 38px;
1088
+ height: 38px;
1089
+ border-radius: 6px;
1090
+ background: var(--surface-3);
1091
+ display: flex;
1092
+ align-items: center;
1093
+ justify-content: center;
1094
+ font-size: 18px;
1095
+ color: var(--accent);
1096
+ border: 1px solid var(--border-strong);
1097
+ font-weight: 600;
1098
+ }
1099
+ .int-info { min-width: 0; }
1100
+ .int-name { font-size: 13px; font-weight: 600; margin-bottom: 1px; }
1101
+ .int-kind { font-size: 10px; color: var(--text-muted); margin-bottom: 6px; }
1102
+ .int-scopes {
1103
+ font-family: var(--mono);
1104
+ font-size: 10px;
1105
+ color: var(--text-dim);
1106
+ margin-bottom: 2px;
1107
+ overflow: hidden;
1108
+ text-overflow: ellipsis;
1109
+ white-space: nowrap;
1110
+ }
1111
+ .int-meta { font-size: 10px; color: var(--text-dim); font-style: italic; margin-bottom: 8px; }
1112
+ .int-flow {
1113
+ display: inline-flex;
1114
+ align-items: center;
1115
+ gap: 6px;
1116
+ margin-top: 8px;
1117
+ font-size: 10px;
1118
+ color: var(--text-dim);
1119
+ cursor: pointer;
1120
+ user-select: none;
1121
+ }
1122
+ .int-flow input { accent-color: var(--accent); cursor: pointer; }
1123
+ .int-flow:hover { color: var(--text-muted); }
1124
+ .int-devicecode:not(:empty) {
1125
+ font-size: 11px;
1126
+ color: var(--text-muted);
1127
+ margin-top: 8px;
1128
+ line-height: 1.6;
1129
+ word-break: break-word;
1130
+ }
1131
+ .int-devicecode a { color: var(--accent-bright); text-decoration: underline; }
1132
+ .int-devicecode a:hover { color: var(--accent); }
1133
+ .int-devicecode .dc-code {
1134
+ font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
1135
+ font-size: 13px;
1136
+ letter-spacing: 0.14em;
1137
+ color: var(--text);
1138
+ background: var(--accent-soft);
1139
+ padding: 2px 7px;
1140
+ border-radius: 4px;
1141
+ user-select: all;
1142
+ }
1143
+ .int-row {
1144
+ display: flex;
1145
+ justify-content: space-between;
1146
+ align-items: center;
1147
+ margin-top: 4px;
1148
+ gap: 6px;
1149
+ }
1150
+ .int-badge {
1151
+ font-size: 9px;
1152
+ text-transform: uppercase;
1153
+ letter-spacing: 0.1em;
1154
+ padding: 2px 8px;
1155
+ border-radius: 10px;
1156
+ font-weight: 600;
1157
+ flex-shrink: 0;
1158
+ }
1159
+ .int-badge-connected { background: rgba(74, 222, 128, 0.15); color: var(--ok); }
1160
+ .int-badge-expired { background: rgba(251, 191, 36, 0.15); color: var(--warn); }
1161
+ .int-badge-disconnected { background: var(--surface-3); color: var(--text-dim); }
1162
+ .int-action {
1163
+ font-size: 10px;
1164
+ padding: 4px 9px;
1165
+ background: var(--surface);
1166
+ border: 1px solid var(--border-strong);
1167
+ color: var(--text-muted);
1168
+ border-radius: 3px;
1169
+ cursor: pointer;
1170
+ font-family: var(--ui);
1171
+ }
1172
+ .int-action:hover { color: var(--text); border-color: var(--accent-dim); background: var(--surface-2); }
1173
+
1174
+ /* ========== FOOTER ========== */
1175
+ footer {
1176
+ grid-area: footer;
1177
+ background: var(--surface);
1178
+ border-top: 1px solid var(--border);
1179
+ display: flex;
1180
+ align-items: center;
1181
+ justify-content: space-between;
1182
+ padding: 0 18px;
1183
+ font-size: 11px;
1184
+ color: var(--text-muted);
1185
+ }
1186
+ .status { display: flex; align-items: center; gap: 14px; }
1187
+ .stat { display: inline-flex; align-items: center; gap: 6px; }
1188
+ .dot {
1189
+ width: 6px;
1190
+ height: 6px;
1191
+ border-radius: 50%;
1192
+ background: var(--ok);
1193
+ display: inline-block;
1194
+ box-shadow: 0 0 6px var(--ok);
1195
+ }
1196
+ .stat-key { color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.1em; font-size: 9px; }
1197
+ .stat-val { font-family: var(--mono); font-size: 11px; color: var(--text); }
1198
+ .sep { color: var(--text-dim); opacity: 0.4; }
1199
+ .version-info { display: inline-flex; align-items: center; gap: 8px; }
1200
+ .aware-version, .app-version { font-family: var(--mono); font-size: 11px; color: var(--text-muted); letter-spacing: 0.02em; }
1201
+ .aware-version:not(:empty) + .app-version::before { content: '·'; margin-right: 8px; opacity: 0.4; }
1202
+
1203
+ /* ========== TOOLTIPS (styled, app-themed; replaces native title="") ========== */
1204
+ #tooltip {
1205
+ position: fixed;
1206
+ z-index: 100000;
1207
+ max-width: 280px;
1208
+ padding: 7px 10px;
1209
+ border-radius: 6px;
1210
+ background: var(--surface-2);
1211
+ color: var(--text);
1212
+ border: 1px solid var(--border-strong);
1213
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
1214
+ font-family: var(--ui);
1215
+ font-size: 12px;
1216
+ line-height: 1.45;
1217
+ white-space: pre-line;
1218
+ pointer-events: none;
1219
+ }
1220
+ #tooltip[hidden] { display: none; }
1221
+
1222
+ /* ========== REQUESTS WINDOW ========== */
1223
+ .modal.requests { width: 580px; max-width: 92vw; }
1224
+ .requests-list { display: flex; flex-direction: column; gap: 8px; max-height: 52vh; overflow-y: auto; margin: 4px 0 2px; }
1225
+ .req-item {
1226
+ display: flex; gap: 10px; align-items: flex-start;
1227
+ padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-2);
1228
+ }
1229
+ .req-info { flex: 1; min-width: 0; }
1230
+ .req-head { font-size: 12px; color: var(--text-dim); display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
1231
+ .req-head code { font-family: var(--mono); color: var(--text-muted); }
1232
+ .req-time { font-family: var(--mono); font-size: 9.5px; color: var(--text-muted); letter-spacing: 0; flex-shrink: 0; margin-left: auto; }
1233
+ .req-type {
1234
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
1235
+ padding: 1px 6px; border-radius: 3px; background: var(--surface);
1236
+ border: 1px solid var(--border-strong); color: var(--accent);
1237
+ }
1238
+ .req-type-tweak { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
1239
+ .req-instruction {
1240
+ font-size: 12px; color: var(--text); line-height: 1.45; word-break: break-word;
1241
+ /* Clamp long instructions so one verbose request can't dominate the list — the
1242
+ full text is always one click away via the copy button. */
1243
+ display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
1244
+ }
1245
+ .req-actions { display: flex; gap: 4px; flex-shrink: 0; }
1246
+ .req-copy, .req-del {
1247
+ /* flex-center the glyph; padding:0 overrides the global button padding (7px 12px)
1248
+ that otherwise leaves 0px content width in the fixed 26x26 box, pushing ⧉ × off-center. */
1249
+ display: inline-flex; align-items: center; justify-content: center; padding: 0;
1250
+ appearance: none; border: 1px solid var(--border-strong); background: transparent;
1251
+ color: var(--text-muted); cursor: pointer; width: 26px; height: 26px;
1252
+ border-radius: 4px; font-size: 13px; line-height: 1;
1253
+ }
1254
+ .req-copy:hover { color: var(--text); background: var(--surface); }
1255
+ .req-del:hover { color: var(--err); border-color: var(--err); }
1256
+ /* "Clear all" is destructive → float it away from the safe Close button, with the
1257
+ danger colour surfacing only on hover (low-stakes queue, so no confirm step). */
1258
+ #requests-clear { margin-right: auto; }
1259
+ #requests-clear:hover { color: var(--err); border-color: var(--err); background: color-mix(in srgb, var(--err) 10%, transparent); }
1260
+
1261
+ /* "Don't save" in the unsaved-changes dialog: left-pushed, neutral at rest,
1262
+ destructive only on hover — same restraint as the Requests "Clear all". */
1263
+ #confirm-dont-save { margin-right: auto; }
1264
+ #confirm-dont-save:hover { color: var(--err); border-color: var(--err); background: color-mix(in srgb, var(--err) 10%, transparent); }
1265
+
1266
+ /* ========== ROUTINES ========== */
1267
+ /* Header entry point: same ghost tier as #browse-btn (Agents) — a utility that
1268
+ opens a management modal, not a workflow step. */
1269
+ #routines-btn { background: transparent; border-color: transparent; color: var(--text-dim); }
1270
+ #routines-btn:hover { background: var(--surface-2); border-color: var(--border-strong); color: var(--text); }
1271
+
1272
+ .modal.routines { width: 600px; max-width: 92vw; max-height: 86vh; display: flex; flex-direction: column; }
1273
+ .rtn-quota { font-size: 11px; color: var(--text-dim); margin-bottom: 10px; flex: 0 0 auto; }
1274
+ .rtn-quota .num { font-family: var(--mono); color: var(--text-muted); }
1275
+ .rtn-quota.near .num { color: var(--warn); }
1276
+ .rtn-quota.full .num { color: var(--err); }
1277
+ .rtn-quota .cap-note { color: var(--text-muted); }
1278
+ .routines-list { display: flex; flex-direction: column; gap: 8px; overflow-y: auto; flex: 1 1 auto; min-height: 0; padding-right: 2px; margin-bottom: 2px; }
1279
+ .rtn-item {
1280
+ display: flex; align-items: center; gap: 10px;
1281
+ padding: 10px 12px; background: var(--surface-2);
1282
+ border: 1px solid var(--border-strong); border-radius: 6px;
1283
+ transition: border-color 0.15s, opacity 0.15s;
1284
+ }
1285
+ .rtn-item:hover { border-color: var(--accent-dim); }
1286
+ .rtn-item.disabled { opacity: 0.58; }
1287
+ .rtn-item.running { border-color: var(--accent-dim); }
1288
+ .rtn-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
1289
+ .rtn-name { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1290
+ .rtn-sub { font-size: 11px; color: var(--text-muted); display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
1291
+ .rtn-wf { font-family: var(--mono); font-size: 10px; color: var(--text-dim); }
1292
+ .rtn-sub .rtn-dot { opacity: 0.4; }
1293
+ .rtn-meta { display: flex; align-items: center; gap: 9px; flex-shrink: 0; }
1294
+ .rtn-next { font-family: var(--mono); font-size: 10px; color: var(--text-dim); white-space: nowrap; }
1295
+ .rtn-status { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; background: var(--text-dim); }
1296
+ .rtn-status.ok { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
1297
+ .rtn-status.failed { background: var(--err); box-shadow: 0 0 6px var(--err); }
1298
+ .rtn-status.skipped { background: var(--warn); box-shadow: 0 0 6px var(--warn); }
1299
+ /* Small inline spinner for an in-flight routine (reuses the global `spin` keyframe;
1300
+ the existing .spinner is sized for the report overlay, too large for a row). */
1301
+ .rtn-spinner { width: 13px; height: 13px; border: 2px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; flex-shrink: 0; }
1302
+ .rtn-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
1303
+ .rtn-act {
1304
+ /* flex-center the glyph; padding:0 overrides the global button padding (7px 12px)
1305
+ that was pushing ▶ ✎ × off-center in the fixed 26x26 box. */
1306
+ display: inline-flex; align-items: center; justify-content: center; padding: 0;
1307
+ appearance: none; border: 1px solid var(--border-strong); background: transparent;
1308
+ color: var(--text-muted); cursor: pointer; width: 26px; height: 26px;
1309
+ border-radius: 4px; font-size: 12px; line-height: 1;
1310
+ }
1311
+ .rtn-act:hover { color: var(--text); border-color: var(--accent-dim); background: var(--surface); }
1312
+ .rtn-del-act:hover { color: var(--err); border-color: var(--err); }
1313
+
1314
+ /* Toggle switch — no switch existed in the app; this is the one new control. */
1315
+ .rtn-toggle { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; position: relative; }
1316
+ .rtn-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
1317
+ .rtn-toggle-track { position: relative; width: 30px; height: 17px; border-radius: 999px; background: var(--border-strong); transition: background 0.15s; flex-shrink: 0; }
1318
+ .rtn-toggle-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--text-muted); transition: transform 0.15s, background 0.15s; }
1319
+ .rtn-toggle input:checked + .rtn-toggle-track { background: var(--accent); }
1320
+ .rtn-toggle input:checked + .rtn-toggle-track::after { transform: translateX(13px); background: #fff; }
1321
+ .rtn-toggle input:focus-visible + .rtn-toggle-track { outline: 2px solid var(--accent); outline-offset: 2px; }
1322
+ .rtn-toggle-label { font-size: 12px; color: var(--text-muted); }
1323
+ /* The form's Enabled toggle is a <label> inside .modal-field; stop the generic
1324
+ `.modal-field label` rule (display:block + uppercase, higher specificity) from
1325
+ breaking the inline switch layout. */
1326
+ .modal-field label.rtn-toggle { display: inline-flex; text-transform: none; letter-spacing: normal; font-size: 12px; color: var(--text-muted); margin-bottom: 0; }
1327
+
1328
+ /* ===== Graft into agent — wizard modal (reuses baseline tokens; no new colors/fonts) ===== */
1329
+ .menu-item:disabled { opacity: 0.45; cursor: not-allowed; }
1330
+ .menu-item:disabled:hover { background: transparent; }
1331
+
1332
+ .modal.graft { width: 600px; max-width: 92vw; max-height: 86vh; display: flex; flex-direction: column; }
1333
+ #graft-body { display: flex; flex-direction: column; gap: 13px; overflow-y: auto; flex: 1 1 auto; min-height: 0; padding-right: 2px; }
1334
+
1335
+ .graft-kinds { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
1336
+ .graft-kind { text-align: left; padding: 9px 11px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--border-strong); color: var(--text); cursor: pointer; transition: border-color 0.12s, background 0.12s; }
1337
+ .graft-kind:hover { border-color: var(--accent-dim); }
1338
+ .graft-kind.selected { border-color: var(--accent); background: var(--accent-soft); }
1339
+ .graft-kind .gk-name { font-weight: 600; font-size: 13px; }
1340
+ .graft-kind .gk-desc { color: var(--text-muted); font-size: 11px; margin-top: 2px; line-height: 1.35; }
1341
+ .graft-more { background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 12px; padding: 2px 0; text-align: left; }
1342
+ .graft-more:hover { color: var(--text); }
1343
+
1344
+ .graft-hint { color: var(--text-dim); font-size: 11px; margin-top: 4px; }
1345
+ .graft-row { display: flex; gap: 8px; align-items: center; }
1346
+ .graft-row input[type="text"] { flex: 1 1 auto; }
1347
+ .graft-matched { background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; margin-top: 8px; }
1348
+ .graft-matched-h { color: var(--text-muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 4px; }
1349
+ .graft-matched-f { font-family: var(--mono); font-size: 11px; color: var(--text-dim); line-height: 1.6; }
1350
+
1351
+ .graft-progress { display: flex; align-items: center; gap: 10px; padding: 26px 8px; color: var(--text-muted); font-size: 13px; }
1352
+ .graft-badge { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; padding: 3px 9px; border-radius: 999px; background: var(--surface-3); color: var(--text-dim); border: 1px solid var(--border-strong); width: fit-content; }
1353
+ .graft-badge.tekla { color: var(--accent-bright); border-color: var(--accent-dim); background: var(--accent-soft); }
1354
+ .graft-warn { border-left: 3px solid var(--warn); background: rgba(251, 191, 36, 0.08); padding: 8px 12px; border-radius: 0 4px 4px 0; font-size: 12px; color: var(--text); line-height: 1.45; }
1355
+
1356
+ .graft-cmds { display: flex; flex-direction: column; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; max-height: 240px; overflow-y: auto; }
1357
+ .graft-cmd { background: var(--surface-2); padding: 7px 10px; }
1358
+ .graft-cmd-name { font-family: var(--mono); font-size: 12px; color: var(--text); font-weight: 600; }
1359
+ .graft-cmd-mode { font-family: var(--mono); font-size: 10px; padding: 1px 5px; border-radius: 3px; margin-left: 6px; }
1360
+ .graft-cmd-mode.write { color: var(--warn); background: rgba(251, 191, 36, 0.1); }
1361
+ .graft-cmd-mode.read { color: var(--info); background: rgba(103, 179, 255, 0.1); }
1362
+ .graft-cmd-in { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-top: 3px; }
1363
+ .graft-skills { font-size: 11px; color: var(--text-dim); }
1364
+
1365
+ .graft-identity { background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: 6px; padding: 10px 12px; display: grid; grid-template-columns: auto 1fr; gap: 5px 12px; font-size: 12px; margin: 0; }
1366
+ .graft-identity dt { color: var(--text-muted); text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em; align-self: center; margin: 0; }
1367
+ .graft-identity dd { color: var(--text); font-family: var(--mono); margin: 0; }
1368
+
1369
+ .graft-success { text-align: center; padding: 26px 8px; }
1370
+ .graft-success .gs-icon { font-size: 30px; color: var(--ok); }
1371
+ .graft-success .gs-msg { margin-top: 8px; font-size: 13px; color: var(--text); }
1372
+ .graft-err { color: var(--err); font-size: 12px; margin-top: 6px; }
1373
+
1374
+ /* ===== App self-update tag (footer) — accent pill, reuses tokens ===== */
1375
+ .app-update { display: none; border: 1px solid var(--accent-dim); background: var(--accent-soft); color: var(--accent-bright); font-size: 10px; padding: 1px 7px; border-radius: 999px; cursor: pointer; margin-left: 8px; line-height: 1.5; }
1376
+ .app-update:not([hidden]) { display: inline-flex; align-items: center; gap: 4px; }
1377
+ .app-update:hover { border-color: var(--accent); color: var(--accent); }
1378
+ .app-update[disabled] { opacity: 0.6; cursor: default; }
1379
+
1380
+ /* Add/edit form */
1381
+ .modal.routine-edit { width: 540px; max-width: 92vw; max-height: 90vh; overflow-y: auto; }
1382
+ #rtn-workflow, .rtn-kind-select { min-width: 0; width: 100%; }
1383
+ .rtn-days { display: flex; flex-wrap: wrap; gap: 5px; }
1384
+ .rtn-day { text-transform: capitalize; }
1385
+ /* Time picker: two app-styled selects (replaces the native time input so it
1386
+ matches the dark baseline instead of the OS picker). */
1387
+ .rtn-time { display: flex; align-items: center; gap: 8px; }
1388
+ .rtn-time select { min-width: 0; width: auto; flex: 0 0 auto; }
1389
+ .rtn-time-sep { color: var(--text-dim); font-family: var(--mono); }
1390
+ #rtn-cron { font-family: var(--mono); letter-spacing: 0.02em; }
1391
+ .rtn-cron-hint { margin-top: 7px; font-size: 10.5px; color: var(--text-dim); line-height: 1.55; }
1392
+ .rtn-cron-hint code { font-family: var(--mono); color: var(--text-muted); background: var(--surface-2); padding: 0 3px; border-radius: 3px; }
1393
+ .rtn-enabled-field { margin-top: 6px; margin-bottom: 0; }
1394
+ #rtn-inputs .modal-field:last-child { margin-bottom: 0; }
1395
+ /* Destructive confirm: neutral at rest, danger only on hover (same restraint as
1396
+ #confirm-dont-save / #requests-clear). Never the accent primary. */
1397
+ #rtn-delete-confirm:hover { color: var(--err); border-color: var(--err); background: color-mix(in srgb, var(--err) 10%, transparent); }
1398
+
1399
+ /* ========== WORKFLOW COMBOBOX (searchable, provider-grouped) ========== */
1400
+ .wf-combo { position: relative; display: inline-flex; }
1401
+ .wf-trigger {
1402
+ display: inline-flex; align-items: center; gap: 8px;
1403
+ min-width: 300px; max-width: 400px;
1404
+ background: var(--surface-2); color: var(--text);
1405
+ border: 1px solid var(--border-strong); border-radius: 4px;
1406
+ padding: 7px 12px; font-family: var(--ui); font-size: 12px; cursor: pointer;
1407
+ transition: border-color 0.15s;
1408
+ }
1409
+ .wf-trigger:hover, .wf-trigger.open { border-color: var(--accent-dim); }
1410
+ .wf-trigger:disabled { opacity: 0.6; cursor: default; }
1411
+ .wf-trigger-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
1412
+ .wf-caret { color: var(--text-muted); flex-shrink: 0; transition: transform 0.15s; }
1413
+ .wf-trigger.open .wf-caret { transform: rotate(180deg); }
1414
+ /* Unsaved-input indicator: a warn-amber dot tucked beside the caret. Shown only
1415
+ when the open workflow has input values that differ from the last save. */
1416
+ .wf-dirty-dot { flex-shrink: 0; color: var(--warn); font-size: 9px; line-height: 1; margin-left: -2px; }
1417
+ .wf-dirty-dot[hidden] { display: none; }
1418
+
1419
+ .wf-popover {
1420
+ position: absolute; top: calc(100% + 4px); left: 0;
1421
+ min-width: 100%; width: max-content; max-width: 460px;
1422
+ background: var(--surface); border: 1px solid var(--border-strong);
1423
+ border-radius: 6px; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
1424
+ z-index: 50; display: flex; flex-direction: column;
1425
+ animation: wf-in 0.16s ease-out;
1426
+ }
1427
+ @keyframes wf-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
1428
+ .wf-popover[hidden] { display: none; }
1429
+ .wf-search-wrap { padding: 8px 8px 4px; }
1430
+ .wf-search {
1431
+ width: 100%; box-sizing: border-box;
1432
+ background: var(--surface-2); color: var(--text);
1433
+ border: 1px solid var(--border-strong); border-radius: 4px;
1434
+ padding: 8px 10px; font-family: var(--ui); font-size: 13px; outline: none;
1435
+ }
1436
+ .wf-search:focus { border-color: var(--accent); }
1437
+ .wf-list { overflow-y: auto; max-height: 320px; padding: 2px 4px 6px; }
1438
+ .wf-group[hidden] { display: none; }
1439
+ .wf-group:not(:first-child) .wf-group-header { border-top: 1px solid var(--border); margin-top: 4px; }
1440
+ .wf-group-header {
1441
+ font-size: 9px; text-transform: uppercase; letter-spacing: 0.16em; font-weight: 600;
1442
+ color: var(--text-dim); padding: 10px 8px 4px;
1443
+ }
1444
+ .wf-option {
1445
+ display: flex; align-items: center; justify-content: space-between; gap: 10px;
1446
+ width: 100%; box-sizing: border-box;
1447
+ background: transparent; border: none; border-left: 2px solid transparent;
1448
+ border-radius: 4px; padding: 6px 8px; cursor: pointer; text-align: left;
1449
+ font-family: var(--ui); font-size: 12px; color: var(--text);
1450
+ }
1451
+ .wf-option[hidden] { display: none; }
1452
+ .wf-option:hover, .wf-option.highlighted { background: var(--surface-2); }
1453
+ .wf-option.selected { border-left-color: var(--accent); }
1454
+ .wf-option.selected .wf-option-name { color: var(--accent); }
1455
+ .wf-option-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1456
+ .wf-option-meta { font-family: var(--mono); font-size: 10px; color: var(--text-dim); white-space: nowrap; flex-shrink: 0; }
1457
+ .wf-empty-search { color: var(--text-dim); font-style: italic; font-size: 12px; padding: 16px 10px; text-align: center; }
1458
+ .wf-empty-search[hidden] { display: none; }
1459
+
1460
+ /* ========== CANVAS TOOLBAR (zoom + undo) ========== */
1461
+ .canvas-toolbar {
1462
+ position: absolute;
1463
+ bottom: 56px;
1464
+ left: 14px;
1465
+ display: flex;
1466
+ gap: 8px;
1467
+ z-index: 5;
1468
+ }
1469
+ .toolbar-group {
1470
+ display: flex;
1471
+ background: var(--surface);
1472
+ border: 1px solid var(--border-strong);
1473
+ border-radius: 6px;
1474
+ padding: 3px;
1475
+ box-shadow: 0 4px 14px rgba(0,0,0,0.3);
1476
+ }
1477
+ .tb-btn {
1478
+ background: transparent;
1479
+ border: none;
1480
+ color: var(--text-muted);
1481
+ width: 28px;
1482
+ height: 26px;
1483
+ padding: 0;
1484
+ font-size: 14px;
1485
+ line-height: 1;
1486
+ cursor: pointer;
1487
+ border-radius: 3px;
1488
+ font-family: var(--ui);
1489
+ display: inline-flex;
1490
+ align-items: center;
1491
+ justify-content: center;
1492
+ }
1493
+ .tb-btn:hover {
1494
+ color: var(--text);
1495
+ background: var(--surface-2);
1496
+ }
1497
+ .tb-btn:disabled {
1498
+ opacity: 0.35;
1499
+ cursor: not-allowed;
1500
+ }
1501
+ .tb-btn:disabled:hover { background: transparent; color: var(--text-muted); }
1502
+ .tb-pct {
1503
+ width: 50px;
1504
+ font-family: var(--mono);
1505
+ font-size: 11px;
1506
+ color: var(--text);
1507
+ }
1508
+ .tb-pct:hover { color: var(--accent); }
1509
+
1510
+ /* ========== PORTS in agent footer ========== */
1511
+ .agent-card .ports {
1512
+ font-family: var(--mono);
1513
+ font-size: 10px;
1514
+ color: var(--text-muted);
1515
+ }
1516
+ .agent-card .ports .ports-arrow {
1517
+ color: var(--text-dim);
1518
+ margin: 0 2px;
1519
+ }
1520
+ .agent-card .ports .ports-num {
1521
+ color: var(--text);
1522
+ }
1523
+
1524
+ /* ========== HAMBURGER + MENU ========== */
1525
+ .hamburger {
1526
+ background: transparent;
1527
+ color: var(--text);
1528
+ border: 1px solid var(--border-strong);
1529
+ width: 34px;
1530
+ height: 34px;
1531
+ padding: 0;
1532
+ display: flex;
1533
+ align-items: center;
1534
+ justify-content: center;
1535
+ font-size: 18px;
1536
+ line-height: 1;
1537
+ cursor: pointer;
1538
+ border-radius: 4px;
1539
+ transition: all 0.15s;
1540
+ margin-right: 10px;
1541
+ flex-shrink: 0;
1542
+ }
1543
+ .hamburger:hover {
1544
+ border-color: var(--accent-dim);
1545
+ background: var(--surface-2);
1546
+ }
1547
+ .hamburger.open {
1548
+ border-color: var(--accent);
1549
+ background: var(--accent-soft);
1550
+ color: var(--accent);
1551
+ }
1552
+
1553
+ .menu {
1554
+ position: fixed;
1555
+ top: 60px;
1556
+ left: 14px;
1557
+ width: 280px;
1558
+ background: var(--surface);
1559
+ border: 1px solid var(--border-strong);
1560
+ border-radius: 6px;
1561
+ padding: 6px;
1562
+ box-shadow: 0 16px 40px rgba(0,0,0,0.5);
1563
+ z-index: 50;
1564
+ display: none;
1565
+ animation: menu-in 0.16s ease-out;
1566
+ }
1567
+ .menu.show { display: block; }
1568
+ @keyframes menu-in {
1569
+ from { opacity: 0; transform: translateY(-6px); }
1570
+ to { opacity: 1; transform: translateY(0); }
1571
+ }
1572
+ .menu-item {
1573
+ display: flex;
1574
+ align-items: center;
1575
+ gap: 10px;
1576
+ width: 100%;
1577
+ background: transparent;
1578
+ border: none;
1579
+ padding: 7px 9px;
1580
+ font-size: 12px;
1581
+ color: var(--text);
1582
+ cursor: pointer;
1583
+ border-radius: 4px;
1584
+ text-align: left;
1585
+ font-family: var(--ui);
1586
+ }
1587
+ .menu-item:hover { background: var(--surface-2); }
1588
+ /* Save item when the open workflow has unsaved input changes — echoes the
1589
+ header dirty-dot so the menu reinforces it without a new component. */
1590
+ .menu-item-dirty .menu-icon { color: var(--warn); }
1591
+ .menu-item-dirty .menu-label::after { content: " ●"; color: var(--warn); font-size: 9px; vertical-align: 1px; }
1592
+ .menu-icon {
1593
+ width: 18px;
1594
+ font-size: 13px;
1595
+ color: var(--text-muted);
1596
+ display: inline-flex;
1597
+ align-items: center;
1598
+ justify-content: center;
1599
+ flex-shrink: 0;
1600
+ }
1601
+ .menu-label { flex: 1; }
1602
+ .menu-kbd {
1603
+ font-family: var(--mono);
1604
+ font-size: 10px;
1605
+ color: var(--text-dim);
1606
+ background: var(--surface-2);
1607
+ border: 1px solid var(--border);
1608
+ padding: 1px 5px;
1609
+ border-radius: 3px;
1610
+ }
1611
+ .menu-arrow { color: var(--text-dim); font-size: 14px; }
1612
+ .menu-divider {
1613
+ height: 1px;
1614
+ background: var(--border);
1615
+ margin: 5px 4px;
1616
+ }
1617
+ .menu-theme {
1618
+ display: flex;
1619
+ align-items: center;
1620
+ justify-content: space-between;
1621
+ padding: 7px 9px;
1622
+ font-size: 12px;
1623
+ color: var(--text);
1624
+ }
1625
+ /* The Start-on-startup row reuses .menu-theme (display:flex), which overrides its
1626
+ `hidden` attribute, so it showed even when unsupported (dev/non-Windows). Restore
1627
+ the hide with a class+attribute selector (higher specificity than .menu-theme). */
1628
+ .menu-startup[hidden] { display: none; }
1629
+ .theme-toggle {
1630
+ display: flex;
1631
+ gap: 2px;
1632
+ background: var(--surface-2);
1633
+ border: 1px solid var(--border);
1634
+ border-radius: 4px;
1635
+ padding: 2px;
1636
+ }
1637
+ .theme-btn {
1638
+ background: transparent;
1639
+ border: none;
1640
+ width: 26px;
1641
+ height: 22px;
1642
+ padding: 0;
1643
+ color: var(--text-muted);
1644
+ font-size: 13px;
1645
+ border-radius: 3px;
1646
+ cursor: pointer;
1647
+ }
1648
+ .theme-btn:hover { color: var(--text); }
1649
+ .theme-btn.active {
1650
+ background: var(--accent);
1651
+ color: white;
1652
+ }
1653
+
1654
+ /* ========== TOAST ========== */
1655
+ .toast-container {
1656
+ position: fixed;
1657
+ bottom: 60px;
1658
+ right: 20px;
1659
+ display: flex;
1660
+ flex-direction: column;
1661
+ gap: 6px;
1662
+ z-index: 200;
1663
+ pointer-events: none;
1664
+ }
1665
+ .toast {
1666
+ background: var(--surface);
1667
+ border: 1px solid var(--border-strong);
1668
+ border-left: 3px solid var(--accent);
1669
+ color: var(--text);
1670
+ padding: 10px 14px;
1671
+ font-size: 12px;
1672
+ border-radius: 4px;
1673
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
1674
+ animation: toast-in 0.2s ease-out;
1675
+ pointer-events: auto;
1676
+ min-width: 240px;
1677
+ display: flex;
1678
+ align-items: center;
1679
+ gap: 10px;
1680
+ }
1681
+ .toast-msg { flex: 1; min-width: 0; }
1682
+ .toast-close {
1683
+ appearance: none; border: none; background: transparent; color: var(--text-dim);
1684
+ cursor: pointer; font-size: 15px; line-height: 1; padding: 0; flex-shrink: 0;
1685
+ }
1686
+ .toast-close:hover { color: var(--text); }
1687
+ .toast.ok { border-left-color: var(--ok); }
1688
+ .toast.warn { border-left-color: var(--warn); }
1689
+ .toast.err { border-left-color: var(--err); }
1690
+ @keyframes toast-in {
1691
+ from { opacity: 0; transform: translateY(8px); }
1692
+ to { opacity: 1; transform: translateY(0); }
1693
+ }
1694
+
1695
+ /* ========== FIND OVERLAY ========== */
1696
+ .find-overlay {
1697
+ position: absolute;
1698
+ top: 14px;
1699
+ left: 50%;
1700
+ transform: translateX(-50%);
1701
+ background: var(--surface);
1702
+ border: 1px solid var(--border-strong);
1703
+ border-radius: 6px;
1704
+ padding: 6px 8px;
1705
+ box-shadow: 0 8px 24px rgba(0,0,0,0.5);
1706
+ display: none;
1707
+ align-items: center;
1708
+ gap: 8px;
1709
+ z-index: 10;
1710
+ }
1711
+ .find-overlay.show { display: flex; }
1712
+ .find-overlay input {
1713
+ background: var(--surface-2);
1714
+ border: 1px solid var(--border);
1715
+ color: var(--text);
1716
+ padding: 6px 10px;
1717
+ font-family: var(--ui);
1718
+ font-size: 12px;
1719
+ border-radius: 3px;
1720
+ outline: none;
1721
+ width: 220px;
1722
+ }
1723
+ .find-overlay input:focus { border-color: var(--accent); }
1724
+ .find-count {
1725
+ font-family: var(--mono);
1726
+ font-size: 10px;
1727
+ color: var(--text-dim);
1728
+ }
1729
+ .find-close {
1730
+ background: transparent;
1731
+ border: none;
1732
+ color: var(--text-dim);
1733
+ cursor: pointer;
1734
+ font-size: 16px;
1735
+ padding: 0 6px;
1736
+ line-height: 1;
1737
+ }
1738
+ .find-close:hover { color: var(--text); }
1739
+ .agent-card.find-dim { opacity: 0.22; }
1740
+ .agent-card.find-match {
1741
+ border-color: var(--star) !important;
1742
+ box-shadow: 0 0 0 1px var(--star), 0 0 18px rgba(251, 191, 36, 0.3);
1743
+ }
1744
+
1745
+ /* ========== LIGHT THEME ========== */
1746
+ [data-theme="light"] {
1747
+ --bg: #f7f9fc;
1748
+ --surface: #ffffff;
1749
+ --surface-2: #f1f5f9;
1750
+ --surface-3: #e2e8f0;
1751
+ --border: #e2e8f0;
1752
+ --border-strong: #cbd5e1;
1753
+ --text: #0f172a;
1754
+ --text-muted: #475569;
1755
+ --text-dim: #94a3b8;
1756
+ --accent: #2563eb;
1757
+ --accent-bright: #3b82f6;
1758
+ --accent-dim: #93c5fd;
1759
+ --accent-glow: rgba(37, 99, 235, 0.3);
1760
+ --accent-soft: rgba(37, 99, 235, 0.08);
1761
+ --star: #d97706;
1762
+ --ok: #16a34a;
1763
+ --warn: #d97706;
1764
+ --err: #dc2626;
1765
+ --info: #2563eb;
1766
+ }
1767
+ [data-theme="light"] body {
1768
+ background-image:
1769
+ linear-gradient(rgba(37, 99, 235, 0.05) 1px, transparent 1px),
1770
+ linear-gradient(90deg, rgba(37, 99, 235, 0.05) 1px, transparent 1px);
1771
+ }
1772
+ [data-theme="light"] pre.code,
1773
+ [data-theme="light"] .trace,
1774
+ [data-theme="light"] .host-banner {
1775
+ background: #f8fafc;
1776
+ color: var(--text);
1777
+ }
1778
+ [data-theme="light"] pre.code .kw { color: #7e22ce; }
1779
+ [data-theme="light"] pre.code .ty { color: #1d4ed8; }
1780
+ [data-theme="light"] pre.code .st { color: #16a34a; }
1781
+ [data-theme="light"] pre.code .nm { color: #be185d; }
1782
+ [data-theme="light"] pre.code .fn { color: #c2410c; }
1783
+ [data-theme="light"] pre.code .nu { color: #1e40af; }
1784
+ [data-theme="light"] pre.code .cm { color: #64748b; }
1785
+ [data-theme="light"] .trace .lvl.info { color: #2563eb; }
1786
+ [data-theme="light"] .trace .lvl.event { color: #7e22ce; }
1787
+ [data-theme="light"] .trace .lvl.pass,
1788
+ [data-theme="light"] .trace .lvl.ok { color: #16a34a; }
1789
+ [data-theme="light"] .trace .lvl.warn { color: #d97706; }
1790
+ [data-theme="light"] .trace .lvl.err { color: #dc2626; }
1791
+ [data-theme="light"] .dag-label-bg { fill: var(--bg); stroke: var(--border); }
1792
+
1793
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
1794
+ ::-webkit-scrollbar-track { background: transparent; }
1795
+ ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
1796
+ ::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
1797
+
1798
+ /* Block 2: impeccable-subtle-web — floless-web shadcn token overrides (wins the cascade) */
1799
+ :root {
1800
+ /* dark theme — floless-web .dark + 1-step synthesized surface/surface-3 in same 217° hue */
1801
+ --bg: hsl(222.2 84% 4.9%);
1802
+ --surface: hsl(217.2 32.6% 11%); /* 1-step lift between bg and secondary */
1803
+ --surface-2: hsl(217.2 32.6% 17.5%); /* floless-web --secondary */
1804
+ --surface-3: hsl(217.2 32.6% 22%); /* 1-step lift above secondary */
1805
+ --border: hsl(217.2 32.6% 14%); /* slightly under floless-web --border */
1806
+ --border-strong: hsl(217.2 32.6% 17.5%); /* floless-web --border */
1807
+ --text: hsl(210 40% 98%);
1808
+ --text-muted: hsl(215 20.2% 65.1%);
1809
+ --text-dim: hsl(217.2 16% 45%);
1810
+ --accent: hsl(217.2 91.2% 59.8%);
1811
+ --accent-bright: hsl(213 94% 68%);
1812
+ --accent-dim: hsl(224.3 76.3% 48%); /* floless-web --ring */
1813
+ --accent-glow: hsl(217.2 91.2% 59.8% / 0.40);
1814
+ --accent-soft: hsl(217.2 91.2% 59.8% / 0.10);
1815
+ --star: hsl(38 92% 50%);
1816
+ --ok: hsl(142 71% 45%);
1817
+ --warn: hsl(38 92% 50%);
1818
+ --err: hsl(0 62.8% 50%); /* shifted lighter from floless-web's 30.6% so it reads on dark */
1819
+ --info: hsl(217.2 91.2% 59.8%);
1820
+ }
1821
+
1822
+ [data-theme="light"] {
1823
+ /* light theme — floless-web :root */
1824
+ --bg: hsl(0 0% 100%);
1825
+ --surface: hsl(0 0% 100%);
1826
+ --surface-2: hsl(210 40% 96.1%); /* floless-web --secondary/--muted */
1827
+ --surface-3: hsl(214.3 31.8% 91.4%); /* floless-web --border */
1828
+ --border: hsl(214.3 31.8% 91.4%);
1829
+ --border-strong: hsl(214.3 31.8% 82%);
1830
+ --text: hsl(222.2 84% 4.9%);
1831
+ --text-muted: hsl(215.4 16.3% 46.9%);
1832
+ --text-dim: hsl(215.4 16.3% 60%);
1833
+ --accent: hsl(221.2 83.2% 53.3%); /* floless-web --primary */
1834
+ --accent-bright: hsl(217.2 91.2% 59.8%);
1835
+ --accent-dim: hsl(217 91% 75%);
1836
+ --accent-glow: hsl(221.2 83.2% 53.3% / 0.30);
1837
+ --accent-soft: hsl(221.2 83.2% 53.3% / 0.08);
1838
+ --star: hsl(38 92% 50%);
1839
+ --ok: hsl(142 71% 45%);
1840
+ --warn: hsl(38 92% 50%);
1841
+ --err: hsl(0 84.2% 60.2%); /* floless-web --destructive */
1842
+ --info: hsl(221.2 83.2% 53.3%);
1843
+ }
1844
+
1845
+ /* Re-tint the body grid to match the new accent hue */
1846
+ body {
1847
+ background-image:
1848
+ linear-gradient(hsl(217.2 91.2% 59.8% / 0.025) 1px, transparent 1px),
1849
+ linear-gradient(90deg, hsl(217.2 91.2% 59.8% / 0.025) 1px, transparent 1px);
1850
+ }
1851
+ [data-theme="light"] body {
1852
+ background-image:
1853
+ linear-gradient(hsl(221.2 83.2% 53.3% / 0.05) 1px, transparent 1px),
1854
+ linear-gradient(90deg, hsl(221.2 83.2% 53.3% / 0.05) 1px, transparent 1px);
1855
+ }
1856
+
1857
+ /* ===== floless.app live-wiring additions (not in the demo) ===== */
1858
+
1859
+ /* Node mode stripe — write = red, read = gray (Glass Box semantics).
1860
+ A left ::after ribbon survives the .selected border/box-shadow override. */
1861
+ .agent-card.mode-write::after,
1862
+ .agent-card.mode-read::after {
1863
+ content: "";
1864
+ position: absolute;
1865
+ left: 0; top: 0; bottom: 0;
1866
+ width: 3px;
1867
+ z-index: 2;
1868
+ }
1869
+ .agent-card.mode-write::after { background: var(--err); }
1870
+ .agent-card.mode-read::after { background: var(--text-dim); }
1871
+
1872
+ /* Compile / approve — the gated step before Run. Outline-accent tier: an
1873
+ accent-dim border pairs it visually with the filled Run button, so the eye
1874
+ reads "Compile → Run" as the spine, above the muted Simulate. */
1875
+ #compile-btn { color: var(--text); border-color: var(--accent-dim); }
1876
+ #compile-btn:hover { color: var(--text); border-color: var(--accent); }
1877
+ #compile-btn:disabled { color: var(--text-dim); border-color: var(--border-strong); cursor: default; opacity: 0.6; }
1878
+
1879
+ /* Bake into agent — header button mirrors Compile (a packaging sibling). */
1880
+ #bake-btn { background: var(--surface-2); color: var(--text); border-color: var(--accent-dim); }
1881
+ #bake-btn:hover { color: var(--text); border-color: var(--accent); }
1882
+ #bake-btn:disabled { color: var(--text-dim); border-color: var(--border-strong); cursor: default; opacity: 0.6; }
1883
+ /* Bake modal preview: agent name + inputs chips (mirror the canvas node-input chips). */
1884
+ #bake-modal .bake-name code { font-family: var(--mono); font-size: 12px; color: var(--text-muted); }
1885
+ #bake-modal .bake-inputs { display: flex; flex-wrap: wrap; gap: 6px; }
1886
+ #bake-modal .bake-inputs .ni-pair { display: inline-flex; gap: 6px; padding: 3px 8px; border: 1px solid var(--border-strong);
1887
+ border-radius: 6px; background: var(--surface-2); font-family: var(--mono); font-size: 11px; }
1888
+ #bake-modal .bake-inputs .ni-key { color: var(--text-muted); }
1889
+ #bake-modal .bake-inputs .ni-val { color: var(--accent); font-weight: 600; }
1890
+ #bake-modal .bake-noinputs { color: var(--text-dim); font-style: italic; font-size: 12px; }
1891
+ #bake-modal .bake-progress { display: flex; align-items: center; gap: 10px; padding: 8px 0; font-size: 13px; color: var(--text-muted); }
1892
+
1893
+ /* Run-gate state pill in the header. */
1894
+ .run-state {
1895
+ font-size: 11px;
1896
+ letter-spacing: 0.04em;
1897
+ padding: 2px 8px;
1898
+ border-radius: 999px;
1899
+ border: 1px solid transparent;
1900
+ white-space: nowrap;
1901
+ }
1902
+ .run-state:empty { display: none; }
1903
+ .run-state.ready { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
1904
+ .run-state.drift { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
1905
+ .run-state.uncompiled { color: var(--text-muted); border-color: var(--border-strong); }
1906
+
1907
+ /* Compile-notes strip over the canvas (CONCERNS §1). These are info-level FYIs
1908
+ (e.g. AWARE's read-mode-on-exec note), so they read as info — not a warning —
1909
+ and collapse to a faint one-line pill so they don't eat canvas height. */
1910
+ .notes-strip {
1911
+ margin: 8px 12px 0;
1912
+ padding: 8px 12px;
1913
+ border: 1px solid color-mix(in srgb, var(--info) 32%, transparent);
1914
+ background: color-mix(in srgb, var(--info) 8%, transparent);
1915
+ border-radius: 6px;
1916
+ color: var(--text);
1917
+ font-size: 12px;
1918
+ line-height: 1.5;
1919
+ }
1920
+ .notes-strip .notes-head {
1921
+ display: flex;
1922
+ align-items: center;
1923
+ justify-content: space-between;
1924
+ gap: 10px;
1925
+ color: var(--info);
1926
+ font-weight: 600;
1927
+ margin-bottom: 2px;
1928
+ }
1929
+ .notes-strip .notes-title { display: inline-flex; align-items: center; gap: 6px; }
1930
+ .notes-strip .notes-dismiss {
1931
+ appearance: none; border: none; background: transparent; cursor: pointer; flex: none;
1932
+ color: var(--text-dim); font-size: 14px; line-height: 1; padding: 2px 7px; border-radius: 4px;
1933
+ }
1934
+ .notes-strip .notes-dismiss:hover { color: var(--text); background: color-mix(in srgb, var(--info) 14%, transparent); }
1935
+ .notes-strip ul { margin: 4px 0 0; padding-left: 18px; }
1936
+ .notes-strip li { margin: 1px 0; }
1937
+ .notes-strip code { font-family: var(--mono); color: var(--accent); }
1938
+ .notes-strip .note-kind {
1939
+ display: inline-block; font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em;
1940
+ color: var(--info); border: 1px solid color-mix(in srgb, var(--info) 35%, transparent);
1941
+ border-radius: 3px; padding: 0 4px; margin-right: 6px; vertical-align: 1px;
1942
+ }
1943
+ .notes-strip .note-kind.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
1944
+ .notes-strip .note-kind.error { color: var(--err); border-color: color-mix(in srgb, var(--err) 45%, transparent); }
1945
+ /* Strip severity = worst note kind. info is the base (blue); warn/error escalate. */
1946
+ .notes-strip.sev-warn { border-color: color-mix(in srgb, var(--warn) 38%, transparent); background: color-mix(in srgb, var(--warn) 9%, transparent); }
1947
+ .notes-strip.sev-warn .notes-head { color: var(--warn); }
1948
+ .notes-strip.sev-error { border-color: color-mix(in srgb, var(--err) 42%, transparent); background: color-mix(in srgb, var(--err) 10%, transparent); }
1949
+ .notes-strip.sev-error .notes-head { color: var(--err); }
1950
+ /* Collapsed → faint single line; the whole row re-expands on click. */
1951
+ .notes-strip.collapsed { background: transparent; border-color: color-mix(in srgb, var(--info) 18%, transparent); cursor: pointer; }
1952
+ .notes-strip.collapsed ul { display: none; }
1953
+ .notes-strip.collapsed .notes-head { color: var(--text-dim); margin-bottom: 0; }
1954
+ .notes-strip.collapsed .notes-dismiss { display: none; }
1955
+ .notes-strip.collapsed .notes-title::after { content: " — show"; color: var(--text-dim); font-weight: 400; }
1956
+
1957
+ /* Inspect "Tweak" button — opens the reverse-channel request to the host AI. */
1958
+ .tweak-btn {
1959
+ font-size: 11px;
1960
+ padding: 2px 8px;
1961
+ color: var(--text-muted);
1962
+ background: var(--surface-2);
1963
+ border: 1px solid var(--border-strong);
1964
+ border-radius: 4px;
1965
+ }
1966
+ .tweak-btn:hover { color: var(--text); border-color: var(--accent-dim); }
1967
+ .tweak-btn[hidden] { display: none; }
1968
+
1969
+ /* Footer "requests" pill — lights up when intents are waiting for the AI. */
1970
+ .stat-requests { cursor: pointer; }
1971
+ .stat-requests.has-pending .stat-val { color: var(--warn); font-weight: 600; }
1972
+ .stat-requests.has-pending { text-shadow: 0 0 8px color-mix(in srgb, var(--warn) 40%, transparent); }
1973
+
1974
+ /* Library: agent rows expand to their commands; each command can be saved. */
1975
+ .lib-commands { margin: 6px 0 2px; padding-left: 4px; border-left: 2px solid var(--border); }
1976
+ .lib-cmd {
1977
+ display: flex; align-items: center; gap: 8px;
1978
+ padding: 4px 8px; border-radius: 4px;
1979
+ }
1980
+ .lib-cmd:hover { background: var(--surface-2); }
1981
+ .lib-cmd .cmd-name { font-family: var(--mono); color: var(--accent); font-size: 12px; }
1982
+ .lib-cmd .cmd-cat { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
1983
+ .lib-cmd .cmd-desc { color: var(--text-muted); font-size: 11px; flex: 1; }
1984
+ .lib-cmd .cmd-save { background: none; border: none; color: var(--text-dim); cursor: pointer; padding: 2px 6px; }
1985
+ .lib-cmd .cmd-save:hover { color: var(--star); }
1986
+
1987
+ /* Inspect: key/value table for real node inputs. */
1988
+ .kv { width: 100%; border-collapse: collapse; margin: 6px 0 10px; }
1989
+ .kv th, .kv td { text-align: left; padding: 3px 8px; vertical-align: top; font-size: 12px; }
1990
+ .kv th { color: var(--text-muted); font-weight: 500; white-space: nowrap; width: 1%; }
1991
+ .kv td { font-family: var(--mono); color: var(--text); word-break: break-word; }
1992
+ .kv tr + tr th, .kv tr + tr td { border-top: 1px solid var(--border); }
1993
+
1994
+ /* ── HTML Viewer (report) modal ─────────────────────────────────────────────── */
1995
+ .modal.report-viewer {
1996
+ width: 1180px; max-width: 94vw; height: 86vh;
1997
+ padding: 0; display: flex; flex-direction: column; overflow: hidden;
1998
+ }
1999
+ .report-head {
2000
+ display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
2001
+ padding: 14px 18px; border-bottom: 1px solid var(--border);
2002
+ background: var(--surface-2);
2003
+ }
2004
+ .report-head .modal-sub { margin-bottom: 0; }
2005
+ .report-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
2006
+ .report-actions button { padding: 6px 12px; }
2007
+ .report-actions button.ghost { background: var(--surface-3); color: var(--text-muted); }
2008
+ .report-actions button.ghost:hover { color: var(--text); border-color: var(--accent-dim); }
2009
+ #report-close { font-size: 18px; line-height: 1; padding: 4px 10px; }
2010
+ .report-stage { position: relative; flex: 1; background: #0f172a; }
2011
+ #report-frame { width: 100%; height: 100%; border: 0; display: block; background: #0f172a; }
2012
+ .report-overlay {
2013
+ position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
2014
+ flex-direction: column; gap: 12px; background: rgba(7, 10, 15, 0.82);
2015
+ color: var(--text-muted); font-size: 13px;
2016
+ }
2017
+ /* `display:flex` above overrides the UA `[hidden]{display:none}`, so without this
2018
+ the loading scrim stays painted over the iframe after the report loads — the
2019
+ report looks permanently dimmed. Honor the hidden attribute explicitly. */
2020
+ .report-overlay[hidden] { display: none; }
2021
+ .report-overlay .spinner {
2022
+ width: 28px; height: 28px; border: 3px solid var(--border-strong);
2023
+ border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite;
2024
+ }
2025
+ @keyframes spin { to { transform: rotate(360deg); } }
2026
+ /* Stop the in-flight run — the escape hatch when a host hangs. Reads as a quiet
2027
+ destructive action: warn-tinted outline that fills on hover, never a primary. */
2028
+ .report-overlay .overlay-stop {
2029
+ margin-top: 4px; padding: 6px 16px; border-radius: 6px;
2030
+ background: transparent; color: var(--warn);
2031
+ border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
2032
+ font: 600 12px/1.2 var(--ui); cursor: pointer;
2033
+ transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
2034
+ }
2035
+ .report-overlay .overlay-stop:hover {
2036
+ background: color-mix(in srgb, var(--warn) 16%, transparent); border-color: var(--warn); color: var(--text);
2037
+ }
2038
+ .report-overlay .overlay-stop:disabled { opacity: 0.6; cursor: default; }
2039
+
2040
+ /* Report + input nodes on the canvas carry a first-class action button
2041
+ ("View report ▸" / "Set inputs ▸"). Double-click still works as a shortcut. */
2042
+ .agent-card.report-node,
2043
+ .agent-card.input-node { cursor: pointer; }
2044
+ .agent-card .node-action {
2045
+ display: block; width: 100%; margin-top: 8px;
2046
+ padding: 5px 8px; border-radius: 6px;
2047
+ background: var(--accent-soft); color: var(--accent);
2048
+ border: 1px solid var(--accent-dim);
2049
+ font: 500 11px/1.2 var(--ui); text-align: center; cursor: pointer;
2050
+ transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
2051
+ }
2052
+ .agent-card .node-action:hover {
2053
+ background: color-mix(in srgb, var(--accent) 22%, transparent);
2054
+ border-color: var(--accent); color: var(--text);
2055
+ }
2056
+ /* Input node: current values at a glance ("phase = 2"), so the user knows what
2057
+ the next run uses without opening the dialog. */
2058
+ .agent-card .node-inputs { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
2059
+ .agent-card .node-inputs .ni-pair {
2060
+ display: inline-flex; align-items: baseline; gap: 5px;
2061
+ padding: 2px 7px; border-radius: 4px;
2062
+ background: var(--surface-2); border: 1px solid var(--border);
2063
+ font: 500 11px/1.3 var(--mono);
2064
+ }
2065
+ .agent-card .node-inputs .ni-key { color: var(--text-muted); }
2066
+ .agent-card .node-inputs .ni-val { color: var(--accent); font-weight: 600; }
2067
+
2068
+ /* Per-node run status, painted from the live trace (running → done / error).
2069
+ Uses a box-shadow ring (composes with the left mode-stripe ::after) plus a
2070
+ text pip — colour is never the only signal. */
2071
+ .agent-card.node-running { box-shadow: 0 0 0 1px var(--accent), 0 0 12px var(--accent-glow); animation: nodePulse 1.3s ease-in-out infinite; }
2072
+ @keyframes nodePulse {
2073
+ 0%, 100% { box-shadow: 0 0 0 1px var(--accent), 0 0 5px var(--accent-glow); }
2074
+ 50% { box-shadow: 0 0 0 1px var(--accent), 0 0 16px var(--accent-glow); }
2075
+ }
2076
+ .agent-card.node-done { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ok) 50%, transparent); }
2077
+ .agent-card.node-error { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--err) 60%, transparent); }
2078
+ .agent-card .node-stat {
2079
+ display: block; margin: 10px -14px -14px; /* bleed into the card's padding → flush bottom bar */
2080
+ padding: 5px 14px; text-align: center;
2081
+ font: 600 10px/1.5 var(--ui); letter-spacing: 0.04em;
2082
+ border-top: 1px solid var(--border);
2083
+ }
2084
+ .agent-card.node-running .node-stat { color: var(--accent); background: var(--accent-soft); }
2085
+ .agent-card.node-done .node-stat { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }
2086
+ .agent-card.node-error .node-stat { color: var(--err); background: color-mix(in srgb, var(--err) 14%, transparent); }
2087
+ /* Description-tab pointer to the Code tab (not the code itself). */
2088
+ .dim-note { color: var(--text-dim); font-size: 12px; margin-top: 8px; }
2089
+
2090
+ /* ── On-trigger (event-driven) routines ─────────────────────────────────────
2091
+ The schedule-vs-trigger chooser, the read-only trigger-source line, the
2092
+ trigger row's left accent, the live "fired" flash, and an sr-only utility.
2093
+ Reuses the existing rtn-* dot palette (listening=ok, blocked=skipped,
2094
+ error=failed, stopped=text-dim) — no new colours. */
2095
+ .rtn-mode { display: flex; gap: 6px; }
2096
+ .rtn-mode-btn { flex: 1; background: var(--surface-2); border: 1px solid var(--border-strong);
2097
+ color: var(--text-muted); border-radius: 8px; padding: 7px 10px; font-size: 12.5px;
2098
+ font-weight: 600; cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s; }
2099
+ .rtn-mode-btn:hover { color: var(--text); border-color: var(--accent-dim); }
2100
+ .rtn-mode-btn.active { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }
2101
+ .rtn-mode-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2102
+ .rtn-mode-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
2103
+ .rtn-trigger-info { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px;
2104
+ border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
2105
+ .rtn-trigger-src { color: var(--accent); font-family: var(--mono); font-size: 12px; }
2106
+ .rtn-trigger-note { font-size: 11.5px; color: var(--text-dim); }
2107
+ .rtn-item.trigger { border-left: 2px solid var(--accent-dim); }
2108
+ @keyframes rtn-fired { 0% { background: color-mix(in srgb, var(--ok) 20%, var(--surface-2)); } 100% { background: var(--surface-2); } }
2109
+ .rtn-fired-flash { animation: rtn-fired 1.2s ease-out; }
2110
+ @media (prefers-reduced-motion: reduce) { .rtn-fired-flash { animation: none; } }
2111
+ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
2112
+ clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
2113
+
2114
+ /* ===== Tweak modal: screenshot attach ===== */
2115
+ .fm-file-input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; }
2116
+ .fm-drop { position: relative; display: flex; align-items: center; justify-content: center; min-height: 52px; padding: 12px; border: 1px dashed var(--border-strong); border-radius: 4px; background: var(--surface-2); color: var(--text-dim); font-size: 11px; text-align: center; line-height: 1.45; cursor: pointer; transition: border-color 0.15s, background 0.15s, color 0.15s; }
2117
+ .fm-drop:hover, .fm-drop:focus-visible, .fm-drop.drag-over { border-color: var(--accent-dim); background: var(--accent-soft); color: var(--text-muted); outline: 2px solid var(--accent); outline-offset: 2px; }
2118
+ .fm-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
2119
+ .fm-thumbs[hidden] { display: none; }
2120
+ .fm-thumb { position: relative; width: 48px; height: 48px; border-radius: 4px; overflow: hidden; border: 1px solid var(--border-strong); background: var(--bg); flex-shrink: 0; }
2121
+ .fm-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
2122
+ .fm-thumb-del { position: absolute; top: 2px; right: 2px; width: 16px; height: 16px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; line-height: 1; background: var(--surface); border: 1px solid var(--border-strong); border-radius: 2px; color: var(--text-muted); cursor: pointer; opacity: 0; transition: opacity 0.12s, color 0.12s, border-color 0.12s; }
2123
+ .fm-thumb:hover .fm-thumb-del, .fm-thumb-del:focus-visible { opacity: 1; }
2124
+ .fm-thumb-del:hover { color: var(--err); border-color: var(--err); }
2125
+ /* ===== Requests: snapshot thumbnails ===== */
2126
+ .req-thumbs { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
2127
+ .req-thumb { width: 46px; height: 46px; padding: 0; border: 1px solid var(--border-strong); border-radius: 4px; background: var(--bg); overflow: hidden; cursor: pointer; flex-shrink: 0; transition: border-color 0.15s; }
2128
+ .req-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
2129
+ .req-thumb:hover { border-color: var(--accent-dim); }