@worca/ui 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/styles.css CHANGED
@@ -1150,6 +1150,18 @@ sl-input [slot="prefix"] {
1150
1150
  color: var(--status-cancelled);
1151
1151
  }
1152
1152
 
1153
+ .status-halted {
1154
+ color: var(--status-paused);
1155
+ }
1156
+
1157
+ .status-setup-failed {
1158
+ color: var(--status-failed);
1159
+ }
1160
+
1161
+ .status-unrecoverable {
1162
+ color: var(--status-failed);
1163
+ }
1164
+
1153
1165
  /* --- 18b. Control Buttons --- */
1154
1166
  .action-btn--amber {
1155
1167
  border-color: var(--status-paused);
@@ -1171,6 +1183,19 @@ sl-input [slot="prefix"] {
1171
1183
  color: #ffffff;
1172
1184
  }
1173
1185
 
1186
+ .action-btn--teal {
1187
+ /* "Re-run" / "iterate again" actions — distinct from primary blue
1188
+ (resume) and status palette colors so it reads as a separate
1189
+ intent: launch a fresh attempt from this one. */
1190
+ border-color: #0891b2;
1191
+ color: #0891b2;
1192
+ }
1193
+
1194
+ .action-btn--teal:hover {
1195
+ background: #0891b2;
1196
+ color: #ffffff;
1197
+ }
1198
+
1174
1199
  .action-btn--red {
1175
1200
  border-color: var(--status-failed);
1176
1201
  color: var(--status-failed);
@@ -1302,11 +1327,142 @@ sl-input [slot="prefix"] {
1302
1327
 
1303
1328
  /* Status-colored left border accent on run cards */
1304
1329
  .run-card.status-running { border-left: 3px solid var(--status-running); }
1330
+ .run-card.status-resuming { border-left: 3px solid var(--status-running); }
1305
1331
  .run-card.status-paused { border-left: 3px solid var(--status-paused); }
1332
+ .run-card.status-interrupted { border-left: 3px solid var(--status-interrupted); }
1306
1333
  .run-card.status-completed { border-left: 3px solid var(--status-completed); }
1307
1334
  .run-card.status-failed { border-left: 3px solid var(--status-failed); }
1335
+ .run-card.status-error { border-left: 3px solid var(--status-error); }
1308
1336
  .run-card.status-pending { border-left: 3px solid var(--status-pending); }
1337
+ .run-card.status-skipped { border-left: 3px solid var(--status-pending); }
1309
1338
  .run-card.status-cancelled { border-left: 3px solid var(--status-cancelled); }
1339
+ .run-card.status-halted { border-left: 3px solid var(--status-paused); }
1340
+ .run-card.status-setup-failed { border-left: 3px solid var(--status-failed); }
1341
+ .run-card.status-unrecoverable { border-left: 3px solid var(--status-failed); }
1342
+
1343
+ /* ── Fleet card (shared component for Dashboard + Fleet list) ───────────
1344
+ The fleet card reuses the `.run-card` base class and its shared
1345
+ structural sub-elements (`.run-card-top`, `.run-card-status`,
1346
+ `.run-card-title`, `.run-card-meta`, `.run-card-meta-item`,
1347
+ `.run-card-actions`) — exactly like `.worktree-card` does. The
1348
+ `.fleet-card` / `.fleet-card-*` rules below are only the bits that are
1349
+ genuinely fleet-specific:
1350
+ - a layered "stack of cards" silhouette (::before / ::after layers)
1351
+ - the "Projects:" name-badge row
1352
+ - the failed-count pill + exception pills
1353
+ The box, hover, and status-accent styling all come from `.run-card`. */
1354
+
1355
+ /* Stack-of-cards illusion: two thin layers above-right behind the main card,
1356
+ each progressively offset and dimmed. Pure decoration — pointer-events
1357
+ off so they never steal clicks. */
1358
+ .fleet-card-stack {
1359
+ position: relative;
1360
+ isolation: isolate;
1361
+ }
1362
+ .fleet-card-stack::before,
1363
+ .fleet-card-stack::after {
1364
+ content: '';
1365
+ position: absolute;
1366
+ inset: 0;
1367
+ border: 1px solid var(--border-subtle);
1368
+ border-radius: var(--radius-lg);
1369
+ background: var(--surface);
1370
+ pointer-events: none;
1371
+ z-index: -1;
1372
+ }
1373
+ .fleet-card-stack::before {
1374
+ transform: translate(4px, -4px);
1375
+ opacity: 0.55;
1376
+ }
1377
+ .fleet-card-stack::after {
1378
+ transform: translate(8px, -8px);
1379
+ opacity: 0.3;
1380
+ }
1381
+
1382
+ /* The status-coloured left edge comes from `.run-card.status-*` (the
1383
+ fleet card carries the `run-card` base class). The one fleet-specific
1384
+ override: a user-initiated halt reads as informational grey, matching
1385
+ its `neutral` status-badge variant — per the badge-color-language
1386
+ guide §CSS Variables. Circuit-breaker / targets-not-ready halts keep
1387
+ the inherited orange. */
1388
+ .fleet-card.status-halted[data-halt-reason="user"] {
1389
+ border-left-color: var(--status-pending);
1390
+ }
1391
+
1392
+ /* Project name badges — the value of the "Projects:" meta label. Neutral
1393
+ (no status color), each clamped to ~20ch with an ellipsis; the JS also
1394
+ pre-truncates so the title attr carries the full name. Up to 3 render
1395
+ inline, the rest collapse into `.fleet-card-project-more`. */
1396
+ .fleet-card-project-badge {
1397
+ display: inline-block;
1398
+ max-width: 20ch;
1399
+ overflow: hidden;
1400
+ text-overflow: ellipsis;
1401
+ white-space: nowrap;
1402
+ vertical-align: bottom;
1403
+ padding: 1px 8px;
1404
+ font-size: 11px;
1405
+ color: var(--fg);
1406
+ background: var(--bg-tertiary);
1407
+ border: 1px solid var(--border-subtle);
1408
+ border-radius: 999px;
1409
+ flex-shrink: 0;
1410
+ }
1411
+ .fleet-card-project-more {
1412
+ font-size: 11px;
1413
+ color: var(--muted);
1414
+ flex-shrink: 0;
1415
+ }
1416
+ /* Failed-project count — rides the "Projects:" row after the name badges
1417
+ as a solid red badge (e.g. "1 failed"). Only rendered when at least one
1418
+ project failed; callers guard with `failedCount > 0`. */
1419
+ .fleet-card-failed-count {
1420
+ display: inline-block;
1421
+ flex-shrink: 0;
1422
+ padding: 1px 8px;
1423
+ font-size: 11px;
1424
+ font-weight: 500;
1425
+ color: #ffffff;
1426
+ background: var(--status-error);
1427
+ border-radius: 999px;
1428
+ }
1429
+
1430
+ /* Exception counter pills — surface non-zero failure / halted / paused
1431
+ buckets when they don't already match the fleet's overall status badge.
1432
+ Variants follow the badge-color-language guide: `danger` for failures,
1433
+ `warning` for halts/pauses. Replaces the prior right-edge accent stripe
1434
+ (which was too subtle to be actionable). */
1435
+ .fleet-card-exception-pill {
1436
+ flex-shrink: 0;
1437
+ }
1438
+
1439
+ .fleet-card-mono {
1440
+ font-family: var(--font-mono, ui-monospace, monospace);
1441
+ font-size: 11px;
1442
+ color: var(--fg);
1443
+ }
1444
+
1445
+ .fleet-card-children-label {
1446
+ align-self: center;
1447
+ flex-shrink: 0;
1448
+ font-size: 12px;
1449
+ /* Color/weight inherited from `.meta-label` so this row matches the
1450
+ Plan/Base/Started/Duration label vocabulary on the rows below. */
1451
+ }
1452
+ .fleet-card-children-empty {
1453
+ font-size: 12px;
1454
+ color: var(--muted);
1455
+ font-style: italic;
1456
+ }
1457
+
1458
+ /* "Projects:" label + name-badge list row. */
1459
+ .fleet-card-progress {
1460
+ display: flex;
1461
+ align-items: center;
1462
+ flex-wrap: wrap;
1463
+ gap: 6px;
1464
+ padding-left: 26px;
1465
+ }
1310
1466
 
1311
1467
  /* --- 21. Dashboard --- */
1312
1468
  .dashboard {
@@ -2340,7 +2496,39 @@ sl-details.live-output-panel::part(content) {
2340
2496
  .new-run-advanced {
2341
2497
  display: flex;
2342
2498
  flex-direction: column;
2343
- gap: 16px;
2499
+ gap: 24px;
2500
+ }
2501
+
2502
+ /* Advanced Options is dense — give each field a touch more breathing room
2503
+ between label / control / hint than the default 4px so multi-line
2504
+ sub-controls (head-template preview, plan-mode radio + warning) don't
2505
+ feel stacked. Scoped to .new-run-advanced so the rest of Settings
2506
+ keeps its tighter rhythm. */
2507
+ .new-run-advanced .settings-field {
2508
+ gap: 8px;
2509
+ }
2510
+ .new-run-advanced .settings-field-hint {
2511
+ margin-top: 4px;
2512
+ line-height: 1.5;
2513
+ }
2514
+
2515
+ /* Visual subgroup inside Advanced Options — small caps title + subtle
2516
+ underline divider. Keeps the section card's outer border doing the
2517
+ heavy lifting; the subgroup title is just a quiet "BRANCHES" /
2518
+ "PLANNING" / "CONCURRENCY" label for scanability. */
2519
+ .advanced-subgroup {
2520
+ display: flex;
2521
+ flex-direction: column;
2522
+ gap: 14px;
2523
+ }
2524
+ .advanced-subgroup-title {
2525
+ font-size: 11px;
2526
+ font-weight: 600;
2527
+ color: var(--muted);
2528
+ text-transform: uppercase;
2529
+ letter-spacing: 0.06em;
2530
+ padding-bottom: 6px;
2531
+ border-bottom: 1px dashed var(--border-subtle);
2344
2532
  }
2345
2533
 
2346
2534
  .new-run-grid {
@@ -2484,39 +2672,106 @@ sl-option.template-grouped:focus::part(suffix) {
2484
2672
  background: var(--bg-tertiary);
2485
2673
  }
2486
2674
 
2487
- /* Sidebar new run button */
2675
+ /* Sidebar new run button (split button pattern: primary + chevron).
2676
+ The split button is the W-040 UX choice — primary "New Pipeline"
2677
+ handles the common case; the chevron drops down to a menu of
2678
+ multi-project alternatives (Fleet, plus Workspace when W-047 ships). */
2488
2679
  .sidebar-new-run {
2489
2680
  padding: 8px 8px 4px;
2490
2681
  }
2491
2682
 
2492
- .sidebar-new-run-btn {
2683
+ .sidebar-new-run-split {
2493
2684
  display: flex;
2494
- align-items: center;
2495
- justify-content: center;
2496
- gap: 8px;
2685
+ align-items: stretch;
2497
2686
  width: 100%;
2498
- padding: 8px 12px;
2687
+ min-height: 36px;
2499
2688
  border: 2px dashed var(--accent);
2500
2689
  border-radius: var(--radius);
2690
+ overflow: hidden;
2691
+ background: transparent;
2692
+ transition: background var(--transition-fast), border-color var(--transition-fast);
2693
+ }
2694
+
2695
+ .sidebar-new-run-btn-primary {
2696
+ flex: 1 1 auto;
2697
+ display: flex;
2698
+ align-items: center;
2699
+ justify-content: center;
2700
+ gap: 8px;
2701
+ min-width: 0;
2702
+ padding: 6px 12px;
2703
+ border: 0;
2704
+ border-right: 2px dashed var(--accent);
2501
2705
  background: transparent;
2502
2706
  color: var(--accent);
2503
2707
  font-size: 13px;
2504
2708
  font-weight: 600;
2505
2709
  font-family: inherit;
2506
2710
  cursor: pointer;
2711
+ transition: background var(--transition-fast), color var(--transition-fast),
2712
+ border-color var(--transition-fast);
2713
+ }
2714
+
2715
+ .sidebar-new-run-btn-primary:hover:not(:disabled) {
2716
+ background: var(--accent);
2717
+ color: #ffffff;
2718
+ }
2719
+
2720
+ /* Disabled state: muted (not ghosted) so it reads as "waiting on something"
2721
+ rather than a broken control. Pairs with the title tooltip explaining
2722
+ why it's inactive (e.g., "Select a project first…"). */
2723
+ .sidebar-new-run-btn-primary:disabled {
2724
+ cursor: not-allowed;
2725
+ color: var(--muted);
2726
+ border-right-color: var(--muted);
2727
+ }
2728
+
2729
+ /* sl-dropdown defaults to inline-block; force it to fill the cross-axis
2730
+ so the slotted chevron button can stretch to the full split height. */
2731
+ .sidebar-new-run-chevron-dropdown {
2732
+ display: flex;
2733
+ flex: 0 0 auto;
2734
+ align-self: stretch;
2735
+ }
2736
+
2737
+ .sidebar-new-run-btn-chevron {
2738
+ display: flex;
2739
+ align-items: center;
2740
+ justify-content: center;
2741
+ align-self: stretch;
2742
+ height: 100%;
2743
+ min-height: 32px;
2744
+ padding: 0 12px;
2745
+ border: 0;
2746
+ background: transparent;
2747
+ color: var(--accent);
2748
+ cursor: pointer;
2507
2749
  transition: background var(--transition-fast), color var(--transition-fast);
2508
2750
  }
2509
2751
 
2510
- .sidebar-new-run-btn:hover {
2752
+ .sidebar-new-run-btn-chevron:hover:not(:disabled) {
2511
2753
  background: var(--accent);
2512
2754
  color: #ffffff;
2513
2755
  }
2514
2756
 
2515
- .sidebar.collapsed .sidebar-new-run-btn span {
2757
+ .sidebar-new-run-btn-chevron:disabled {
2758
+ cursor: not-allowed;
2759
+ color: var(--muted);
2760
+ }
2761
+
2762
+ /* Menu items: icon + label, slight indent under their icon */
2763
+ .sidebar-new-run-chevron-dropdown sl-menu-item::part(base) {
2764
+ display: flex;
2765
+ align-items: center;
2766
+ gap: 8px;
2767
+ font-size: 13px;
2768
+ }
2769
+
2770
+ .sidebar.collapsed .sidebar-new-run-btn-primary span {
2516
2771
  display: none;
2517
2772
  }
2518
2773
 
2519
- .sidebar.collapsed .sidebar-new-run-btn {
2774
+ .sidebar.collapsed .sidebar-new-run-btn-primary {
2520
2775
  padding: 8px;
2521
2776
  }
2522
2777
 
@@ -2906,6 +3161,16 @@ sl-details.run-beads-panel::part(content) {
2906
3161
  border: 1px solid var(--border-subtle);
2907
3162
  }
2908
3163
 
3164
+ /* Default badge layout: when a badge contains an icon + text (e.g. the
3165
+ pipeline-page header status badge "<icon> Completed"), the children
3166
+ sit flush against each other. Give them a small gap. Single-child
3167
+ badges are unaffected because flex+gap only manifests between siblings. */
3168
+ sl-badge::part(base) {
3169
+ display: inline-flex;
3170
+ align-items: center;
3171
+ gap: 4px;
3172
+ }
3173
+
2909
3174
  .run-bead-row sl-badge::part(base) {
2910
3175
  font-size: 0.68rem;
2911
3176
  padding: 0 5px;
@@ -3960,6 +4225,7 @@ sl-details.learnings-panel::part(content) {
3960
4225
  .filter-chip-failed.active { background: var(--status-failed); border-color: var(--status-failed); }
3961
4226
  .filter-chip-paused.active { background: var(--status-paused); border-color: var(--status-paused); }
3962
4227
  .filter-chip-error.active { background: var(--status-failed); border-color: var(--status-failed); }
4228
+ .filter-chip-halted.active { background: var(--status-paused); border-color: var(--status-paused); }
3963
4229
  .filter-chip .chip-count {
3964
4230
  font-size: 0.68rem;
3965
4231
  opacity: 0.7;
@@ -3970,6 +4236,7 @@ sl-details.learnings-panel::part(content) {
3970
4236
  display: flex;
3971
4237
  align-items: center;
3972
4238
  justify-content: flex-end;
4239
+ flex-wrap: wrap;
3973
4240
  gap: 8px;
3974
4241
  padding-left: 26px;
3975
4242
  }
@@ -4036,11 +4303,8 @@ sl-details.learnings-panel::part(content) {
4036
4303
  .project-status-paused { background: var(--status-paused, #eab308); }
4037
4304
 
4038
4305
  /* --- Dashboard Project Cards --- */
4039
- .project-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 24px; }
4040
- .project-card { background: var(--surface); border-radius: var(--radius); padding: 16px; cursor: pointer; transition: var(--transition-fast); border: 1px solid var(--border); }
4041
- .project-card:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
4042
- .project-card-name { font-weight: 600; margin-bottom: 4px; }
4043
- .project-card-stats { font-size: 0.85rem; color: var(--text-secondary); }
4306
+ /* .project-cards / .project-card-* removed see app/views/dashboard.js
4307
+ for the rationale (sidebar dropdown replaces them). */
4044
4308
 
4045
4309
  /* --- Settings Projects List --- */
4046
4310
  .projects-list-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
@@ -4639,6 +4903,23 @@ sl-tooltip.bead-tooltip::part(body) {
4639
4903
  flex: 1;
4640
4904
  min-width: 240px;
4641
4905
  }
4906
+
4907
+ /* Shared text-filter row — the line under the filter chips on the
4908
+ History and Fleets list pages. Mirrors `.worktrees-toolbar` so the
4909
+ three list pages present an identical filter affordance. The
4910
+ worktrees page keeps its own toolbar (it carries the extra
4911
+ "Cleanup all completed" button alongside the text input). */
4912
+ .list-filter-row {
4913
+ display: flex;
4914
+ align-items: center;
4915
+ gap: 12px;
4916
+ flex-wrap: wrap;
4917
+ margin-bottom: 12px;
4918
+ }
4919
+ .list-filter-row .list-text-filter {
4920
+ flex: 1;
4921
+ min-width: 240px;
4922
+ }
4642
4923
  .worktree-card-path {
4643
4924
  align-items: baseline;
4644
4925
  }
@@ -4854,3 +5135,576 @@ sl-tooltip.bead-tooltip::part(body) {
4854
5135
  line-height: 1.2;
4855
5136
  flex: 0 0 auto;
4856
5137
  }
5138
+
5139
+ /* ─── Fleet group (W-040 §13.2) ────────────────────────────────────────────── */
5140
+ .fleet-group {
5141
+ margin-bottom: 8px;
5142
+ border: 1px solid var(--border-subtle);
5143
+ border-radius: 8px;
5144
+ overflow: hidden;
5145
+ }
5146
+
5147
+ .fleet-header {
5148
+ display: flex;
5149
+ align-items: center;
5150
+ gap: 10px;
5151
+ padding: 10px 14px;
5152
+ background: var(--bg-secondary);
5153
+ cursor: pointer;
5154
+ user-select: none;
5155
+ }
5156
+
5157
+ .fleet-header:hover {
5158
+ background: var(--bg-tertiary);
5159
+ }
5160
+
5161
+ .fleet-toggle {
5162
+ flex-shrink: 0;
5163
+ color: var(--fg-muted);
5164
+ font-size: 14px;
5165
+ }
5166
+
5167
+ .fleet-title {
5168
+ flex: 0 1 auto;
5169
+ font-size: 13px;
5170
+ font-weight: 600;
5171
+ color: var(--fg);
5172
+ overflow: hidden;
5173
+ text-overflow: ellipsis;
5174
+ white-space: nowrap;
5175
+ }
5176
+
5177
+ .fleet-status-badge {
5178
+ flex-shrink: 0;
5179
+ }
5180
+
5181
+ .fleet-progress {
5182
+ flex-shrink: 0;
5183
+ font-size: 12px;
5184
+ color: var(--fg-muted);
5185
+ }
5186
+
5187
+ .fleet-progress-bar {
5188
+ flex: 1 1 80px;
5189
+ min-width: 60px;
5190
+ max-width: 160px;
5191
+ }
5192
+
5193
+ .fleet-cost {
5194
+ flex-shrink: 0;
5195
+ font-size: 12px;
5196
+ color: var(--fg-muted);
5197
+ }
5198
+
5199
+ .fleet-detail-btn {
5200
+ flex-shrink: 0;
5201
+ margin-left: auto;
5202
+ }
5203
+
5204
+ .fleet-children {
5205
+ padding: 8px;
5206
+ display: flex;
5207
+ flex-direction: column;
5208
+ gap: 8px;
5209
+ background: var(--bg-primary);
5210
+ }
5211
+
5212
+ /* ── Fleet launcher (page) ─────────────────────────────────────────────── */
5213
+
5214
+ .fleet-launcher-page,
5215
+ .fleet-detail-page {
5216
+ max-width: 760px;
5217
+ }
5218
+
5219
+ .fleet-launcher-projects-controls {
5220
+ display: flex;
5221
+ gap: 8px;
5222
+ align-items: center;
5223
+ margin-bottom: 4px;
5224
+ }
5225
+
5226
+ .fleet-launcher-projects-controls .input-project-filter {
5227
+ flex: 1 1 auto;
5228
+ }
5229
+
5230
+ /* Head-template preview rows — render below the input as a small inset
5231
+ panel so the per-project resolved branches feel attached to (not just
5232
+ stacked under) the template field. */
5233
+ .head-template-input {
5234
+ display: flex;
5235
+ flex-direction: column;
5236
+ gap: 8px;
5237
+ }
5238
+ .head-template-preview {
5239
+ display: flex;
5240
+ flex-direction: column;
5241
+ gap: 4px;
5242
+ padding: 8px 12px;
5243
+ background: var(--bg-primary);
5244
+ border: 1px solid var(--border-subtle);
5245
+ border-radius: var(--radius);
5246
+ font-size: 12px;
5247
+ }
5248
+ .head-template-preview-row {
5249
+ display: flex;
5250
+ align-items: center;
5251
+ gap: 8px;
5252
+ font-family: var(--font-mono, ui-monospace, monospace);
5253
+ }
5254
+ .head-template-preview-row .preview-arrow {
5255
+ color: var(--muted);
5256
+ }
5257
+ .head-template-preview-row .preview-branch {
5258
+ color: var(--text);
5259
+ }
5260
+ .head-template-preview-row.collision .preview-branch {
5261
+ color: var(--status-error);
5262
+ }
5263
+ .head-template-preview-row .collision-flag {
5264
+ margin-left: auto;
5265
+ font-size: 11px;
5266
+ color: var(--status-error);
5267
+ text-transform: uppercase;
5268
+ letter-spacing: 0.04em;
5269
+ }
5270
+
5271
+ /* Plan-mode radio container: stack the radios with breathing room and
5272
+ keep the divergence-warning alert visually attached to the chosen
5273
+ option without crowding it. */
5274
+ .plan-mode-radio {
5275
+ display: flex;
5276
+ flex-direction: column;
5277
+ gap: 10px;
5278
+ }
5279
+ .plan-mode-radio sl-radio-group::part(form-control) {
5280
+ display: flex;
5281
+ flex-direction: column;
5282
+ gap: 4px;
5283
+ }
5284
+
5285
+ /* Base-branch validation states */
5286
+ .base-branch-validating {
5287
+ display: flex;
5288
+ align-items: center;
5289
+ gap: 8px;
5290
+ font-size: 12px;
5291
+ color: var(--muted);
5292
+ padding: 4px 0;
5293
+ }
5294
+ .base-branch-error {
5295
+ font-size: 12px;
5296
+ color: var(--status-error);
5297
+ padding: 8px 12px;
5298
+ background: var(--bg-primary);
5299
+ border: 1px solid var(--status-error);
5300
+ border-radius: var(--radius);
5301
+ }
5302
+ .base-branch-missing-list {
5303
+ margin: 4px 0 0;
5304
+ padding-left: 20px;
5305
+ }
5306
+
5307
+ /* ── Fleet detail (page) ────────────────────────────────────────────────── */
5308
+
5309
+ /* The page top mirrors `runDetailView`'s overview: a clickable projects
5310
+ strip (analogue of the stage-timeline pills) sits above a single
5311
+ `.run-info-section` panel that holds the status badge + id chip + flat
5312
+ meta rows. No hero card — the per-project run-cards below carry the
5313
+ "card" affordance now. */
5314
+ .fleet-detail-overview {
5315
+ display: flex;
5316
+ flex-direction: column;
5317
+ gap: 8px;
5318
+ }
5319
+
5320
+ .fleet-projects-strip {
5321
+ display: flex;
5322
+ gap: 6px;
5323
+ width: 100%;
5324
+ }
5325
+ .fleet-projects-strip-segment {
5326
+ flex: 1 1 0;
5327
+ min-width: 0;
5328
+ display: inline-flex;
5329
+ align-items: center;
5330
+ justify-content: center;
5331
+ gap: 6px;
5332
+ padding: 8px 10px;
5333
+ border: 1px solid var(--border-subtle);
5334
+ border-left-width: 3px;
5335
+ border-radius: var(--radius-md);
5336
+ background: var(--bg-secondary);
5337
+ color: var(--fg);
5338
+ font-size: 12px;
5339
+ font-weight: 500;
5340
+ cursor: pointer;
5341
+ transition: background 120ms ease, transform 120ms ease;
5342
+ overflow: hidden;
5343
+ }
5344
+ .fleet-projects-strip-segment:hover {
5345
+ background: var(--bg-hover);
5346
+ }
5347
+ .fleet-projects-strip-segment:disabled {
5348
+ cursor: default;
5349
+ opacity: 0.75;
5350
+ }
5351
+ .fleet-projects-strip-segment.status-running { border-left-color: var(--status-running); }
5352
+ .fleet-projects-strip-segment.status-resuming { border-left-color: var(--status-running); }
5353
+ .fleet-projects-strip-segment.status-paused { border-left-color: var(--status-paused); }
5354
+ .fleet-projects-strip-segment.status-interrupted { border-left-color: var(--status-interrupted); }
5355
+ .fleet-projects-strip-segment.status-completed { border-left-color: var(--status-completed); }
5356
+ .fleet-projects-strip-segment.status-failed,
5357
+ .fleet-projects-strip-segment.status-setup-failed,
5358
+ .fleet-projects-strip-segment.status-unrecoverable { border-left-color: var(--status-failed); }
5359
+ .fleet-projects-strip-segment.status-error { border-left-color: var(--status-error); }
5360
+ .fleet-projects-strip-segment.status-halted { border-left-color: var(--status-paused); }
5361
+ .fleet-projects-strip-segment.status-pending,
5362
+ .fleet-projects-strip-segment.status-skipped,
5363
+ .fleet-projects-strip-segment.status-cancelled { border-left-color: var(--status-pending); }
5364
+ .fleet-projects-strip-icon {
5365
+ display: inline-flex;
5366
+ align-items: center;
5367
+ flex-shrink: 0;
5368
+ }
5369
+ .fleet-projects-strip-label {
5370
+ overflow: hidden;
5371
+ text-overflow: ellipsis;
5372
+ white-space: nowrap;
5373
+ min-width: 0;
5374
+ }
5375
+
5376
+ .fleet-info-section {
5377
+ /* Inherits .run-info-section padding/background — adds a couple of fleet
5378
+ -specific layout rules for the status row. */
5379
+ }
5380
+ .fleet-overview-status-row {
5381
+ display: flex;
5382
+ align-items: center;
5383
+ gap: 10px;
5384
+ flex-wrap: wrap;
5385
+ /* Match `.fleet-meta-line` so the "Fleet ID:" label renders in the
5386
+ same vocabulary as the Projects / Base / Started / Cost labels. */
5387
+ font-size: 13px;
5388
+ color: var(--fg);
5389
+ }
5390
+ .fleet-meta-line {
5391
+ display: flex;
5392
+ flex-wrap: wrap;
5393
+ gap: 4px 20px;
5394
+ font-size: 13px;
5395
+ color: var(--fg);
5396
+ }
5397
+ .fleet-meta-item {
5398
+ display: inline-flex;
5399
+ align-items: center;
5400
+ gap: 6px;
5401
+ }
5402
+ /* Projects row in the hero — label + name badges, same vocabulary as the
5403
+ fleet card's "Projects:" row. Tighter gap than the default meta line so
5404
+ the badges read as a contiguous list. */
5405
+ .fleet-meta-line-projects {
5406
+ align-items: center;
5407
+ gap: 6px;
5408
+ }
5409
+ .fleet-cost-strip {
5410
+ /* Reuses .pipeline-cost-strip — no overrides needed but keep the class
5411
+ so future tweaks can target fleet-specific copy. */
5412
+ }
5413
+
5414
+ /* Anchor wrapper around each project run-card so the projects strip can
5415
+ scroll to a specific card on click. */
5416
+ .fleet-project-anchor {
5417
+ scroll-margin-top: 80px;
5418
+ }
5419
+
5420
+ .fleet-detail-status-row {
5421
+ display: flex;
5422
+ align-items: center;
5423
+ gap: 12px;
5424
+ padding: 12px 16px;
5425
+ border: 1px solid var(--border-subtle);
5426
+ border-radius: var(--radius-lg);
5427
+ background: var(--bg-secondary);
5428
+ }
5429
+
5430
+ .fleet-detail-status-row .fleet-status-badge::part(base) {
5431
+ font-size: 12px;
5432
+ padding: 4px 10px;
5433
+ }
5434
+
5435
+ .fleet-id-chip {
5436
+ font-family: var(--sl-font-mono);
5437
+ font-size: 12px;
5438
+ color: var(--muted);
5439
+ padding: 2px 8px;
5440
+ background: var(--bg-tertiary);
5441
+ border-radius: 4px;
5442
+ border: 1px solid var(--border-subtle);
5443
+ }
5444
+
5445
+ .fleet-detail-body {
5446
+ margin-top: 4px;
5447
+ }
5448
+
5449
+ .fleet-manifest-grid {
5450
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
5451
+ }
5452
+
5453
+ .fleet-meta-mono {
5454
+ font-family: var(--sl-font-mono);
5455
+ font-size: 12.5px;
5456
+ color: var(--fg);
5457
+ background: var(--bg-tertiary);
5458
+ padding: 2px 6px;
5459
+ border-radius: 4px;
5460
+ display: inline-block;
5461
+ word-break: break-all;
5462
+ }
5463
+
5464
+ .fleet-meta-value {
5465
+ font-size: 13px;
5466
+ color: var(--fg);
5467
+ font-weight: 500;
5468
+ }
5469
+
5470
+ .fleet-wr-title {
5471
+ font-size: 16px;
5472
+ font-weight: 600;
5473
+ color: var(--fg);
5474
+ }
5475
+
5476
+ .fleet-wr-description {
5477
+ margin: 0;
5478
+ font-size: 13px;
5479
+ line-height: 1.55;
5480
+ color: var(--fg);
5481
+ white-space: pre-wrap;
5482
+ }
5483
+
5484
+ .fleet-guide-files {
5485
+ display: flex;
5486
+ flex-wrap: wrap;
5487
+ gap: 6px;
5488
+ margin-bottom: 4px;
5489
+ }
5490
+
5491
+ .fleet-guide-tag::part(base) {
5492
+ font-family: var(--sl-font-mono);
5493
+ }
5494
+
5495
+ .fleet-guide-actions {
5496
+ border-top: none;
5497
+ padding-top: 4px;
5498
+ }
5499
+
5500
+ .fleet-children-header {
5501
+ display: flex;
5502
+ align-items: baseline;
5503
+ justify-content: space-between;
5504
+ gap: 12px;
5505
+ }
5506
+
5507
+ .fleet-children-list {
5508
+ margin-top: 4px;
5509
+ }
5510
+
5511
+ .fleet-child-card {
5512
+ border-left-width: 3px;
5513
+ }
5514
+
5515
+ .fleet-child-card .run-card-meta code.fleet-meta-mono {
5516
+ background: transparent;
5517
+ padding: 0;
5518
+ font-size: 12px;
5519
+ }
5520
+
5521
+ .fleet-aggregate-cost-section .fleet-total-cost {
5522
+ font-size: 22px;
5523
+ font-weight: 600;
5524
+ color: var(--fg);
5525
+ font-variant-numeric: tabular-nums;
5526
+ }
5527
+
5528
+ .fleet-circuit-breaker-alert,
5529
+ .fleet-user-halt-alert {
5530
+ display: block;
5531
+ }
5532
+
5533
+ .fleet-circuit-breaker-alert .cb-trip-reason,
5534
+ .fleet-circuit-breaker-alert .cb-unstarted-count {
5535
+ display: block;
5536
+ margin-top: 4px;
5537
+ font-size: 12.5px;
5538
+ color: var(--fg);
5539
+ opacity: 0.85;
5540
+ }
5541
+
5542
+ .fleet-actions {
5543
+ display: flex;
5544
+ flex-wrap: wrap;
5545
+ gap: 10px;
5546
+ align-items: center;
5547
+ }
5548
+
5549
+ /* Run-detail "Fleet:" / "Workspace:" group line above the Branch row.
5550
+ Mirrors .run-branch exactly: same font-size, gap, and value weight.
5551
+ The only visual difference is the accent-colored anchor + underline-
5552
+ on-hover affordance signalling it's clickable. */
5553
+ .run-group {
5554
+ display: flex;
5555
+ align-items: center;
5556
+ gap: 6px;
5557
+ font-size: 13px;
5558
+ }
5559
+
5560
+ .run-group-link {
5561
+ /* Inherits font-family + font-size from .run-group → matches the plain
5562
+ <span class="meta-value"> on the Branch line, just colored and
5563
+ underlined-on-hover to read as a link. */
5564
+ color: var(--accent);
5565
+ font-weight: 600;
5566
+ text-decoration: none;
5567
+ word-break: break-all;
5568
+ }
5569
+
5570
+ .run-group-link:hover {
5571
+ text-decoration: underline;
5572
+ }
5573
+
5574
+ /* Run-card "Fleet:" / "Workspace:" meta-item — sized by parent
5575
+ .run-card-meta (12px). Same coloring as .run-group-link but no
5576
+ monospace. */
5577
+ .run-card-group-link {
5578
+ color: var(--accent);
5579
+ font-weight: 600;
5580
+ text-decoration: none;
5581
+ word-break: break-all;
5582
+ }
5583
+
5584
+ .run-card-group-link:hover {
5585
+ text-decoration: underline;
5586
+ }
5587
+
5588
+ /* Sidebar fleet-header clickable feedback */
5589
+ .fleet-header-clickable {
5590
+ transition: background-color var(--transition-fast);
5591
+ }
5592
+
5593
+ .fleet-header-clickable:hover {
5594
+ background-color: color-mix(in srgb, var(--accent) 6%, transparent);
5595
+ }
5596
+
5597
+ /* Fleet list view (route: /#/fleet-runs) — one `fleet-card` per fleet.
5598
+ No wrapping border / shared background; each card carries its own
5599
+ chrome plus the layered stack-of-cards shadow. Gap matches the
5600
+ gap between pipeline cards in run-list for visual consistency. */
5601
+ .fleet-list {
5602
+ display: flex;
5603
+ flex-direction: column;
5604
+ gap: 16px;
5605
+ }
5606
+
5607
+ .fleet-list-row {
5608
+ display: flex;
5609
+ align-items: center;
5610
+ gap: 12px;
5611
+ padding: 12px 16px;
5612
+ border-bottom: 1px solid var(--border-subtle);
5613
+ cursor: pointer;
5614
+ transition: background-color var(--transition-fast);
5615
+ }
5616
+
5617
+ .fleet-list-row:last-child {
5618
+ border-bottom: none;
5619
+ }
5620
+
5621
+ .fleet-list-row:hover {
5622
+ background-color: color-mix(in srgb, var(--accent) 6%, transparent);
5623
+ }
5624
+
5625
+ .fleet-list-row-title {
5626
+ font-size: 14px;
5627
+ font-weight: 600;
5628
+ color: var(--fg);
5629
+ flex: 1 1 auto;
5630
+ min-width: 0;
5631
+ overflow: hidden;
5632
+ text-overflow: ellipsis;
5633
+ white-space: nowrap;
5634
+ }
5635
+
5636
+ .fleet-list-row-badge {
5637
+ flex-shrink: 0;
5638
+ }
5639
+
5640
+ .fleet-list-row-meta {
5641
+ font-size: 12px;
5642
+ color: var(--muted);
5643
+ flex-shrink: 0;
5644
+ }
5645
+
5646
+ .fleet-list-row-id {
5647
+ font-family: var(--sl-font-mono);
5648
+ font-size: 11.5px;
5649
+ color: var(--muted);
5650
+ background: var(--bg-tertiary);
5651
+ padding: 2px 8px;
5652
+ border-radius: 4px;
5653
+ flex-shrink: 0;
5654
+ }
5655
+
5656
+ .fleet-list-loading,
5657
+ .fleet-list-empty {
5658
+ padding: 24px;
5659
+ text-align: center;
5660
+ color: var(--muted);
5661
+ display: flex;
5662
+ flex-direction: column;
5663
+ align-items: center;
5664
+ gap: 12px;
5665
+ }
5666
+
5667
+ .fleet-detail-empty {
5668
+ padding: 64px 24px;
5669
+ text-align: center;
5670
+ color: var(--muted);
5671
+ display: flex;
5672
+ flex-direction: column;
5673
+ align-items: center;
5674
+ gap: 12px;
5675
+ max-width: 540px;
5676
+ margin: 0 auto;
5677
+ }
5678
+
5679
+ .fleet-detail-empty sl-icon {
5680
+ font-size: 48px;
5681
+ color: var(--muted);
5682
+ opacity: 0.6;
5683
+ }
5684
+
5685
+ .fleet-detail-empty h2 {
5686
+ margin: 8px 0 0 0;
5687
+ font-size: 18px;
5688
+ color: var(--fg);
5689
+ }
5690
+
5691
+ .fleet-detail-empty p {
5692
+ margin: 0;
5693
+ line-height: 1.5;
5694
+ }
5695
+
5696
+ .fleet-detail-empty code {
5697
+ font-size: 12px;
5698
+ background: var(--bg-2, var(--border));
5699
+ padding: 1px 6px;
5700
+ border-radius: 4px;
5701
+ }
5702
+
5703
+ .fleet-detail-empty-hint {
5704
+ font-size: 13px;
5705
+ opacity: 0.85;
5706
+ }
5707
+
5708
+ .fleet-detail-empty-hint a {
5709
+ color: var(--accent, var(--fg));
5710
+ }