storymapper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/ARCHITECTURE.md +334 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +6 -0
  4. package/README.md +239 -0
  5. package/frontend/css/storymap.css +4637 -0
  6. package/frontend/js/adapters.js +423 -0
  7. package/frontend/js/card-animate.js +384 -0
  8. package/frontend/js/core/graph.js +825 -0
  9. package/frontend/js/core.js +3908 -0
  10. package/frontend/js/dialog-ticket-import.js +506 -0
  11. package/frontend/js/dnd.js +322 -0
  12. package/frontend/js/filter.js +215 -0
  13. package/frontend/js/main.js +2499 -0
  14. package/frontend/js/project-io.js +109 -0
  15. package/frontend/js/query-autocomplete.js +196 -0
  16. package/frontend/js/query-engine.js +339 -0
  17. package/frontend/js/query.js +280 -0
  18. package/frontend/js/renderer-card.js +639 -0
  19. package/frontend/js/renderer-dependencies.js +974 -0
  20. package/frontend/js/renderer-kanban.js +505 -0
  21. package/frontend/js/renderer-storymap.js +2530 -0
  22. package/frontend/js/renderer-ticket-editor.js +455 -0
  23. package/frontend/js/renderer-ticket-modal.js +758 -0
  24. package/frontend/js/save-pipeline.js +170 -0
  25. package/frontend/js/smartbar-autocomplete.js +162 -0
  26. package/frontend/js/store.js +197 -0
  27. package/frontend/js/ticket-editor-boot.js +24 -0
  28. package/frontend/js/ticket-form.js +2095 -0
  29. package/frontend/js/ticket-import.js +477 -0
  30. package/frontend/js/ui-shell.js +441 -0
  31. package/frontend/js/view-process-steps.js +233 -0
  32. package/frontend/js/view-requirements.js +361 -0
  33. package/frontend/js/view-settings.js +1864 -0
  34. package/frontend/js/view-table.js +659 -0
  35. package/frontend/js/wheel-pan.js +65 -0
  36. package/frontend/storymap.html +87 -0
  37. package/frontend/ticket-editor.html +29 -0
  38. package/package.json +76 -0
  39. package/server/bus.js +16 -0
  40. package/server/core/graph.js +10 -0
  41. package/server/core.js +10 -0
  42. package/server/identity.js +134 -0
  43. package/server/index.js +283 -0
  44. package/server/ingest.js +212 -0
  45. package/server/mcp.js +2510 -0
  46. package/server/server.js +1599 -0
  47. package/server/slice.js +103 -0
  48. package/server/storage.js +571 -0
  49. package/server/validation.js +225 -0
  50. package/shared/core/graph.js +825 -0
  51. package/shared/core.js +3908 -0
  52. package/shared/project-io.js +109 -0
  53. package/shared/query-autocomplete.js +196 -0
  54. package/shared/query-engine.js +339 -0
  55. package/shared/query.js +280 -0
  56. package/shared/ticket-import.js +477 -0
  57. package/skill/SKILL.md +458 -0
  58. package/skill/reference/anatomy.md +196 -0
  59. package/skill/reference/spec-evolution.md +52 -0
  60. package/skill/reference/tools.md +156 -0
  61. package/skill/reference/workflows.md +203 -0
@@ -0,0 +1,4637 @@
1
+ /* Storymapper — paperlike theme.
2
+ *
3
+ * The CSS tokens and the modal/menu/button geometry are shared with the
4
+ * companion Concept Mapper project so the two tools look identical. Keep the
5
+ * design language in sync when changing anything here.
6
+ */
7
+
8
+ @import url('https://fonts.googleapis.com/css2?family=Libre+Caslon+Text:ital,wght@0,400;0,700;1,400&family=Mona+Sans:wght@400;500;600;700;800;900&display=swap');
9
+
10
+ :root {
11
+ --paper: #F5F1EA;
12
+ --paper-warm: #EDE7DA;
13
+ --ink: #1A1A1A;
14
+ --ink-soft: #717071;
15
+ --ink-faint: #B3B0AD;
16
+ --rule: #DAD7D1;
17
+ --rule-soft: #E8E5DE;
18
+ --accent: #0F5F4F;
19
+ --danger: #B0382B;
20
+ --danger-hover: #8E2C20;
21
+ --selection: rgba(15,95,79,0.22);
22
+ --target: #2E5A52;
23
+ /* SM-257 — Story-Map "backbone spine": the process-step header is a dark
24
+ accent band so it reads distinctly from the warm-paper release band.
25
+ The heavy colour appears once (sticky top); release bands stay quiet. */
26
+ --backbone-bg: #4C8170; /* lighter muted green-slate */
27
+ --backbone-edge: #3C6856; /* darker, bottom rule */
28
+ --backbone-ink: var(--paper); /* light label text */
29
+ --backbone-ink-soft: rgba(245,241,234,0.62); /* grip / empty / secondary */
30
+ --backbone-divider: rgba(245,241,234,0.18); /* column separators */
31
+ --backbone-lift: rgba(245,241,234,0.10); /* hover tint */
32
+ --backbone-lift-strong: rgba(245,241,234,0.20); /* drop-target tint */
33
+ --shadow-lift: 0 2px 4px rgba(26,26,26,0.06), 0 8px 24px rgba(26,26,26,0.06);
34
+ --font-display: 'Libre Caslon Text', Georgia, serif;
35
+ --font-body: 'Mona Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
36
+
37
+ /* Cluster-Palette 1:1 aus cmapper (concept_map.html#CLUSTERS).
38
+ Verwendet für ticket-typ-spezifische Karten-Hintergründe. */
39
+ --cluster-blue-fill: #E8EEF5;
40
+ --cluster-blue-border: #2E4665;
41
+ --cluster-blue-ink: #1A2A40;
42
+ --cluster-green-fill: #E7EDE3;
43
+ --cluster-green-border: #3D5639;
44
+ --cluster-green-ink: #22341F;
45
+ --cluster-amber-fill: #F5ECD8;
46
+ --cluster-amber-border: #7A5A1F;
47
+ --cluster-amber-ink: #3F2F11;
48
+ --cluster-rust-fill: #F1DCD2;
49
+ --cluster-rust-border: #7A3E26;
50
+ --cluster-rust-ink: #3F1E12;
51
+ --cluster-plum-fill: #EAE0EA;
52
+ --cluster-plum-border: #5A3957;
53
+ --cluster-plum-ink: #2E1B2D;
54
+ --cluster-slate-fill: #E5E5E2;
55
+ --cluster-slate-border: #3A3A37;
56
+ --cluster-slate-ink: #1A1A18;
57
+ --cluster-teal-fill: #DCE9E7;
58
+ --cluster-teal-border: #2E5A52;
59
+ --cluster-teal-ink: #152C28;
60
+ }
61
+
62
+ * { box-sizing: border-box; }
63
+ html, body { height: 100%; margin: 0; padding: 0; }
64
+ body {
65
+ background: var(--paper);
66
+ color: var(--ink);
67
+ font-family: var(--font-body);
68
+ font-size: 13px;
69
+ -webkit-font-smoothing: antialiased;
70
+ /* Global default: deny text selection. The HTML chrome (toolbar, lanes,
71
+ cards, modal headers, drag-handles) shouldn't be selectable — and most
72
+ importantly drag-reorder shouldn't paint a text selection across rows
73
+ while the pointer moves. Elements that DO want selection opt in via
74
+ the whitelist below or via `.selectable`. */
75
+ user-select: none;
76
+ -webkit-user-select: none;
77
+ }
78
+ /* Form controls + things the user authors text into stay selectable.
79
+ `[contenteditable]` covers rich-text fields (none today, future-proof);
80
+ `pre`/`code` cover snippets the user wants to copy out; `.selectable`
81
+ is the explicit opt-in escape hatch for any other content. */
82
+ input,
83
+ textarea,
84
+ select,
85
+ [contenteditable="true"],
86
+ pre,
87
+ code,
88
+ .selectable {
89
+ user-select: text;
90
+ -webkit-user-select: text;
91
+ }
92
+ ::selection { background: var(--accent); color: var(--paper); }
93
+
94
+ /* ---------- App-Header (zwei Toolbar-Zeilen) ----------
95
+ Row 1 (.toolbar-menu): Brand + Menü + aktuelles Projekt (rechts).
96
+ Row 2 (.toolbar-action): Create-Buttons + View-Toggle (rechts).
97
+ cmapper-Pattern: Menüs oben, Aktionen unten, klar getrennt. */
98
+ .app-header {
99
+ display: flex; flex-direction: column;
100
+ background: var(--paper-warm);
101
+ border-bottom: 1px solid var(--rule);
102
+ position: sticky; top: 0; z-index: 10;
103
+ }
104
+ .app-header .toolbar {
105
+ display: flex; align-items: center; gap: 8px;
106
+ padding: 8px 16px;
107
+ }
108
+ .app-header .toolbar-menu { padding-bottom: 4px; }
109
+ .app-header .toolbar-action {
110
+ padding-top: 4px;
111
+ border-top: 1px solid var(--rule);
112
+ }
113
+ /* Current-Project anchor rechts in Row 1 (klickbar, öffnet Load-Dialog). */
114
+ .current-project {
115
+ font-size: 12px;
116
+ color: var(--ink-soft);
117
+ cursor: pointer;
118
+ padding: 3px 10px;
119
+ border: 1px solid transparent;
120
+ border-radius: 4px;
121
+ transition: background 100ms, border-color 100ms;
122
+ }
123
+ .current-project:hover {
124
+ background: var(--paper);
125
+ border-color: var(--rule);
126
+ color: var(--ink);
127
+ }
128
+ .current-project:empty { display: none; }
129
+ /* Brand — 1:1 aus cmapper: <span class="brand"><b>X</b><em>Y</em></span> */
130
+ .brand {
131
+ font-family: var(--font-body);
132
+ font-size: 18px;
133
+ letter-spacing: -0.02em;
134
+ margin-right: 4px;
135
+ color: var(--ink);
136
+ }
137
+ .brand b { color: var(--accent); font-weight: 900; font-style: normal; }
138
+ .brand em { font-style: normal; font-weight: 400; color: var(--ink); }
139
+ .toolbar-spacer { flex: 1; }
140
+ .sep {
141
+ display: inline-block;
142
+ width: 1px; height: 22px;
143
+ background: var(--rule);
144
+ margin: 0 4px;
145
+ }
146
+ .app-toolbar {
147
+ display: inline-flex; align-items: center; gap: 6px;
148
+ }
149
+
150
+ /* Legacy alias for old .toolbar selectors used by other code paths. */
151
+ .toolbar { display: flex; align-items: center; gap: 8px; }
152
+
153
+ /* ---------- Buttons (1:1 cmapper) ---------- */
154
+ .btn {
155
+ display: inline-flex; align-items: center; gap: 6px;
156
+ padding: 6px 11px;
157
+ background: transparent;
158
+ border: 1px solid var(--rule);
159
+ border-radius: 4px;
160
+ color: var(--ink);
161
+ font: inherit; font-size: 12px;
162
+ cursor: pointer;
163
+ transition: background 120ms, border-color 120ms;
164
+ white-space: nowrap;
165
+ }
166
+ .btn:hover:not(:disabled) { background: var(--paper-warm); border-color: var(--ink-faint); }
167
+ .btn:disabled { opacity: 0.35; cursor: default; }
168
+ .btn:disabled:hover { background: transparent; border-color: var(--rule); }
169
+ .btn.active { background: var(--ink); color: var(--paper); border-color: var(--ink); }
170
+ .btn.primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
171
+ .btn.primary:hover:not(:disabled) { background: var(--ink-soft); border-color: var(--ink-soft); }
172
+ .btn.destructive {
173
+ background: var(--danger);
174
+ border-color: var(--danger);
175
+ color: #FFFFFF;
176
+ }
177
+ .btn.destructive:hover:not(:disabled) {
178
+ background: var(--danger-hover);
179
+ border-color: var(--danger-hover);
180
+ }
181
+ /* SM-244: Cancel-ticket action — SOLID amber (filled like the red Delete, but
182
+ warning-yellow not danger-red). Dark ink text reads with high contrast on
183
+ amber (~9:1), clearly more visible than an outline button. */
184
+ .btn.warn {
185
+ background: #F59E0B;
186
+ border-color: #F59E0B;
187
+ color: #1A1A1A;
188
+ font-weight: 600;
189
+ }
190
+ .btn.warn:hover:not(:disabled) {
191
+ background: #D97706;
192
+ border-color: #D97706;
193
+ color: #1A1A1A;
194
+ }
195
+
196
+ /* Native form controls match the paperlike palette */
197
+ select, input[type="text"], input[type="number"], input[type="password"], textarea {
198
+ background: var(--paper);
199
+ color: var(--ink);
200
+ border: 1px solid var(--rule);
201
+ border-radius: 4px;
202
+ padding: 5px 8px;
203
+ font: inherit;
204
+ font-size: 12px;
205
+ }
206
+ select { min-width: 160px; }
207
+ select:focus, input:focus, textarea:focus { outline: none; border-color: var(--ink); }
208
+
209
+ .badge {
210
+ font-size: 11px;
211
+ padding: 2px 8px;
212
+ border: 1px solid var(--rule);
213
+ border-radius: 999px;
214
+ background: var(--paper);
215
+ color: var(--ink-soft);
216
+ letter-spacing: 0.02em;
217
+ }
218
+
219
+ /* Build-Tag neben dem Brand — zeigt git-Commit + Subject als Tooltip.
220
+ Bei jeder Code-Änderung sichtbar im UI, sodass "ich sehe die neue
221
+ Version nicht"-Verwirrungen sich in 1 Sekunde klären lassen. */
222
+ .build-tag {
223
+ font-family: ui-monospace, SFMono-Regular, monospace;
224
+ font-size: 10px;
225
+ color: var(--ink-faint);
226
+ padding: 2px 6px;
227
+ background: var(--rule-soft);
228
+ border-radius: 3px;
229
+ letter-spacing: 0.04em;
230
+ cursor: help;
231
+ user-select: all;
232
+ }
233
+
234
+ /* ---------- Main / View host ---------- */
235
+ /* App-Grid: header (auto) | main (1fr) | statusbar (auto). */
236
+ body {
237
+ display: grid;
238
+ grid-template-rows: auto 1fr auto;
239
+ height: 100vh;
240
+ }
241
+ main {
242
+ display: grid;
243
+ grid-template-columns: 1fr 0;
244
+ transition: grid-template-columns 0.18s ease;
245
+ overflow: hidden;
246
+ }
247
+ main.sidebar-open { grid-template-columns: 1fr 360px; }
248
+ /* Scroll-Container: KEIN padding-top (sticky Backbone klebt direkt an
249
+ die Toolbar) und KEIN seitliches padding — sonst weichen die Spalten
250
+ der Backbone von den Release-Cells ab (das Grid `auto-fit` der beiden
251
+ Container produziert dann unterschiedliche Breiten). Inhalt erhält
252
+ seine Atem-Räume über die jeweiligen inneren Paddings: `.sm-cell`
253
+ intern 10px, Kanban-Board separates Side-Padding für Lanes. */
254
+ #view-host { overflow: auto; padding: 0 0 16px; }
255
+ /* SM-281 (Wiedergänger von SM-280, diesmal mit korrekter Diagnose): macOS-
256
+ Overlay-Scrollbalken erscheinen NUR für die gerade gescrollte Achse. Eine
257
+ Maus mit rein vertikalem Scrollrad erzeugt nie ein horizontales Delta —
258
+ der horizontale Balken taucht also nie auf und ist auch nicht greifbar;
259
+ Process-Steps rechts außerhalb des Viewports sind dann unerreichbar.
260
+ ::-webkit-scrollbar-Styling opt-out't aus dem Overlay → dauerhaft sicht-
261
+ und greifbare klassische Balken (Safari/Chrome). Bewusst schlanker als der
262
+ revertierte SM-280-Wurf (10px, zarter Thumb, transparenter Track), damit
263
+ es nicht "heavy und un-Safari-like" wirkt. Ergänzend übersetzt
264
+ js/wheel-pan.js Shift+Scrollrad in horizontales Scrollen. */
265
+ #view-host::-webkit-scrollbar { width: 10px; height: 10px; }
266
+ #view-host::-webkit-scrollbar-thumb {
267
+ background: rgba(26, 26, 26, 0.22);
268
+ border-radius: 5px;
269
+ border: 2px solid var(--paper);
270
+ }
271
+ #view-host::-webkit-scrollbar-thumb:hover { background: rgba(26, 26, 26, 0.42); }
272
+ #view-host::-webkit-scrollbar-track { background: transparent; }
273
+ #view-host::-webkit-scrollbar-corner { background: transparent; }
274
+ #sidebar-host {
275
+ overflow: auto;
276
+ border-left: 1px solid var(--rule);
277
+ background: var(--paper);
278
+ padding: 16px;
279
+ }
280
+
281
+ /* ---------- Statusbar (am Boden, fixiert) ---------- */
282
+ .statusbar {
283
+ display: flex; align-items: center; gap: 8px;
284
+ padding: 4px 16px;
285
+ border-top: 1px solid var(--rule);
286
+ background: var(--paper-warm);
287
+ font-size: 11px;
288
+ color: var(--ink-soft);
289
+ min-height: 24px;
290
+ }
291
+ .statusbar-spacer { flex: 1; }
292
+
293
+ /* ---------- Empty state ---------- */
294
+ .empty-state {
295
+ display: flex; flex-direction: column;
296
+ align-items: center; justify-content: center;
297
+ padding: 60px 20px;
298
+ color: var(--ink-soft);
299
+ text-align: center;
300
+ }
301
+ .empty-state h2 {
302
+ margin: 0 0 8px;
303
+ font-weight: 600;
304
+ font-size: 14px;
305
+ }
306
+ .empty-state p { margin: 0 0 16px; max-width: 480px; }
307
+
308
+ /* ============================================================
309
+ UI-Shell — Modal, Popover, Menu, Flash (1:1 cmapper)
310
+ ============================================================ */
311
+
312
+ /* Single host that contains at most one modal at a time. */
313
+ #modal-host { position: relative; z-index: 100; }
314
+
315
+ .modal-overlay {
316
+ position: fixed; inset: 0;
317
+ background: rgba(26,26,26,0.35);
318
+ display: flex; align-items: center; justify-content: center;
319
+ z-index: 100;
320
+ }
321
+ .modal {
322
+ background: var(--paper);
323
+ border: 1px solid var(--rule);
324
+ border-radius: 6px;
325
+ box-shadow: var(--shadow-lift);
326
+ padding: 22px 24px;
327
+ width: 580px; max-width: 92vw; max-height: 85vh;
328
+ display: flex; flex-direction: column;
329
+ }
330
+ .modal h2, .modal .modal-title {
331
+ font-family: var(--font-display);
332
+ font-size: 20px; font-weight: 700;
333
+ margin: 0 0 4px 0; letter-spacing: -0.01em;
334
+ color: var(--ink);
335
+ }
336
+ .modal .modal-sub {
337
+ font-size: 12px; color: var(--ink-soft);
338
+ margin-bottom: 14px; line-height: 1.5;
339
+ }
340
+ .modal .modal-body { display: flex; flex-direction: column; }
341
+ .modal textarea {
342
+ flex: 1; min-height: 80px; width: 100%;
343
+ border: 1px solid var(--rule);
344
+ border-radius: 4px;
345
+ padding: 10px 12px;
346
+ background: #FFFFFF;
347
+ font-family: var(--font-body);
348
+ font-size: 11px; color: var(--ink-soft);
349
+ line-height: 1.55;
350
+ letter-spacing: 0.01em;
351
+ resize: vertical;
352
+ }
353
+ .modal textarea:focus { outline: none; border-color: var(--ink); }
354
+ .modal-actions {
355
+ display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px;
356
+ }
357
+
358
+ /* Generic form row used inside modals (label + control). */
359
+ .modal-row {
360
+ display: flex; align-items: center; gap: 8px;
361
+ margin: 8px 0;
362
+ }
363
+ .modal-row label {
364
+ flex: 0 0 140px;
365
+ color: var(--ink-soft);
366
+ font-size: 12px;
367
+ }
368
+ .modal-row input[type="text"],
369
+ .modal-row input[type="number"],
370
+ .modal-row input[type="password"],
371
+ .modal-row select,
372
+ .modal-row textarea {
373
+ flex: 1;
374
+ padding: 5px 8px;
375
+ border: 1px solid var(--rule);
376
+ border-radius: 4px;
377
+ background: var(--paper);
378
+ font: inherit; font-size: 12px;
379
+ color: var(--ink);
380
+ }
381
+ .modal-row textarea { min-height: 60px; resize: vertical; }
382
+ .modal-row .row-hint {
383
+ flex: 0 0 100%;
384
+ margin-left: 148px;
385
+ font-size: 11px; color: var(--ink-faint);
386
+ margin-top: 2px;
387
+ }
388
+
389
+ /* DoR/DoD-Gate-Fehler-Liste in Modals */
390
+ .missing-list {
391
+ list-style: none; margin: 8px 0 0; padding: 8px 12px;
392
+ background: rgba(176,56,43,0.08);
393
+ border-left: 3px solid var(--danger);
394
+ border-radius: 4px;
395
+ font-size: 12px;
396
+ }
397
+ .missing-list li { padding: 2px 0; color: var(--ink); }
398
+ .missing-list li::before { content: "○ "; color: var(--danger); }
399
+
400
+ /* ---------- Context menu (SM-30) ---------- */
401
+ .context-menu {
402
+ position: fixed;
403
+ z-index: 60;
404
+ min-width: 180px;
405
+ background: var(--paper);
406
+ border: 1px solid var(--rule);
407
+ border-radius: 6px;
408
+ box-shadow: var(--shadow-lift);
409
+ padding: 4px 0;
410
+ }
411
+ .context-menu-list { display: flex; flex-direction: column; }
412
+ .context-menu-item {
413
+ appearance: none;
414
+ border: 0;
415
+ background: transparent;
416
+ text-align: left;
417
+ padding: 6px 14px;
418
+ font-size: 12px;
419
+ color: var(--ink);
420
+ cursor: pointer;
421
+ font-family: inherit;
422
+ }
423
+ .context-menu-item:hover { background: var(--accent-soft); color: var(--ink); }
424
+ .context-menu-item-disabled {
425
+ cursor: not-allowed;
426
+ color: var(--ink-soft);
427
+ }
428
+ .context-menu-item-disabled:hover { background: transparent; }
429
+
430
+ /* ---------- Settings popover (anchored) ---------- */
431
+ .settings-popover {
432
+ position: fixed;
433
+ z-index: 50;
434
+ min-width: 240px;
435
+ max-width: min(360px, 92vw);
436
+ padding: 12px 14px;
437
+ background: var(--paper);
438
+ border: 1px solid var(--rule);
439
+ border-radius: 6px;
440
+ box-shadow: var(--shadow-lift);
441
+ display: flex; flex-direction: column; gap: 8px;
442
+ }
443
+ .sp-title {
444
+ font-size: 13px; font-weight: 600;
445
+ color: var(--ink);
446
+ }
447
+ .sp-row {
448
+ display: grid;
449
+ grid-template-columns: 96px 1fr 56px;
450
+ align-items: center;
451
+ gap: 10px;
452
+ font-size: 12px;
453
+ }
454
+ .sp-row label { color: var(--ink-soft); }
455
+ .sp-row input[type="range"] { accent-color: var(--accent); }
456
+ .sp-out {
457
+ font-variant-numeric: tabular-nums;
458
+ text-align: right;
459
+ color: var(--ink);
460
+ }
461
+ .sp-hint { font-size: 11px; color: var(--ink-faint); line-height: 1.45; }
462
+ .sp-footer { display: flex; justify-content: flex-end; }
463
+ .sp-reset {
464
+ background: none; border: none;
465
+ color: var(--accent);
466
+ font: inherit; cursor: pointer;
467
+ padding: 2px 4px;
468
+ }
469
+ .sp-reset:hover { text-decoration: underline; }
470
+
471
+ /* ---------- Menu Bar (1:1 cmapper) ---------- */
472
+ .menu-bar {
473
+ display: inline-flex; align-items: center; gap: 2px;
474
+ }
475
+ .menu-item {
476
+ background: transparent;
477
+ border: 1px solid transparent;
478
+ border-radius: 4px;
479
+ padding: 5px 10px;
480
+ font-family: var(--font-body);
481
+ font-size: 12px;
482
+ color: var(--ink);
483
+ cursor: pointer;
484
+ line-height: 1.2;
485
+ user-select: none;
486
+ transition: background 100ms, border-color 100ms;
487
+ }
488
+ .menu-item:hover { background: var(--paper-warm); border-color: var(--rule); }
489
+ .menu-item.open {
490
+ background: var(--ink); color: var(--paper); border-color: var(--ink);
491
+ }
492
+ .menu-dropdown {
493
+ position: fixed;
494
+ z-index: 70;
495
+ min-width: 220px;
496
+ padding: 4px 0;
497
+ background: var(--paper);
498
+ border: 1px solid var(--rule);
499
+ border-radius: 6px;
500
+ box-shadow: var(--shadow-lift);
501
+ font-family: var(--font-body);
502
+ font-size: 12px;
503
+ color: var(--ink);
504
+ }
505
+ .menu-dropdown-item {
506
+ /* SM-205: [check gutter | label | shortcut] grid so labels share one column
507
+ and the active item's ✓ sits in the gutter (not inline with the text). */
508
+ display: grid;
509
+ grid-template-columns: 11px 1fr auto;
510
+ align-items: center;
511
+ column-gap: 5px;
512
+ padding: 6px 14px 6px 9px;
513
+ cursor: pointer;
514
+ color: var(--ink);
515
+ background: transparent;
516
+ border: none;
517
+ width: 100%;
518
+ text-align: left;
519
+ font: inherit;
520
+ line-height: 1.3;
521
+ }
522
+ .menu-dropdown-item:hover:not(.disabled) { background: var(--paper-warm); }
523
+ .menu-dropdown-item.disabled {
524
+ color: var(--ink-faint);
525
+ cursor: default;
526
+ background: transparent;
527
+ }
528
+ .menu-dropdown-item .menu-check {
529
+ width: 11px; text-align: center; color: var(--accent);
530
+ font-size: 11px; line-height: 1;
531
+ }
532
+ .menu-dropdown-item .menu-label { white-space: nowrap; }
533
+ .menu-dropdown-item .menu-shortcut {
534
+ font-family: var(--font-body);
535
+ font-size: 11px;
536
+ color: var(--ink-faint);
537
+ letter-spacing: 0.04em;
538
+ white-space: nowrap;
539
+ }
540
+ .menu-dropdown-separator {
541
+ height: 1px;
542
+ background: var(--rule);
543
+ margin: 4px 6px;
544
+ }
545
+
546
+ /* ---------- Flash status (bottom-left toast) ---------- */
547
+ .flash {
548
+ position: fixed;
549
+ bottom: 16px; left: 16px;
550
+ background: var(--ink);
551
+ color: var(--paper);
552
+ padding: 6px 12px;
553
+ border-radius: 999px;
554
+ font-size: 12px;
555
+ letter-spacing: 0.01em;
556
+ box-shadow: var(--shadow-lift);
557
+ display: inline-flex;
558
+ align-items: center;
559
+ gap: 8px;
560
+ z-index: 80;
561
+ animation: flashIn 140ms ease-out;
562
+ }
563
+ @keyframes flashIn { from { transform: translateY(8px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
564
+ .flash-error { background: var(--danger); color: #FFFFFF; }
565
+ .flash-ok { background: var(--accent); color: #FFFFFF; }
566
+ .flash-spinner {
567
+ display: inline-block;
568
+ width: 12px; height: 12px;
569
+ border: 2px solid currentColor;
570
+ border-top-color: transparent;
571
+ border-radius: 50%;
572
+ animation: spin 0.8s linear infinite;
573
+ }
574
+ @keyframes spin { to { transform: rotate(360deg); } }
575
+
576
+ /* ============================================================
577
+ Story-Map (E9c) — 3-Ebenen: ProcessSteps × Releases → Epics → Stories
578
+ ============================================================
579
+ CSS-Variablen --sm-* werden vom Renderer aus STORY_MAP_LAYOUT gesetzt,
580
+ damit Werte zentral aus JS gepflegt werden können. */
581
+
582
+ .sm-grid {
583
+ /* SM-257 Safari fix: block flow, NOT flex-column. The children are
584
+ full-width rows that simply stack, so block is visually identical to a
585
+ gap:0 flex column in Chrome — but it removes the "sticky element is a
586
+ flex item" condition, which Safari renders incorrectly (the sticky
587
+ .sm-backbone flex child inside this width:max-content parent silently
588
+ fails to paint / position). A plain sticky block child is the
589
+ best-supported sticky scenario across engines. */
590
+ display: block;
591
+ /* SM-55 follow-up: make the grid container as wide as its widest child
592
+ (the cells-row with explicit pixel grid-columns summing past the
593
+ viewport). Without this, label-rows + the sm-backbone background paint
594
+ only up to the viewport edge — when the user scrolls horizontally to
595
+ reveal off-screen process steps, the background ends where the viewport
596
+ ended, not at the actual column width. `min-width: 100%` keeps the
597
+ grid filling the viewport when content is narrower. */
598
+ width: max-content;
599
+ min-width: 100%;
600
+ --sm-backbone-col-w: 240px;
601
+ }
602
+
603
+ /* ---------- Feature-Backbone (sticky top) ----------
604
+ grid-template-columns wird vom Renderer inline gesetzt: pro PS-Spalte
605
+ eine eigene Breite = calc(multiplier * var(--sm-backbone-col-w)).
606
+ Hier nur ein sinnvoller Fallback, falls Inline-Style fehlt. */
607
+ .sm-backbone {
608
+ display: grid;
609
+ grid-template-columns: repeat(auto-fit, minmax(var(--sm-backbone-col-w), 1fr));
610
+ background: #4C8170; /* literal fallback — paints even if a
611
+ browser stumbles on the custom prop */
612
+ background: var(--backbone-bg);
613
+ border-bottom: 2px solid var(--backbone-edge);
614
+ position: sticky; top: 0; z-index: 2;
615
+ }
616
+ .sm-backbone-col {
617
+ padding: 10px 12px;
618
+ font-size: 13px; font-weight: 600;
619
+ color: var(--backbone-ink);
620
+ letter-spacing: -0.01em;
621
+ border-right: 1px solid var(--backbone-divider);
622
+ text-align: center;
623
+ cursor: grab;
624
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
625
+ transition: background 120ms, box-shadow 120ms;
626
+ }
627
+ .sm-backbone-col:hover { background: var(--backbone-lift); }
628
+ .sm-backbone-col.sm-dragging { opacity: 0.4; cursor: grabbing; }
629
+ .sm-backbone-col.sm-col-drop-target {
630
+ background: var(--backbone-lift-strong);
631
+ box-shadow: inset 3px 0 0 var(--paper);
632
+ }
633
+ .sm-backbone-col.sm-col-shadow {
634
+ opacity: 0.45;
635
+ background: var(--backbone-lift);
636
+ outline: 1px dashed var(--paper);
637
+ outline-offset: -2px;
638
+ }
639
+ .sm-col-grip {
640
+ color: var(--backbone-ink-soft);
641
+ font-size: 11px;
642
+ letter-spacing: -2px;
643
+ cursor: grab;
644
+ user-select: none;
645
+ }
646
+ .sm-col-label { /* keeps label naming explicit */ }
647
+ /* SM-272: process-step column collapse — chevron + narrow collapsed column. */
648
+ .sm-col-chevron {
649
+ appearance: none;
650
+ background: transparent;
651
+ border: none;
652
+ color: var(--backbone-ink-soft);
653
+ cursor: pointer;
654
+ font-size: 9px;
655
+ line-height: 1;
656
+ padding: 2px 1px;
657
+ flex: 0 0 auto;
658
+ }
659
+ .sm-col-chevron:hover { color: var(--backbone-ink); }
660
+ .sm-backbone-col.sm-col-collapsed {
661
+ padding: 10px 3px;
662
+ gap: 2px;
663
+ }
664
+ .sm-backbone-col.sm-col-collapsed .sm-col-grip { display: none; }
665
+ .sm-backbone-col.sm-col-collapsed .sm-col-label {
666
+ font-size: 11px;
667
+ overflow: hidden;
668
+ text-overflow: ellipsis;
669
+ white-space: nowrap;
670
+ max-width: 100%;
671
+ min-width: 0;
672
+ }
673
+ .sm-cell.sm-cell-collapsed {
674
+ display: flex;
675
+ align-items: flex-start;
676
+ justify-content: center;
677
+ padding: 8px 4px;
678
+ }
679
+ .sm-cell-collapsed-count {
680
+ font-size: 11px;
681
+ font-weight: 700;
682
+ color: var(--ink-soft, #717071);
683
+ background: rgba(0, 0, 0, 0.05);
684
+ border-radius: 8px;
685
+ padding: 2px 8px;
686
+ white-space: nowrap;
687
+ }
688
+ .sm-backbone-col.sm-empty {
689
+ font-size: 12px;
690
+ color: var(--backbone-ink-soft);
691
+ font-weight: normal;
692
+ font-style: italic;
693
+ cursor: default;
694
+ }
695
+ .sm-backbone-col.sm-empty:hover { background: transparent; }
696
+
697
+ /* Add-Column-Dropzone am rechten Rand des Backbones — Klick = neuer
698
+ ProcessStep, Drop von Epic = neuer ProcessStep + Epic-Move atomic. */
699
+ .sm-backbone-col.sm-add-col {
700
+ display: flex; align-items: center; justify-content: center; gap: 6px;
701
+ background: transparent;
702
+ color: var(--backbone-ink-soft);
703
+ font-weight: 500;
704
+ font-size: 12px;
705
+ cursor: pointer;
706
+ border-left: 1px dashed var(--backbone-divider);
707
+ border-right: none;
708
+ min-width: 120px;
709
+ transition: background 120ms, color 120ms, box-shadow 120ms;
710
+ }
711
+ .sm-backbone-col.sm-add-col:hover {
712
+ background: var(--backbone-lift);
713
+ color: var(--backbone-ink);
714
+ }
715
+ .sm-backbone-col.sm-add-col .sm-add-icon {
716
+ display: inline-flex; align-items: center; justify-content: center;
717
+ width: 16px; height: 16px;
718
+ background: var(--backbone-lift-strong);
719
+ color: var(--backbone-ink);
720
+ border-radius: 50%;
721
+ font-size: 12px;
722
+ font-weight: 700;
723
+ line-height: 1;
724
+ }
725
+ .sm-backbone-col.sm-add-col:hover .sm-add-icon { background: var(--paper); color: var(--accent); }
726
+ .sm-backbone-col.sm-add-col.sm-drop-target {
727
+ background: var(--backbone-lift-strong);
728
+ color: var(--backbone-ink);
729
+ box-shadow: inset 0 0 0 3px var(--paper);
730
+ }
731
+ .sm-backbone-col.sm-add-col.sm-drop-target .sm-add-icon { background: var(--paper); color: var(--accent); }
732
+
733
+ /* ---------- Release-Cells-Zeile ----------
734
+ grid-template-columns: inline vom Renderer (eine Spalte pro PS, plus
735
+ auto-Filler für den Add-Step-Slot der Backbone). Kein Release-Label-
736
+ Slot mehr — das Label sitzt in der Zeile darunter. */
737
+ .sm-release-row.sm-release-cells {
738
+ display: grid;
739
+ grid-template-columns: repeat(auto-fit, minmax(var(--sm-backbone-col-w), 1fr));
740
+ min-height: 120px;
741
+ background: var(--paper);
742
+ }
743
+ .sm-row-filler {
744
+ background: transparent;
745
+ }
746
+
747
+ /* ---------- Release-Label-Zeile (unter den Cells) ---------- */
748
+ .sm-release-label-row {
749
+ display: flex;
750
+ align-items: center;
751
+ gap: 10px;
752
+ padding: 6px 12px;
753
+ background: var(--paper-warm);
754
+ border-top: 1px solid var(--rule);
755
+ border-bottom: 2px solid var(--rule); /* visuelle Trennung zwischen Releases */
756
+ }
757
+ /* SM-178: the chevron + name + pill cluster. In the Story-Map it sticks to the
758
+ left edge so it stays visible while scrolling horizontally through wide
759
+ process steps (orientation). The Kanban swimlane header uses the same DOM
760
+ but does NOT pin it — the sticky rule is scoped to `.sm-grid`. */
761
+ .sm-release-label-main {
762
+ display: inline-flex;
763
+ align-items: center;
764
+ gap: 10px;
765
+ }
766
+ .sm-grid .sm-release-label-main {
767
+ position: sticky;
768
+ left: 0;
769
+ z-index: 1;
770
+ background: var(--paper-warm);
771
+ /* paint a little to the right so the pinned cluster reads cleanly over the
772
+ row content scrolling underneath it */
773
+ padding-right: 8px;
774
+ border-radius: 0 4px 4px 0;
775
+ }
776
+ .sm-release-label {
777
+ font-size: 13px; font-weight: 600;
778
+ color: var(--ink);
779
+ letter-spacing: -0.01em;
780
+ cursor: pointer;
781
+ padding: 4px 8px;
782
+ border-radius: 4px;
783
+ transition: background 120ms;
784
+ /* SM-277: fixed title column so the progress/pill/Add badges line up in stable
785
+ columns across every release row instead of fluttering with the title
786
+ length. Longer titles truncate with an ellipsis (full name in the tooltip).
787
+ --sm-release-label-w comes from RELEASE_LABEL_WIDTH_PX (set on .sm-grid). */
788
+ flex: 0 0 var(--sm-release-label-w, 300px);
789
+ max-width: var(--sm-release-label-w, 300px);
790
+ box-sizing: border-box;
791
+ white-space: nowrap;
792
+ overflow: hidden;
793
+ text-overflow: ellipsis;
794
+ }
795
+ .sm-release-label:hover { background: var(--paper); }
796
+
797
+ /* SM-80 — Release-Lifecycle visual markers. Completed and cancelled
798
+ releases get a dimmed style + status pill so they're recognisable
799
+ without the filter being active. */
800
+ .sm-release-label[data-status="completed"],
801
+ .sm-release-label[data-status="cancelled"] {
802
+ color: var(--ink-soft);
803
+ text-decoration: line-through;
804
+ text-decoration-color: var(--ink-faint);
805
+ text-decoration-thickness: 1px;
806
+ }
807
+ .sm-release-cells[data-status="completed"],
808
+ .sm-release-cells[data-status="cancelled"] {
809
+ opacity: 0.55;
810
+ filter: saturate(0.7);
811
+ }
812
+ /* SM-83: scope the hover-restore so it does NOT fight the collapsed-cells
813
+ opacity:0 (which would reveal content when the user hovers over the
814
+ collapsed row's box). */
815
+ .sm-release-cells[data-status="completed"]:not(.sm-release-collapsed-cells):hover,
816
+ .sm-release-cells[data-status="cancelled"]:not(.sm-release-collapsed-cells):hover { opacity: 0.85; }
817
+ .sm-release-pill {
818
+ display: inline-block;
819
+ padding: 1px 8px;
820
+ border-radius: 9px;
821
+ font-size: 10px; font-weight: 700;
822
+ text-transform: uppercase; letter-spacing: 0.05em;
823
+ line-height: 16px;
824
+ }
825
+ .sm-release-pill-completed {
826
+ background: var(--accent-soft, rgba(15, 95, 79, 0.16));
827
+ color: var(--accent);
828
+ border: 1px solid var(--accent);
829
+ }
830
+ .sm-release-pill-cancelled {
831
+ background: rgba(180, 60, 60, 0.12);
832
+ color: var(--danger, #B43C3C);
833
+ border: 1px solid var(--danger, #B43C3C);
834
+ }
835
+
836
+ /* SM-239 — release progress badge "X/Y". Neutral by default; turns green
837
+ (accent) when every work item is done. Shared by the Story-Map release
838
+ label rows AND the Kanban swimlane headers (one renderReleaseLabelRow). */
839
+ .sm-release-progress {
840
+ display: inline-block;
841
+ padding: 1px 8px;
842
+ border-radius: 9px;
843
+ font-size: 11px; font-weight: 700;
844
+ line-height: 16px;
845
+ font-variant-numeric: tabular-nums;
846
+ background: var(--ink-soft-bg, rgba(0, 0, 0, 0.06));
847
+ color: var(--ink-soft, #717071);
848
+ border: 1px solid transparent;
849
+ /* SM-277: fixed min-width + centered so "8/9" and "10/10" occupy the same
850
+ slot — the status pill after it doesn't shift when the count changes. */
851
+ min-width: 44px;
852
+ box-sizing: border-box;
853
+ text-align: center;
854
+ }
855
+ .sm-release-progress.complete {
856
+ background: var(--accent-soft, rgba(15, 95, 79, 0.16));
857
+ color: var(--accent);
858
+ border-color: var(--accent);
859
+ }
860
+
861
+ /* SM-83 — Collapsible release rows. Chevron sits at the start of the
862
+ label row; toggling collapses the cells-row (animated via max-height)
863
+ and visually compacts the label row so closed releases take less
864
+ vertical space. */
865
+ .sm-release-chevron {
866
+ appearance: none;
867
+ background: transparent;
868
+ border: 1px solid transparent;
869
+ border-radius: 4px;
870
+ color: var(--ink-soft);
871
+ cursor: pointer;
872
+ /* Larger size — at 12px the ▾/▸ glyphs read as dots. ▼/▶ at 14px is
873
+ unambiguous as a disclosure triangle. */
874
+ font-size: 14px;
875
+ line-height: 1;
876
+ padding: 3px 8px;
877
+ min-width: 24px;
878
+ display: inline-flex;
879
+ align-items: center;
880
+ justify-content: center;
881
+ transition: background 120ms, color 120ms, border-color 120ms;
882
+ }
883
+ .sm-release-chevron:hover {
884
+ background: var(--paper);
885
+ color: var(--ink);
886
+ border-color: var(--rule);
887
+ }
888
+ .sm-release-label-row.sm-release-collapsed {
889
+ padding: 2px 12px; /* tighter vertical when collapsed */
890
+ border-bottom-width: 1px; /* the heavy 2px separator only fits expanded */
891
+ }
892
+ .sm-release-label-row.sm-release-collapsed .sm-release-label {
893
+ font-size: 12px;
894
+ padding: 2px 8px;
895
+ }
896
+ /* Animated collapse: the cells-row stays in the DOM and animates via
897
+ max-height + opacity + padding + border-width. main.js owns the
898
+ inline max-height — it sets scrollHeight → 0 on collapse and
899
+ 0 → scrollHeight on expand, then clears the inline value after
900
+ transitionend so future content growth isn't clipped. We deliberately
901
+ do NOT set max-height in the .sm-release-collapsed-cells class
902
+ (especially not !important) — that would clobber the inline start
903
+ value and Safari would skip the transition entirely. overflow:hidden
904
+ is permanent on the row so the animation looks clean on either edge. */
905
+ .sm-release-cells {
906
+ overflow: hidden;
907
+ transition:
908
+ max-height 240ms ease,
909
+ opacity 200ms ease,
910
+ padding 240ms ease,
911
+ border-top-width 240ms ease,
912
+ border-bottom-width 240ms ease;
913
+ }
914
+ .sm-release-cells.sm-release-collapsed-cells {
915
+ opacity: 0;
916
+ /* .sm-release-row.sm-release-cells has min-height:120px so an empty
917
+ cells-row still shows a sensible drop-target. When collapsed, we
918
+ need to override that — otherwise max-height:0 has no effect and
919
+ the row keeps reserving 120px of empty space. */
920
+ min-height: 0;
921
+ border-top-width: 0;
922
+ border-bottom-width: 0;
923
+ padding-top: 0;
924
+ padding-bottom: 0;
925
+ }
926
+
927
+ /* "+ Add Release" — Ghost-Variant, nur neben dem letzten Release-Label. */
928
+ .sm-add-release-btn {
929
+ display: inline-flex; align-items: center; gap: 4px;
930
+ padding: 4px 10px;
931
+ font: inherit; font-size: 11px;
932
+ color: var(--ink-faint);
933
+ background: transparent;
934
+ border: 1px dashed var(--rule);
935
+ border-radius: 4px;
936
+ cursor: pointer;
937
+ transition: background 120ms, color 120ms, border-color 120ms;
938
+ }
939
+ .sm-add-release-btn:hover {
940
+ background: var(--paper);
941
+ color: var(--ink);
942
+ border-color: var(--ink-faint);
943
+ border-style: solid;
944
+ }
945
+ .sm-add-release-btn .sm-add-icon {
946
+ display: inline-flex; align-items: center; justify-content: center;
947
+ width: 14px; height: 14px;
948
+ background: var(--ink-faint);
949
+ color: var(--paper);
950
+ border-radius: 50%;
951
+ font-size: 11px;
952
+ font-weight: 700;
953
+ line-height: 1;
954
+ }
955
+ .sm-add-release-btn:hover .sm-add-icon { background: var(--accent); }
956
+
957
+ /* Phantom-Row CTA — zeigt nur den +Add-Button, kein Label dahinter. */
958
+ .sm-release-label-row.sm-phantom-label-row {
959
+ border-top: none;
960
+ justify-content: flex-start;
961
+ }
962
+
963
+ /* ---------- Cell pro (Release × ProcessStep) ---------- */
964
+ /* SM-55 follow-up: cell padding shrunk 10 → 4px each side so the cell's
965
+ content area equals exactly BACKBONE_COL_WIDTH_PX − 2×4 = 240px =
966
+ STORY_CARD_WIDTH_PX. Means a 1-sub-col Epic-card fits flush into a
967
+ 1-sub-col cell with 4px breathing room on each side. */
968
+ .sm-cell {
969
+ background: var(--paper);
970
+ padding: 4px;
971
+ border-right: 1px solid var(--rule);
972
+ display: flex; flex-direction: column; gap: 8px;
973
+ /* SM-136: absorb any sub-pixel horizontal overflow of the column-row. Use
974
+ `clip`, NOT `auto`: `auto` makes every cell a horizontal SCROLL container,
975
+ so a 1px sub-pixel overflow is enough for Safari to route a horizontal
976
+ trackpad swipe INTO the cell instead of chaining it to #view-host — the map
977
+ then can't be scrolled horizontally to reach off-screen process steps
978
+ (SM-281). `clip` absorbs the same overflow without becoming a scroll
979
+ container, so the gesture reaches the map. */
980
+ overflow-x: clip;
981
+ min-height: 160px;
982
+ transition: background-color 120ms, box-shadow 120ms;
983
+ position: relative;
984
+ box-shadow: inset 0 0 0 1px transparent;
985
+ }
986
+ /* SM-223 (a), gated (SM-274 follow-up): skip layout/paint for cells below the
987
+ fold — ONLY on very large boards (renderInto adds .sm-grid-virtualized past
988
+ VIRTUALIZE_TICKET_THRESHOLD). `auto` keeps the last-rendered size once a cell
989
+ has been on screen; the fallback estimate (backbone column width ×
990
+ min-height-ish) only matters before first paint. FLIP rect-capture is
991
+ no-op-safe for 0-rects (E24) and drag hit-testing only targets on-screen
992
+ cells. Gated because content-visibility makes WebKit/Gecko estimate
993
+ off-screen cell heights and only settle the grid row height when the cell
994
+ scrolls into view — a visible height jump on normal boards. */
995
+ .sm-grid-virtualized .sm-cell {
996
+ content-visibility: auto;
997
+ contain-intrinsic-size: auto var(--sm-backbone-col-w, 248px) auto 300px;
998
+ }
999
+ .sm-cell:nth-of-type(odd) { background: var(--paper); }
1000
+ .sm-cell:nth-of-type(even) { background: #FBF6E8; } /* spürbar wärmere Streifen */
1001
+ .sm-cell:last-child { border-right: none; }
1002
+ .sm-cell:hover {
1003
+ background: var(--paper-warm);
1004
+ box-shadow: inset 0 0 0 1px var(--ink-faint);
1005
+ }
1006
+ .sm-cell.sm-empty {
1007
+ grid-column: 3 / -1;
1008
+ color: var(--ink-faint);
1009
+ font-style: italic;
1010
+ padding: 14px;
1011
+ }
1012
+ .sm-cell.sm-drop-target {
1013
+ background: rgba(15, 95, 79, 0.16);
1014
+ box-shadow: inset 0 0 0 3px var(--accent);
1015
+ }
1016
+ .sm-cell.sm-drop-target::after {
1017
+ content: "Drop here";
1018
+ position: absolute;
1019
+ inset: 0;
1020
+ display: flex; align-items: center; justify-content: center;
1021
+ color: var(--accent);
1022
+ font-weight: 600;
1023
+ font-size: 13px;
1024
+ letter-spacing: 0.05em;
1025
+ text-transform: uppercase;
1026
+ pointer-events: none;
1027
+ background: rgba(15, 95, 79, 0.06);
1028
+ }
1029
+
1030
+ /* Per-Cell-Add-Button — Ghost-Variant, sticky bottom, prominenter +-Icon */
1031
+ .sm-add-ticket {
1032
+ margin-top: auto; /* sticky bottom of cell */
1033
+ display: inline-flex;
1034
+ align-items: center;
1035
+ gap: 4px;
1036
+ align-self: stretch;
1037
+ padding: 5px 10px;
1038
+ font-size: 11px;
1039
+ color: var(--ink-faint);
1040
+ background: transparent;
1041
+ border: 1px dashed var(--rule);
1042
+ border-radius: 4px;
1043
+ cursor: pointer;
1044
+ font: inherit; font-size: 11px;
1045
+ transition: background 120ms, color 120ms, border-color 120ms;
1046
+ }
1047
+ .sm-add-ticket:hover {
1048
+ background: var(--paper-warm);
1049
+ color: var(--ink);
1050
+ border-color: var(--ink-faint);
1051
+ border-style: solid;
1052
+ }
1053
+ .sm-add-ticket .sm-add-icon {
1054
+ display: inline-flex;
1055
+ align-items: center; justify-content: center;
1056
+ width: 14px; height: 14px;
1057
+ background: var(--ink-faint);
1058
+ color: var(--paper);
1059
+ border-radius: 50%;
1060
+ font-size: 11px;
1061
+ line-height: 1;
1062
+ font-weight: 700;
1063
+ }
1064
+ .sm-add-ticket:hover .sm-add-icon {
1065
+ background: var(--accent);
1066
+ }
1067
+ .sm-add-ticket .sm-add-label { letter-spacing: 0.02em; }
1068
+
1069
+ /* ---------- Cluster-Klassen (paperlike, 1:1 aus cmapper) ----------- */
1070
+ .sm-cluster-blue { background: var(--cluster-blue-fill); border-color: var(--cluster-blue-border); color: var(--cluster-blue-ink); }
1071
+ .sm-cluster-green { background: var(--cluster-green-fill); border-color: var(--cluster-green-border); color: var(--cluster-green-ink); }
1072
+ .sm-cluster-amber { background: var(--cluster-amber-fill); border-color: var(--cluster-amber-border); color: var(--cluster-amber-ink); }
1073
+ .sm-cluster-rust { background: var(--cluster-rust-fill); border-color: var(--cluster-rust-border); color: var(--cluster-rust-ink); }
1074
+ .sm-cluster-plum { background: var(--cluster-plum-fill); border-color: var(--cluster-plum-border); color: var(--cluster-plum-ink); }
1075
+ .sm-cluster-slate { background: var(--cluster-slate-fill); border-color: var(--cluster-slate-border); color: var(--cluster-slate-ink); }
1076
+ .sm-cluster-teal { background: var(--cluster-teal-fill); border-color: var(--cluster-teal-border); color: var(--cluster-teal-ink); }
1077
+
1078
+ /* ---------- Cell-Layout: schlanke Säulen nebeneinander (SM-136) ----------
1079
+ Epic-Säulen UND die Loose-Säule stehen in EINER horizontalen, nicht-
1080
+ wrappenden Reihe. Die Cell-Breite ist via columnMultiplier so bemessen,
1081
+ dass alle Säulen hineinpassen; overflow-x:clip auf .sm-cell ist ein
1082
+ defensives Netz (clip statt auto — SM-281: kein Scroll-Container, sonst
1083
+ fängt die Zelle die horizontale Trackpad-Geste ab). align-items:stretch
1084
+ (SM-134) gleicht die Säulen-
1085
+ Unterkanten an. */
1086
+ .sm-cell-epics {
1087
+ display: flex;
1088
+ flex-direction: row;
1089
+ flex-wrap: nowrap; /* SM-136: keine zweite Reihe — alles nebeneinander */
1090
+ align-items: stretch; /* SM-134: Säulen auf gleiche Höhe → Unterkanten alignen */
1091
+ flex: 1; /* füllt die Zell-Höhe (= Release-Row-Höhe via Grid-Stretch) */
1092
+ gap: 8px;
1093
+ }
1094
+ /* SM-136: Loose-Säule — visuell wie eine Epic-Karte (Papier + Lift), aber
1095
+ mit neutralem Header-Band statt Plum. Breite wird inline auf
1096
+ STORY_CARD_WIDTH_PX gesetzt (Konstante bleibt Single-Source). */
1097
+ .sm-loose-card {
1098
+ display: flex;
1099
+ flex-direction: column;
1100
+ flex: 0 0 auto;
1101
+ background: var(--paper);
1102
+ border-radius: 6px;
1103
+ overflow: hidden;
1104
+ box-shadow: var(--shadow-lift);
1105
+ }
1106
+ .sm-loose-header {
1107
+ display: flex; align-items: center; gap: 8px;
1108
+ padding: 8px 10px;
1109
+ height: var(--sm-epic-header-h); /* SM-136: gleiche Höhe wie Epic-Header → Baseline */
1110
+ box-sizing: border-box;
1111
+ overflow: hidden;
1112
+ background: rgba(26, 26, 26, 0.05);
1113
+ border-bottom: 1px solid var(--rule);
1114
+ font-size: 12px;
1115
+ }
1116
+ .sm-loose-title {
1117
+ font-weight: 600;
1118
+ color: var(--ink-soft);
1119
+ font-size: 12px;
1120
+ letter-spacing: 0.01em;
1121
+ }
1122
+ /* SM-136: Loose-Stories als Single-Column-Grid — identische Rhythmik wie
1123
+ .sm-stories-grid (feste Karten-Höhe, gleiche Gaps/Paddings), damit die
1124
+ losen Karten Zeile für Zeile mit den Epic-Stories fluchten. Die einzige
1125
+ Spaltenbreite wird inline gesetzt (epicW − 2×Padding). */
1126
+ .sm-cell-loose {
1127
+ display: grid;
1128
+ grid-auto-flow: row;
1129
+ grid-auto-rows: var(--sm-card-h);
1130
+ gap: 4px;
1131
+ padding: 6px;
1132
+ background: transparent;
1133
+ }
1134
+
1135
+ /* ---------- Epic-Karte (Container für Stories) ---------- */
1136
+ .sm-epic-card {
1137
+ /* Kein flächiger Plum-BG mehr — sonst entstand ein "Klammer"-Effekt
1138
+ unterhalb des Story-Grids. Stattdessen Papierhintergrund + Header
1139
+ trägt die Cluster-Farbe als oberer Akzent. */
1140
+ border: 0;
1141
+ background: var(--paper);
1142
+ border-radius: 6px;
1143
+ overflow: hidden; /* clip Header-Plum an den abgerundeten Ecken */
1144
+ cursor: grab;
1145
+ box-shadow: var(--shadow-lift);
1146
+ transition: transform 0.1s, box-shadow 0.1s;
1147
+ flex: 0 1 auto; /* shrink-to-fit innerhalb von .sm-cell-epics */
1148
+ width: fit-content;
1149
+ min-width: 292px; /* eine Story-Spaltenbreite (280) + Padding */
1150
+ max-width: 100%; /* SM-55: niemals breiter als die Cell */
1151
+ }
1152
+ .sm-epic-card:hover {
1153
+ transform: translateY(-1px);
1154
+ box-shadow: 0 4px 8px rgba(26,26,26,0.08), 0 10px 28px rgba(26,26,26,0.08);
1155
+ }
1156
+ .sm-epic-card.sm-dragging { opacity: 0.4; cursor: grabbing; }
1157
+ .sm-epic-card.sm-drop-target {
1158
+ box-shadow: inset 0 0 0 3px var(--accent), 0 4px 12px rgba(15, 95, 79, 0.30);
1159
+ transform: scale(1.01);
1160
+ }
1161
+ /* SM-131: uniform-grid Layout-Tokens (spiegeln STORY_MAP_LAYOUT). */
1162
+ .sm-grid {
1163
+ --sm-card-h: 72px;
1164
+ --sm-epic-header-h: 56px;
1165
+ --sm-gap: 8px;
1166
+ --sm-pad: 6px;
1167
+ }
1168
+ .sm-epic-header {
1169
+ display: flex; align-items: center; gap: 8px;
1170
+ padding: 8px 10px;
1171
+ height: var(--sm-epic-header-h); /* SM-131: feste Header-Höhe */
1172
+ box-sizing: border-box;
1173
+ overflow: hidden;
1174
+ background: var(--cluster-plum-fill);
1175
+ border-bottom: 1px solid rgba(90, 57, 87, 0.18);
1176
+ font-size: 12px;
1177
+ cursor: pointer;
1178
+ }
1179
+ .sm-epic-header:hover { background: rgba(90, 57, 87, 0.10); }
1180
+ /* SM-138: kompaktes „+" im Epic-Header (oben rechts), neben dem Chevron. */
1181
+ .sm-epic-add {
1182
+ appearance: none;
1183
+ background: transparent;
1184
+ border: 1px solid transparent;
1185
+ border-radius: 4px;
1186
+ color: var(--cluster-plum-ink);
1187
+ cursor: pointer;
1188
+ font-size: 14px; font-weight: 700; line-height: 1;
1189
+ padding: 2px 6px;
1190
+ min-width: 22px;
1191
+ display: inline-flex; align-items: center; justify-content: center;
1192
+ transition: background 120ms, color 120ms, border-color 120ms;
1193
+ }
1194
+ .sm-epic-add:hover {
1195
+ background: rgba(90, 57, 87, 0.10);
1196
+ border-color: var(--cluster-plum-border);
1197
+ }
1198
+ .sm-epic-info {
1199
+ display: flex; flex-direction: column; gap: 1px;
1200
+ flex: 1; min-width: 0;
1201
+ }
1202
+ .sm-epic-title {
1203
+ font-weight: 600;
1204
+ color: var(--cluster-plum-ink);
1205
+ font-size: 12px;
1206
+ line-height: 1.25;
1207
+ overflow-wrap: anywhere;
1208
+ word-break: break-word;
1209
+ /* SM-131: fest auf 2 Zeilen + Ellipse (statt freiem Umbruch). */
1210
+ display: -webkit-box;
1211
+ -webkit-line-clamp: 2;
1212
+ -webkit-box-orient: vertical;
1213
+ overflow: hidden;
1214
+ text-overflow: ellipsis;
1215
+ }
1216
+ .sm-epic-key {
1217
+ font-family: ui-monospace, SFMono-Regular, monospace;
1218
+ font-size: 10px;
1219
+ color: var(--ink-faint);
1220
+ letter-spacing: 0.02em;
1221
+ }
1222
+
1223
+ /* + Story Button am Boden jeder Epic-Karte */
1224
+ .sm-add-story {
1225
+ display: inline-flex; align-items: center; gap: 4px;
1226
+ margin: 6px 8px 8px;
1227
+ padding: 4px 8px;
1228
+ font: inherit; font-size: 11px;
1229
+ color: var(--ink-faint);
1230
+ background: transparent;
1231
+ border: 1px dashed rgba(90, 57, 87, 0.4);
1232
+ border-radius: 4px;
1233
+ cursor: pointer;
1234
+ transition: background 120ms, color 120ms, border-color 120ms;
1235
+ }
1236
+ .sm-add-story:hover {
1237
+ background: rgba(90, 57, 87, 0.08);
1238
+ color: var(--cluster-plum-ink);
1239
+ border-color: var(--cluster-plum-border);
1240
+ border-style: solid;
1241
+ }
1242
+ .sm-add-story .sm-add-icon {
1243
+ display: inline-flex; align-items: center; justify-content: center;
1244
+ width: 12px; height: 12px;
1245
+ background: rgba(90, 57, 87, 0.5);
1246
+ color: var(--paper);
1247
+ border-radius: 50%;
1248
+ font-size: 9px;
1249
+ font-weight: 700;
1250
+ line-height: 1;
1251
+ }
1252
+ .sm-add-story:hover .sm-add-icon { background: var(--cluster-plum-border); }
1253
+
1254
+ /* ---------- Stories-Grid unter dem Epic (Single-Column, SM-135) -----------
1255
+ Inline-Styles vom Renderer setzen:
1256
+ grid-auto-flow: row
1257
+ grid-template-columns: <inside-story-width>
1258
+ grid-auto-rows: var(--sm-card-h)
1259
+ Stories stapeln einspaltig vertikal und wachsen beliebig nach unten —
1260
+ kein Sub-Spalten-Wrap mehr (jedes Epic ist eine schlanke Säule). */
1261
+ .sm-stories-grid {
1262
+ display: grid;
1263
+ gap: 4px;
1264
+ padding: 6px;
1265
+ background: transparent; /* erbt --paper vom .sm-epic-card, kein Plum unten */
1266
+ /* SM-84: animated collapse — the grid stays in the DOM and animates via
1267
+ max-height + opacity + padding. main.js owns the inline max-height
1268
+ (scrollHeight → 0 on collapse, 0 → scrollHeight on expand, then cleared
1269
+ after transitionend so future story additions aren't clipped). We do
1270
+ NOT set max-height in the .sm-stories-grid-collapsed class — that
1271
+ would clobber the inline start value and Safari would skip the
1272
+ transition. overflow:hidden is permanent for clean animation edges. */
1273
+ overflow: hidden;
1274
+ transition:
1275
+ max-height 240ms ease,
1276
+ opacity 200ms ease,
1277
+ padding 240ms ease;
1278
+ }
1279
+ .sm-stories-grid.sm-stories-grid-collapsed {
1280
+ opacity: 0;
1281
+ padding-top: 0;
1282
+ padding-bottom: 0;
1283
+ }
1284
+ /* SM-84 — collapse chevron in the epic header. Sits at the rightmost
1285
+ end, after .sm-epic-info (flex:1) and .sm-epic-badges. Small + neutral
1286
+ so it doesn't compete with the title or badges for attention. */
1287
+ .sm-epic-chevron {
1288
+ appearance: none;
1289
+ background: transparent;
1290
+ border: 1px solid transparent;
1291
+ border-radius: 4px;
1292
+ color: var(--ink-soft);
1293
+ cursor: pointer;
1294
+ font-size: 11px;
1295
+ line-height: 1;
1296
+ padding: 2px 6px;
1297
+ min-width: 22px;
1298
+ display: inline-flex;
1299
+ align-items: center;
1300
+ justify-content: center;
1301
+ transition: background 120ms, color 120ms, border-color 120ms;
1302
+ }
1303
+ .sm-epic-chevron:hover {
1304
+ background: var(--paper);
1305
+ color: var(--ink);
1306
+ border-color: var(--rule);
1307
+ }
1308
+ .sm-epic-card.sm-epic-card-collapsed .sm-epic-header {
1309
+ border-bottom-color: transparent;
1310
+ }
1311
+
1312
+ /* ---------- Story-Karte (paperlike, cluster-coloured) -------------
1313
+ Width wird inline aus STORY_CARD_WIDTH_PX gesetzt. Kein Border —
1314
+ das Cluster-Fill kodiert den Type. */
1315
+ .sm-story-card {
1316
+ border: 0;
1317
+ border-radius: 4px;
1318
+ padding: 6px 8px;
1319
+ height: var(--sm-card-h); /* SM-131: feste Höhe → uniformes Raster */
1320
+ overflow: hidden;
1321
+ cursor: grab;
1322
+ font-size: 11px;
1323
+ line-height: 1.35;
1324
+ transition: transform 0.08s, box-shadow 0.08s;
1325
+ flex-shrink: 0;
1326
+ box-sizing: border-box;
1327
+ }
1328
+ .sm-story-card:hover {
1329
+ transform: translateY(-1px);
1330
+ box-shadow: var(--shadow-lift);
1331
+ }
1332
+ .sm-story-card.sm-dragging { opacity: 0.4; cursor: grabbing; }
1333
+
1334
+ /* Drag-Live-Preview: Origin verschwindet, eine Schatten-Karte erscheint
1335
+ an der Insertion-Position im Drop-Target. Gestricheltes 2px-Border +
1336
+ gedimmt — wie svelte-dnd-action in der alten StoryBrain. */
1337
+ .sm-card-shadow {
1338
+ border: 2px dashed var(--accent) !important;
1339
+ background: var(--paper-warm) !important;
1340
+ opacity: 0.55;
1341
+ pointer-events: none;
1342
+ }
1343
+
1344
+ .sm-card-info { display: flex; flex-direction: column; gap: 1px; }
1345
+
1346
+ .sm-story-key {
1347
+ font-family: ui-monospace, SFMono-Regular, monospace;
1348
+ font-size: 9px;
1349
+ color: currentColor;
1350
+ opacity: 0.55;
1351
+ letter-spacing: 0.02em;
1352
+ }
1353
+ .sm-story-title {
1354
+ font-weight: 500;
1355
+ color: currentColor;
1356
+ line-height: 1.35;
1357
+ height: calc(2 * 1.35em); /* SM-131: 2-Zeilen-Slot, 1-Zeiler füllt ihn auch */
1358
+ overflow: hidden; text-overflow: ellipsis;
1359
+ display: -webkit-box;
1360
+ -webkit-line-clamp: 2;
1361
+ -webkit-box-orient: vertical;
1362
+ }
1363
+ .sm-card-meta {
1364
+ display: flex; gap: 4px; flex-wrap: nowrap; align-items: center;
1365
+ margin-top: 4px;
1366
+ overflow: hidden; /* einzeilig — die breiteren Karten (280px) fassen alle Badges in eine Zeile */
1367
+ font-size: 10px; color: var(--ink-soft);
1368
+ }
1369
+ .sm-card-status {
1370
+ padding: 1px 7px;
1371
+ border-radius: 999px;
1372
+ font-size: 9px; font-weight: 500;
1373
+ text-transform: capitalize;
1374
+ white-space: nowrap; /* Status-Pille nie intern umbrechen (z.B. "In-Progress") */
1375
+ }
1376
+ .sm-card-release-tag {
1377
+ display: inline-block;
1378
+ padding: 0 6px;
1379
+ border-radius: 4px;
1380
+ background: var(--paper-warm);
1381
+ border: 1px solid var(--rule);
1382
+ font-size: 10px;
1383
+ color: var(--ink);
1384
+ font-weight: 500;
1385
+ letter-spacing: 0.01em;
1386
+ }
1387
+
1388
+ /* Status-Pill colours (alt-StoryBrain-Palette) */
1389
+ .sm-status-pill-backlog { background: #e5e7eb; color: #374151; }
1390
+ .sm-status-pill-ready { background: #dbeafe; color: #1e40af; }
1391
+ .sm-status-pill-in-progress { background: #fef3c7; color: #92400e; }
1392
+ .sm-status-pill-review { background: #ede9fe; color: #5b21b6; }
1393
+ .sm-status-pill-done { background: #d1fae5; color: #065f46; }
1394
+ /* SM-244: cancelled = terminal, deliberately dropped. Muted + struck through. */
1395
+ .sm-status-pill-cancelled { background: #e7e5e4; color: #78716c; text-decoration: line-through; }
1396
+
1397
+ /* SM-244: a cancelled card is dimmed and its title struck through — visible
1398
+ (Cancel ≠ Delete) but clearly out of scope. Applies on both views (the
1399
+ shared card carries .sm-status-cancelled; the epic card too). */
1400
+ .sm-story-card.sm-status-cancelled,
1401
+ .sm-epic-card.sm-status-cancelled { opacity: 0.6; }
1402
+ .sm-story-card.sm-status-cancelled .sm-story-title,
1403
+ .sm-epic-card.sm-status-cancelled .sm-epic-title { text-decoration: line-through; }
1404
+
1405
+ .sm-badge {
1406
+ padding: 1px 5px;
1407
+ border-radius: 3px;
1408
+ background: var(--paper-warm);
1409
+ border: 1px solid var(--rule);
1410
+ font-size: 9px;
1411
+ }
1412
+ .sm-badge-dor { color: var(--target); }
1413
+ .sm-badge-dod { color: var(--accent); }
1414
+
1415
+ /* SM-170: card-aging badge — surfaces cards sitting too long in an active
1416
+ status. Amber at WARN, red at STALE (thresholds in renderer-card.js). */
1417
+ .sm-card-age { font-weight: 700; letter-spacing: 0.02em; }
1418
+ .sm-card-age-warn {
1419
+ color: #8A5A12;
1420
+ background: rgba(214, 158, 46, 0.16);
1421
+ border-color: rgba(214, 158, 46, 0.55);
1422
+ }
1423
+ .sm-card-age-stale {
1424
+ color: var(--danger, #B43C3C);
1425
+ background: rgba(180, 60, 60, 0.14);
1426
+ border-color: rgba(180, 60, 60, 0.55);
1427
+ }
1428
+
1429
+ /* SM-49: link badges (forward / backward dependency counts) */
1430
+ .sm-link-badge {
1431
+ cursor: pointer;
1432
+ font-weight: 500;
1433
+ border-color: transparent;
1434
+ }
1435
+ .sm-link-badge-sem-blocking { background: #FCE4E4; color: #8A1F1F; }
1436
+ .sm-link-badge-sem-precedence { background: #DCE6F5; color: #1E3A6B; }
1437
+ .sm-link-badge-sem-sequence { background: #E5E7EB; color: #374151; }
1438
+ .sm-link-badge-sem-containment { background: #DEF0E3; color: #1F5530; }
1439
+ .sm-link-badge-sem-validation { background: #FCEFD3; color: #8A5A14; }
1440
+ .sm-link-badge-sem-freeform { background: #E5E7EB; color: #4B5563; }
1441
+ .sm-link-badge-sem-mixed { background: #ECE4F2; color: #4A2860; }
1442
+ .sm-link-badge:hover { filter: brightness(0.96); }
1443
+ .sm-link-badge-backward { font-style: italic; opacity: 0.92; }
1444
+
1445
+ /* SM-60: last-execution outcome badge (test-definition cards only).
1446
+ Cursor:pointer because clicking opens the linked execution modal.
1447
+ Colours roughly match the in-modal outcome pill so the user sees the
1448
+ same encoding everywhere. */
1449
+ .sm-exec-badge {
1450
+ cursor: pointer;
1451
+ font-weight: 600;
1452
+ text-transform: uppercase;
1453
+ letter-spacing: 0.04em;
1454
+ border-color: transparent;
1455
+ }
1456
+ .sm-exec-badge:hover { filter: brightness(0.95); }
1457
+ .sm-exec-badge-passed { background: rgba(34, 139, 34, 0.16); color: #1F5530; }
1458
+ .sm-exec-badge-failed { background: rgba(180, 60, 60, 0.18); color: #8A1F1F; }
1459
+ .sm-exec-badge-blocked { background: rgba(230, 180, 50, 0.24); color: #8A5A14; }
1460
+ .sm-exec-badge-skipped { background: rgba(150, 150, 150, 0.18); color: #4B5563; }
1461
+ .sm-exec-badge-pending { background: var(--paper-warm); color: var(--ink-soft); }
1462
+ .sm-exec-badge-untested {
1463
+ background: transparent;
1464
+ color: var(--ink-faint);
1465
+ border: 1px dashed var(--rule);
1466
+ cursor: default;
1467
+ font-style: italic;
1468
+ }
1469
+
1470
+ .sm-epic-badges {
1471
+ display: inline-flex; gap: 4px; align-items: center; margin-left: 8px;
1472
+ }
1473
+
1474
+ /* ---------- Drag-Affordance + Ghost (E9f, Pointer-Events) ---------- */
1475
+ .sm-epic-card, .sm-story-card {
1476
+ cursor: grab;
1477
+ user-select: none; /* keine Text-Selektion beim Drag */
1478
+ -webkit-user-select: none;
1479
+ touch-action: none; /* Browser darf kein Scroll bei Drag */
1480
+ }
1481
+ .sm-epic-card.sm-dragging, .sm-story-card.sm-dragging {
1482
+ opacity: 0.35;
1483
+ cursor: grabbing;
1484
+ }
1485
+ .sm-drag-ghost {
1486
+ pointer-events: none;
1487
+ transition: none;
1488
+ }
1489
+
1490
+ /* ---------- Phantom-Release-Row (E9g) — wenn ProcessSteps aber kein Release */
1491
+ .sm-phantom-row { opacity: 0.85; }
1492
+ .sm-phantom-label {
1493
+ background: var(--rule-soft);
1494
+ padding: 10px;
1495
+ justify-content: center;
1496
+ }
1497
+ .sm-phantom-cta {
1498
+ font-size: 11px;
1499
+ padding: 6px 10px;
1500
+ border-style: dashed;
1501
+ background: transparent;
1502
+ }
1503
+ .sm-phantom-cta:hover {
1504
+ border-style: solid;
1505
+ background: var(--paper-warm);
1506
+ }
1507
+ .sm-phantom-cell {
1508
+ background: repeating-linear-gradient(
1509
+ 45deg,
1510
+ var(--rule-soft),
1511
+ var(--rule-soft) 8px,
1512
+ transparent 8px,
1513
+ transparent 16px
1514
+ );
1515
+ }
1516
+ .sm-phantom-cell::after {
1517
+ content: "";
1518
+ display: block;
1519
+ height: 100%;
1520
+ min-height: 80px;
1521
+ }
1522
+
1523
+ /* ---------- Backlog-Section unten (Epics ohne Release) ---------- */
1524
+ /* SM-253: per-release holding strip — release-assigned-but-unplaced tickets sit
1525
+ in a compact strip under their release's cells row (drag into a step to place;
1526
+ drop here / on the release header to unplace). No global backlog anymore. */
1527
+ .sm-release-unplaced-row {
1528
+ display: flex;
1529
+ align-items: flex-start;
1530
+ gap: 10px;
1531
+ padding: 6px 12px 10px;
1532
+ margin-bottom: 2px;
1533
+ background: var(--rule-soft);
1534
+ }
1535
+ .sm-release-unplaced-row.sm-drop-target,
1536
+ .sm-release-label-row.sm-drop-target {
1537
+ box-shadow: inset 3px 0 0 var(--accent);
1538
+ background: var(--accent-soft);
1539
+ }
1540
+ .sm-release-unplaced-label {
1541
+ flex: 0 0 auto;
1542
+ padding-top: 6px;
1543
+ font-size: 10px;
1544
+ letter-spacing: 0.04em;
1545
+ text-transform: uppercase;
1546
+ color: var(--ink-faint);
1547
+ white-space: nowrap;
1548
+ }
1549
+ .sm-release-unplaced-list {
1550
+ display: flex;
1551
+ flex-wrap: wrap;
1552
+ gap: 8px;
1553
+ }
1554
+
1555
+ .sm-backlog-section {
1556
+ margin-top: 24px;
1557
+ padding: 12px 16px 24px;
1558
+ border-top: 2px solid var(--rule);
1559
+ background: var(--rule-soft);
1560
+ }
1561
+ .sm-backlog-title {
1562
+ font-size: 13px; font-weight: 700;
1563
+ margin: 0 0 10px;
1564
+ color: var(--ink-soft);
1565
+ letter-spacing: 0.04em;
1566
+ text-transform: uppercase;
1567
+ }
1568
+ .sm-add-backlog-item {
1569
+ display: inline-flex; align-items: center; gap: 4px;
1570
+ margin-top: 8px;
1571
+ padding: 5px 12px;
1572
+ font: inherit; font-size: 11px;
1573
+ color: var(--ink-faint);
1574
+ background: transparent;
1575
+ border: 1px dashed var(--rule);
1576
+ border-radius: 4px;
1577
+ cursor: pointer;
1578
+ transition: background 120ms, color 120ms, border-color 120ms;
1579
+ align-self: flex-start;
1580
+ }
1581
+ .sm-add-backlog-item:hover {
1582
+ background: var(--paper-warm);
1583
+ color: var(--ink);
1584
+ border-color: var(--ink-faint);
1585
+ border-style: solid;
1586
+ }
1587
+ .sm-add-backlog-item .sm-add-icon {
1588
+ display: inline-flex; align-items: center; justify-content: center;
1589
+ width: 14px; height: 14px;
1590
+ background: var(--ink-faint);
1591
+ color: var(--paper);
1592
+ border-radius: 50%;
1593
+ font-size: 11px;
1594
+ font-weight: 700;
1595
+ line-height: 1;
1596
+ }
1597
+ .sm-add-backlog-item:hover .sm-add-icon { background: var(--accent); }
1598
+ /* Backlog ist eine PRIORISIERTE Liste (oben = höchste Priorität). Stack
1599
+ vertikal, per Drag & Drop sortierbar. Maximalbreite, damit die Karten
1600
+ nicht über die ganze Map ausufern. */
1601
+ .sm-backlog-list {
1602
+ display: flex;
1603
+ flex-direction: column;
1604
+ gap: 6px;
1605
+ max-width: 720px;
1606
+ }
1607
+ .sm-backlog-list .sm-story-card,
1608
+ .sm-backlog-list .sm-epic-card {
1609
+ width: 100%;
1610
+ max-width: 100%;
1611
+ }
1612
+ /* SM-193: multi-column backlog — CSS columns balance card heights; the
1613
+ column-count + max-width are set inline from the layout constants. The 8px
1614
+ below mirrors STORY_MAP_LAYOUT.TICKET_GAP_PX (the inline max-width math in
1615
+ applyBacklogWrap assumes the same gap — keep both in sync if it changes). */
1616
+ .sm-backlog-list-multicol {
1617
+ display: block; /* override the flex column so cards flow into columns */
1618
+ column-gap: 8px; /* == TICKET_GAP_PX */
1619
+ }
1620
+ .sm-backlog-list-multicol .sm-story-card,
1621
+ .sm-backlog-list-multicol .sm-epic-card {
1622
+ break-inside: avoid; /* never split a card across a column boundary */
1623
+ -webkit-column-break-inside: avoid;
1624
+ margin-bottom: 8px; /* == TICKET_GAP_PX (flex `gap` doesn't apply in block/column flow) */
1625
+ }
1626
+ .sm-backlog-empty {
1627
+ color: var(--ink-faint);
1628
+ font-style: italic;
1629
+ font-size: 12px;
1630
+ }
1631
+
1632
+ /* ============================================================
1633
+ E10 — Ticket-Detail-Modal
1634
+ ============================================================ */
1635
+
1636
+ /* Widen the standard modal when it hosts the ticket-detail body. */
1637
+ .modal-overlay .modal:has(.tm-body-host) {
1638
+ width: 760px;
1639
+ max-width: 92vw;
1640
+ }
1641
+ .tm-body-host {
1642
+ display: flex; flex-direction: column;
1643
+ gap: 6px;
1644
+ overflow-y: auto;
1645
+ max-height: 68vh;
1646
+ padding-right: 4px;
1647
+ }
1648
+
1649
+ .tm-header {
1650
+ display: flex; align-items: center; gap: 8px;
1651
+ margin: -4px 0 8px;
1652
+ }
1653
+ .tm-key {
1654
+ font-family: ui-monospace, SFMono-Regular, monospace;
1655
+ font-size: 11px;
1656
+ color: var(--ink-soft);
1657
+ background: var(--paper-warm);
1658
+ border: 1px solid var(--rule);
1659
+ border-radius: 3px;
1660
+ padding: 1px 6px;
1661
+ }
1662
+ .tm-type-badge {
1663
+ font-size: 11px;
1664
+ color: var(--ink-soft);
1665
+ text-transform: lowercase;
1666
+ letter-spacing: 0.02em;
1667
+ background: var(--rule-soft);
1668
+ border-radius: 3px;
1669
+ padding: 1px 6px;
1670
+ }
1671
+
1672
+ .tm-error-banner {
1673
+ font-size: 12px;
1674
+ background: rgba(180, 60, 60, 0.10);
1675
+ color: var(--danger, #B43C3C);
1676
+ border: 1px solid var(--danger, #B43C3C);
1677
+ border-radius: 4px;
1678
+ padding: 8px 10px;
1679
+ margin: 0 0 6px;
1680
+ }
1681
+
1682
+ /* Form row layout — mirrors .modal-row but namespaced. */
1683
+ .tm-row {
1684
+ display: flex; align-items: center; gap: 8px;
1685
+ margin: 6px 0;
1686
+ }
1687
+ .tm-label {
1688
+ flex: 0 0 130px;
1689
+ color: var(--ink-soft);
1690
+ font-size: 12px;
1691
+ }
1692
+ .tm-input {
1693
+ flex: 1;
1694
+ padding: 5px 8px;
1695
+ border: 1px solid var(--rule);
1696
+ border-radius: 4px;
1697
+ background: var(--paper);
1698
+ font: inherit; font-size: 12px;
1699
+ color: var(--ink);
1700
+ }
1701
+ .tm-input:focus { outline: none; border-color: var(--ink); }
1702
+ .tm-input:disabled {
1703
+ opacity: 0.7;
1704
+ background: var(--paper-soft, var(--paper));
1705
+ cursor: not-allowed;
1706
+ }
1707
+ /* SM-238 — epic derived-status: a read-only pill + the derivation breakdown,
1708
+ in place of the editable status <select>. The epic's status is rolled up
1709
+ from its stories, so there is nothing to pick here. */
1710
+ .tm-status-derived-wrap { flex: 1; display: flex; flex-direction: column; gap: 3px; }
1711
+ .tm-status-derived {
1712
+ align-self: flex-start;
1713
+ padding: 2px 10px;
1714
+ border-radius: 9px;
1715
+ font-size: 11px; font-weight: 700;
1716
+ background: var(--accent-soft, rgba(15, 95, 79, 0.16));
1717
+ color: var(--accent);
1718
+ border: 1px solid var(--accent);
1719
+ }
1720
+ .tm-status-breakdown { font-size: 11px; color: var(--ink-soft, #717071); }
1721
+ /* SM-67: inherited-position hint shown under Release/ProcessStep when a
1722
+ story is contained by an epic. Standalone row (not a child of .tm-row). */
1723
+ .tm-field-hint {
1724
+ margin: -2px 0 6px 138px;
1725
+ font-size: 11px;
1726
+ color: var(--ink-soft);
1727
+ font-style: italic;
1728
+ }
1729
+ .tm-field-hint:empty { display: none; }
1730
+ .tm-textarea {
1731
+ flex: 1;
1732
+ min-height: 90px;
1733
+ padding: 8px 10px;
1734
+ border: 1px solid var(--rule);
1735
+ border-radius: 4px;
1736
+ background: var(--paper);
1737
+ font: inherit; font-size: 12px;
1738
+ line-height: 1.5;
1739
+ color: var(--ink);
1740
+ resize: vertical;
1741
+ }
1742
+ .tm-textarea:focus { outline: none; border-color: var(--ink); }
1743
+
1744
+ /* SM-119: plain-text contentEditable Description. Grows with content — no
1745
+ max-height, no inner scrollbar. white-space: pre-wrap keeps user newlines.
1746
+ Shared by the modal and the full-page editor (both build it from
1747
+ ticket-form.js#buildDescriptionField). */
1748
+ .tm-description-edit {
1749
+ min-height: 90px;
1750
+ padding: 8px 10px;
1751
+ line-height: 1.5;
1752
+ white-space: pre-wrap;
1753
+ overflow-wrap: anywhere;
1754
+ word-break: break-word;
1755
+ cursor: text;
1756
+ }
1757
+ .tm-description-edit:empty::before {
1758
+ content: attr(data-placeholder);
1759
+ color: var(--ink-soft);
1760
+ pointer-events: none;
1761
+ }
1762
+ /* SM-157: Jira-style inline confirm/cancel for the description. The row wraps so
1763
+ the ✓/✗ sit on their own line under the editable, aligned with it (label is
1764
+ flex 0 0 130px + 8px gap = 138px). Hidden until the text differs from saved. */
1765
+ .tm-row:has(.tm-description-edit) { flex-wrap: wrap; align-items: flex-start; }
1766
+ .tm-desc-actions {
1767
+ flex-basis: 100%;
1768
+ margin-left: 138px;
1769
+ margin-top: 6px;
1770
+ display: flex;
1771
+ gap: 6px;
1772
+ }
1773
+ .tm-desc-save, .tm-desc-cancel {
1774
+ width: 26px; height: 26px;
1775
+ display: inline-flex; align-items: center; justify-content: center;
1776
+ border: 1px solid var(--rule);
1777
+ border-radius: 4px;
1778
+ background: var(--paper);
1779
+ font-size: 13px; line-height: 1;
1780
+ cursor: pointer;
1781
+ }
1782
+ .tm-desc-save { color: var(--accent); border-color: var(--accent); }
1783
+ .tm-desc-save:hover { background: var(--accent); color: var(--paper); }
1784
+ .tm-desc-cancel { color: var(--ink-soft); }
1785
+ .tm-desc-cancel:hover { border-color: var(--ink); color: var(--ink); }
1786
+
1787
+ /* Section headings inside the modal. */
1788
+ .tm-section-title {
1789
+ font-family: var(--font-body);
1790
+ font-size: 11px;
1791
+ font-weight: 700;
1792
+ text-transform: uppercase;
1793
+ letter-spacing: 0.06em;
1794
+ color: var(--ink-soft);
1795
+ margin: 14px 0 6px;
1796
+ }
1797
+
1798
+ /* SM-185 — Attachments section (ticket-scoped dropzone + list) */
1799
+ .tm-attachments-section { margin-top: 14px; }
1800
+ .tm-attach-dropzone {
1801
+ border: 1px dashed var(--rule);
1802
+ border-radius: 6px;
1803
+ padding: 14px;
1804
+ text-align: center;
1805
+ color: var(--ink-faint);
1806
+ font-size: 12px;
1807
+ cursor: pointer;
1808
+ transition: background 120ms, border-color 120ms, color 120ms;
1809
+ }
1810
+ .tm-attach-dropzone:hover,
1811
+ .tm-attach-dropzone-over {
1812
+ background: var(--paper-warm);
1813
+ border-color: var(--accent);
1814
+ color: var(--ink);
1815
+ }
1816
+ .tm-attach-list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
1817
+ .tm-attach-empty { color: var(--ink-faint); font-size: 12px; font-style: italic; padding: 2px 6px; }
1818
+ .tm-attach-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 4px 6px; border-radius: 4px; }
1819
+ .tm-attach-item:hover { background: var(--paper-warm); }
1820
+ .tm-attach-name { color: var(--accent); text-decoration: none; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1821
+ .tm-attach-name:hover { text-decoration: underline; }
1822
+ .tm-attach-size { color: var(--ink-faint); font-family: ui-monospace, SFMono-Regular, monospace; font-size: 11px; }
1823
+ .tm-attach-del {
1824
+ appearance: none; background: transparent; border: none; cursor: pointer;
1825
+ color: var(--ink-faint); font-size: 12px; line-height: 1; padding: 2px 6px; border-radius: 4px;
1826
+ }
1827
+ .tm-attach-del:hover { color: var(--danger, #B43C3C); background: var(--paper); }
1828
+
1829
+ /* SM-195 — attachment dropzone inside the New-project dialog row (fills the
1830
+ modal-row's second column next to the label). */
1831
+ .np-attach-row { align-items: flex-start; }
1832
+ .np-attach-wrap { flex: 1; }
1833
+
1834
+ /* Checklists (DoR / DoD). */
1835
+ .tm-checklist {
1836
+ border-top: 1px dashed var(--rule);
1837
+ padding: 4px 0 0;
1838
+ }
1839
+ .tm-checklist-empty {
1840
+ color: var(--ink-faint);
1841
+ font-size: 12px;
1842
+ font-style: italic;
1843
+ padding: 4px 0;
1844
+ }
1845
+ .tm-checkitem {
1846
+ display: flex; align-items: center;
1847
+ padding: 3px 0;
1848
+ font-size: 12px;
1849
+ }
1850
+ .tm-checkitem-label {
1851
+ display: inline-flex; align-items: center; gap: 8px;
1852
+ cursor: pointer;
1853
+ flex: 1;
1854
+ }
1855
+ .tm-checkitem-text { color: var(--ink); }
1856
+ .tm-required-tag {
1857
+ font-size: 10px;
1858
+ letter-spacing: 0.05em;
1859
+ text-transform: uppercase;
1860
+ color: var(--ink-faint);
1861
+ background: var(--rule-soft);
1862
+ border-radius: 3px;
1863
+ padding: 1px 5px;
1864
+ margin-left: auto;
1865
+ }
1866
+
1867
+ /* Acceptance criteria — small inline list editor. */
1868
+ .tm-ac-section {
1869
+ border-top: 1px dashed var(--rule);
1870
+ padding: 4px 0 0;
1871
+ }
1872
+ .tm-ac-list { display: flex; flex-direction: column; gap: 4px; }
1873
+ .tm-ac-row {
1874
+ display: flex; align-items: center; gap: 6px;
1875
+ padding: 2px 4px;
1876
+ border-radius: 3px;
1877
+ transition: background 100ms, box-shadow 100ms;
1878
+ }
1879
+ .tm-ac-row.sm-dragging { opacity: 0.4; }
1880
+ .tm-ac-row.tm-ac-drop-target {
1881
+ background: var(--paper-warm);
1882
+ box-shadow: inset 0 2px 0 var(--accent);
1883
+ }
1884
+ .tm-ac-grip {
1885
+ flex: 0 0 auto;
1886
+ color: var(--ink-faint);
1887
+ font-size: 11px;
1888
+ letter-spacing: -2px;
1889
+ cursor: grab;
1890
+ user-select: none;
1891
+ padding: 0 2px;
1892
+ }
1893
+ .tm-ac-grip:active { cursor: grabbing; }
1894
+ .tm-ac-row input[type="checkbox"] { flex: 0 0 auto; }
1895
+ .tm-ac-text { flex: 1; }
1896
+ .tm-ac-remove {
1897
+ flex: 0 0 auto;
1898
+ background: transparent;
1899
+ border: 1px solid var(--rule);
1900
+ border-radius: 3px;
1901
+ width: 22px; height: 22px;
1902
+ font-size: 14px;
1903
+ line-height: 1;
1904
+ color: var(--ink-faint);
1905
+ cursor: pointer;
1906
+ }
1907
+ .tm-ac-remove:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
1908
+ .tm-ac-add {
1909
+ display: inline-flex; align-items: center; gap: 4px;
1910
+ margin-top: 6px;
1911
+ padding: 4px 10px;
1912
+ font: inherit; font-size: 11px;
1913
+ color: var(--ink-faint);
1914
+ background: transparent;
1915
+ border: 1px dashed var(--rule);
1916
+ border-radius: 4px;
1917
+ cursor: pointer;
1918
+ align-self: flex-start;
1919
+ }
1920
+ .tm-ac-add:hover {
1921
+ background: var(--paper-warm); color: var(--ink);
1922
+ border-color: var(--ink-faint); border-style: solid;
1923
+ }
1924
+ .tm-ac-add .sm-add-icon {
1925
+ display: inline-flex; align-items: center; justify-content: center;
1926
+ width: 14px; height: 14px;
1927
+ background: var(--ink-faint); color: var(--paper);
1928
+ border-radius: 50%; font-size: 11px; font-weight: 700; line-height: 1;
1929
+ }
1930
+
1931
+ /* Project-Load-Dialog (cmapper-Pattern). Liste von Cards mit Counts. */
1932
+ .project-list-cards {
1933
+ display: grid;
1934
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
1935
+ gap: 10px;
1936
+ max-height: 60vh;
1937
+ overflow-y: auto;
1938
+ padding: 4px 2px;
1939
+ }
1940
+ .project-list-empty,
1941
+ .project-list-empty-filtered {
1942
+ color: var(--ink-faint);
1943
+ font-style: italic;
1944
+ font-size: 12px;
1945
+ padding: 12px 0;
1946
+ }
1947
+ /* SM-209 — project picker search + sort toolbar (cmapper parity). */
1948
+ .project-picker-toolbar {
1949
+ display: flex;
1950
+ gap: 8px;
1951
+ align-items: center;
1952
+ margin-bottom: 10px;
1953
+ }
1954
+ .project-picker-toolbar .project-search {
1955
+ flex: 1 1 auto;
1956
+ font-size: 12px;
1957
+ padding: 5px 9px;
1958
+ border: 1px solid var(--rule);
1959
+ border-radius: 4px;
1960
+ background: var(--paper);
1961
+ color: var(--ink);
1962
+ }
1963
+ .project-picker-toolbar .project-search:focus {
1964
+ outline: none;
1965
+ border-color: var(--accent);
1966
+ }
1967
+ .project-picker-toolbar .project-sort {
1968
+ flex: 0 0 auto;
1969
+ font-size: 12px;
1970
+ padding: 5px 8px;
1971
+ border: 1px solid var(--rule);
1972
+ border-radius: 4px;
1973
+ background: var(--paper);
1974
+ color: var(--ink);
1975
+ cursor: pointer;
1976
+ }
1977
+ .project-card-desc {
1978
+ font-size: 11px;
1979
+ color: var(--ink-soft);
1980
+ margin-top: 4px;
1981
+ line-height: 1.35;
1982
+ display: -webkit-box;
1983
+ -webkit-line-clamp: 2;
1984
+ -webkit-box-orient: vertical;
1985
+ overflow: hidden;
1986
+ }
1987
+ .project-card-mtime {
1988
+ font-size: 10px;
1989
+ color: var(--ink-faint);
1990
+ margin-top: 4px;
1991
+ }
1992
+ .project-card {
1993
+ position: relative;
1994
+ background: var(--paper);
1995
+ border: 1px solid var(--rule);
1996
+ border-radius: 6px;
1997
+ padding: 12px 14px;
1998
+ cursor: pointer;
1999
+ transition: background 100ms, border-color 100ms, transform 80ms;
2000
+ }
2001
+ .project-card:hover {
2002
+ background: var(--paper-warm);
2003
+ border-color: var(--ink-faint);
2004
+ transform: translateY(-1px);
2005
+ }
2006
+ .project-card.current {
2007
+ cursor: default;
2008
+ border-color: var(--accent);
2009
+ background: var(--paper-warm);
2010
+ }
2011
+ .project-card.current:hover { transform: none; }
2012
+ .project-card-header {
2013
+ display: flex; align-items: baseline; gap: 8px;
2014
+ margin-bottom: 4px;
2015
+ }
2016
+ .project-card-name {
2017
+ font-size: 13px;
2018
+ font-weight: 600;
2019
+ color: var(--ink);
2020
+ }
2021
+ .project-card-id {
2022
+ font-family: ui-monospace, SFMono-Regular, monospace;
2023
+ font-size: 10px;
2024
+ color: var(--ink-faint);
2025
+ }
2026
+ .project-card-current-badge {
2027
+ margin-left: auto;
2028
+ font-size: 10px;
2029
+ letter-spacing: 0.04em;
2030
+ text-transform: uppercase;
2031
+ color: var(--accent);
2032
+ background: rgba(15, 95, 79, 0.10);
2033
+ border-radius: 3px;
2034
+ padding: 1px 6px;
2035
+ }
2036
+ .project-card-counts {
2037
+ font-size: 11px;
2038
+ color: var(--ink-soft);
2039
+ font-family: ui-monospace, SFMono-Regular, monospace;
2040
+ }
2041
+ .project-card-delete {
2042
+ position: absolute;
2043
+ top: 8px; right: 10px;
2044
+ padding: 2px 8px;
2045
+ font-size: 10px;
2046
+ display: none; /* hidden until hover */
2047
+ }
2048
+ .project-card:hover .project-card-delete { display: inline-block; }
2049
+
2050
+ /* E13 — History-Dialog (Revisions-Liste, cmapper-Pattern). */
2051
+ .hist-list {
2052
+ display: flex; flex-direction: column;
2053
+ gap: 4px;
2054
+ max-height: 60vh;
2055
+ overflow-y: auto;
2056
+ padding: 2px;
2057
+ }
2058
+ .hist-empty {
2059
+ color: var(--ink-faint);
2060
+ font-style: italic;
2061
+ font-size: 12px;
2062
+ padding: 12px 0;
2063
+ }
2064
+ .hist-row {
2065
+ display: flex; align-items: center; gap: 10px;
2066
+ padding: 6px 10px;
2067
+ background: var(--paper);
2068
+ border: 1px solid var(--rule);
2069
+ border-radius: 4px;
2070
+ transition: background 100ms, border-color 100ms;
2071
+ }
2072
+ .hist-row:hover {
2073
+ background: var(--paper-warm);
2074
+ border-color: var(--ink-faint);
2075
+ }
2076
+ .hist-row-main {
2077
+ display: flex; align-items: center; gap: 10px;
2078
+ flex: 1; min-width: 0;
2079
+ }
2080
+ .hist-timestamp {
2081
+ font-family: ui-monospace, SFMono-Regular, monospace;
2082
+ font-size: 11px;
2083
+ color: var(--ink);
2084
+ }
2085
+ .hist-op-pill {
2086
+ font-size: 10px;
2087
+ letter-spacing: 0.04em;
2088
+ color: var(--ink-soft);
2089
+ background: var(--rule-soft);
2090
+ border-radius: 3px;
2091
+ padding: 1px 6px;
2092
+ }
2093
+ .hist-actor {
2094
+ font-size: 11px;
2095
+ color: var(--ink-faint);
2096
+ margin-left: auto;
2097
+ margin-right: 8px;
2098
+ }
2099
+ .hist-restore { padding: 3px 10px; font-size: 11px; }
2100
+
2101
+ /* Type-Picker (small modal that opens BEFORE the create-modal). */
2102
+ .tm-type-picker {
2103
+ display: flex; flex-direction: column; gap: 8px;
2104
+ padding: 6px 0 0;
2105
+ }
2106
+ .tm-type-pick-btn {
2107
+ display: flex; align-items: center; gap: 10px;
2108
+ padding: 10px 14px;
2109
+ font: inherit; font-size: 13px;
2110
+ color: var(--ink);
2111
+ background: var(--paper);
2112
+ border: 1px solid var(--rule);
2113
+ border-radius: 4px;
2114
+ cursor: pointer;
2115
+ text-align: left;
2116
+ transition: background 100ms, border-color 100ms;
2117
+ }
2118
+ .tm-type-pick-btn:hover {
2119
+ background: var(--paper-warm);
2120
+ border-color: var(--ink-faint);
2121
+ }
2122
+ .tm-type-pick-name {
2123
+ font-weight: 600;
2124
+ letter-spacing: 0.01em;
2125
+ }
2126
+
2127
+ /* ============================================================
2128
+ E11 — View-Toggle (Map / Kanban)
2129
+ ============================================================ */
2130
+
2131
+ .view-toggle {
2132
+ display: inline-flex;
2133
+ border: 1px solid var(--rule);
2134
+ border-radius: 6px;
2135
+ overflow: hidden;
2136
+ background: var(--paper);
2137
+ }
2138
+ .view-toggle-btn {
2139
+ appearance: none;
2140
+ background: transparent;
2141
+ border: none;
2142
+ padding: 5px 12px;
2143
+ font: inherit; font-size: 12px;
2144
+ color: var(--ink-soft);
2145
+ cursor: pointer;
2146
+ transition: background 100ms, color 100ms;
2147
+ }
2148
+ .view-toggle-btn + .view-toggle-btn { border-left: 1px solid var(--rule); }
2149
+ .view-toggle-btn:hover {
2150
+ background: var(--paper-warm);
2151
+ color: var(--ink);
2152
+ }
2153
+ .view-toggle-btn.active {
2154
+ background: var(--ink);
2155
+ color: var(--paper);
2156
+ font-weight: 600;
2157
+ }
2158
+
2159
+ /* ============================================================
2160
+ SM-82 — Filter Button + Popover
2161
+ ============================================================ */
2162
+
2163
+ /* SM-16 — toolbar ticket search */
2164
+ .ticket-search {
2165
+ appearance: none;
2166
+ width: 240px;
2167
+ background: var(--paper);
2168
+ border: 1px solid var(--rule);
2169
+ border-radius: 6px;
2170
+ padding: 5px 10px;
2171
+ margin-right: 8px;
2172
+ font: inherit; font-size: 12px;
2173
+ color: var(--ink);
2174
+ transition: width 120ms ease, border-color 100ms;
2175
+ }
2176
+ /* SM-191: the search box is the unified smart-bar — queries are longer than a
2177
+ word, so it grows wider on focus. */
2178
+ .ticket-search:focus { outline: none; border-color: var(--accent); width: 420px; }
2179
+ .ticket-search::placeholder { color: var(--ink-soft); }
2180
+ .smartbar-status {
2181
+ font-size: 11px;
2182
+ margin-right: 8px;
2183
+ white-space: nowrap;
2184
+ max-width: 280px;
2185
+ overflow: hidden;
2186
+ text-overflow: ellipsis;
2187
+ }
2188
+ .smartbar-status-ok { color: var(--ink-soft); }
2189
+ .smartbar-status-error { color: var(--danger, #a23); }
2190
+ /* SM-216: Jira-style autocomplete dropdown (fixed-positioned under the bar). */
2191
+ .smartbar-ac {
2192
+ position: fixed;
2193
+ z-index: 200;
2194
+ max-height: 280px;
2195
+ overflow-y: auto;
2196
+ background: var(--paper);
2197
+ border: 1px solid var(--rule);
2198
+ border-radius: 6px;
2199
+ box-shadow: 0 6px 22px rgba(0, 0, 0, 0.12);
2200
+ padding: 4px;
2201
+ font-size: 12px;
2202
+ }
2203
+ .smartbar-ac-item {
2204
+ display: flex;
2205
+ align-items: baseline;
2206
+ justify-content: space-between;
2207
+ gap: 12px;
2208
+ padding: 4px 8px;
2209
+ border-radius: 4px;
2210
+ cursor: pointer;
2211
+ white-space: nowrap;
2212
+ }
2213
+ .smartbar-ac-item.active,
2214
+ .smartbar-ac-item:hover { background: var(--paper-warm); }
2215
+ .smartbar-ac-label { color: var(--ink); }
2216
+ .smartbar-ac-kind { color: var(--ink-faint); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
2217
+
2218
+ /* SM-16 — live search highlight: matched cards pop, non-matched fade. */
2219
+ .sm-story-card.sm-search-dim { opacity: 0.22; }
2220
+ .sm-story-card.sm-search-hit { box-shadow: 0 0 0 2px var(--accent); }
2221
+ .sm-story-card.sm-search-dim,
2222
+ .sm-story-card.sm-search-hit { transition: opacity 140ms ease, box-shadow 140ms ease; }
2223
+ /* SM-208 — epic cards ring on match too. They are never dimmed (their opacity
2224
+ cascades to contained story cards), so only the hit-ring applies. */
2225
+ .sm-epic-card.sm-search-hit { box-shadow: 0 0 0 2px var(--accent); }
2226
+ .sm-epic-card.sm-search-hit { transition: box-shadow 140ms ease; }
2227
+
2228
+ /* SM-167 — release-completion warning (reactive inline list in Edit Release). */
2229
+ .er-open-warning {
2230
+ margin-top: 10px;
2231
+ border: 1px solid var(--danger, #b04a3a);
2232
+ border-radius: 6px;
2233
+ background: var(--danger-soft, rgba(176,74,58,0.08));
2234
+ padding: 8px 10px;
2235
+ font-size: 12px;
2236
+ }
2237
+ .er-open-head { color: var(--ink); margin-bottom: 6px; }
2238
+ .er-open-list { list-style: none; margin: 0; padding: 0; max-height: 160px; overflow-y: auto; }
2239
+ .er-open-list li { display: flex; align-items: baseline; gap: 8px; padding: 2px 0; }
2240
+ .er-open-key { font-weight: 600; color: var(--ink); flex: 0 0 auto; }
2241
+ .er-open-title { color: var(--ink-soft); flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2242
+ .er-open-status { flex: 0 0 auto; font-size: 11px; color: var(--ink-soft); border: 1px solid var(--rule); border-radius: 10px; padding: 0 7px; }
2243
+
2244
+ .filter-btn {
2245
+ appearance: none;
2246
+ display: inline-flex; align-items: center; gap: 6px;
2247
+ background: var(--paper);
2248
+ border: 1px solid var(--rule);
2249
+ border-radius: 6px;
2250
+ padding: 5px 12px;
2251
+ margin-right: 8px;
2252
+ font: inherit; font-size: 12px;
2253
+ color: var(--ink-soft);
2254
+ cursor: pointer;
2255
+ transition: background 100ms, color 100ms, border-color 100ms;
2256
+ }
2257
+ .filter-btn:hover { background: var(--paper-warm); color: var(--ink); border-color: var(--ink-faint); }
2258
+ .filter-btn.active {
2259
+ background: var(--accent-soft, rgba(15, 95, 79, 0.10));
2260
+ color: var(--accent);
2261
+ border-color: var(--accent);
2262
+ font-weight: 600;
2263
+ }
2264
+ .filter-badge {
2265
+ display: inline-block;
2266
+ min-width: 18px; height: 18px;
2267
+ padding: 0 5px;
2268
+ border-radius: 9px;
2269
+ background: var(--accent);
2270
+ color: var(--paper);
2271
+ font-size: 11px; font-weight: 700; line-height: 18px;
2272
+ text-align: center;
2273
+ }
2274
+
2275
+ #filter-popover {
2276
+ position: fixed;
2277
+ background: var(--paper);
2278
+ border: 1px solid var(--rule);
2279
+ border-radius: 6px;
2280
+ box-shadow: var(--shadow-lift, 0 8px 24px rgba(0,0,0,0.12));
2281
+ padding: 14px 16px;
2282
+ width: 280px;
2283
+ max-height: 70vh;
2284
+ overflow-y: auto;
2285
+ z-index: 80;
2286
+ }
2287
+ #filter-popover[hidden] { display: none; }
2288
+ .filter-popover-section { margin-bottom: 14px; }
2289
+ .filter-popover-section:last-of-type { margin-bottom: 6px; }
2290
+ .filter-popover-section-header {
2291
+ display: flex; align-items: center; justify-content: space-between;
2292
+ margin-bottom: 6px;
2293
+ }
2294
+ .filter-popover-section-title {
2295
+ font-size: 11px; font-weight: 700;
2296
+ text-transform: uppercase; letter-spacing: 0.05em;
2297
+ color: var(--ink-soft);
2298
+ }
2299
+ .filter-popover-section-actions {
2300
+ display: inline-flex; gap: 4px;
2301
+ }
2302
+ .filter-popover-bulk {
2303
+ appearance: none;
2304
+ background: transparent;
2305
+ border: 1px solid var(--rule);
2306
+ border-radius: 3px;
2307
+ padding: 1px 6px;
2308
+ font: inherit; font-size: 10px;
2309
+ color: var(--ink-soft);
2310
+ cursor: pointer;
2311
+ text-transform: lowercase;
2312
+ }
2313
+ .filter-popover-bulk:hover { color: var(--accent); border-color: var(--accent); }
2314
+ .filter-popover-checkrow {
2315
+ display: flex; align-items: center; gap: 8px;
2316
+ padding: 3px 0;
2317
+ cursor: pointer;
2318
+ font-size: 12px;
2319
+ color: var(--ink);
2320
+ }
2321
+ .filter-popover-checkrow input { margin: 0; cursor: pointer; }
2322
+ .filter-popover-checkrow:hover { color: var(--accent); }
2323
+ .filter-popover-footer {
2324
+ display: flex; justify-content: flex-end;
2325
+ margin-top: 8px;
2326
+ padding-top: 8px;
2327
+ border-top: 1px dashed var(--rule);
2328
+ }
2329
+ .filter-popover-reset {
2330
+ appearance: none;
2331
+ background: transparent;
2332
+ border: none;
2333
+ font: inherit; font-size: 12px;
2334
+ color: var(--ink-soft);
2335
+ cursor: pointer;
2336
+ text-decoration: underline dotted;
2337
+ padding: 2px 4px;
2338
+ }
2339
+ .filter-popover-reset:hover { color: var(--danger, #B43C3C); }
2340
+
2341
+ /* ============================================================
2342
+ E11 — Kanban-Board
2343
+ ============================================================ */
2344
+
2345
+ /* SM-169 — full-bleed like the Story-Map (no page-edge margin); release rows
2346
+ stack flush, separated by the shared release-label-row borders. */
2347
+ #kanban-host { padding: 0; }
2348
+ .km-board {
2349
+ display: flex; flex-direction: column;
2350
+ }
2351
+ /* Per-release column header (repeats Backlog / Ready / … / Done inside every
2352
+ expanded swimlane). Shares the cells' grid template so columns line up, and
2353
+ the same nth-of-type stripes so the tinted bands run header→cards. */
2354
+ .km-col-header {
2355
+ display: grid;
2356
+ background: var(--paper);
2357
+ border-bottom: 1px solid var(--rule);
2358
+ }
2359
+ .km-col-head-cell {
2360
+ font-size: 12px;
2361
+ font-weight: 700;
2362
+ text-transform: uppercase;
2363
+ letter-spacing: 0.06em;
2364
+ color: var(--ink-soft);
2365
+ padding: 6px 12px;
2366
+ border-right: 1px solid var(--rule);
2367
+ }
2368
+ .km-col-head-cell:last-child { border-right: none; }
2369
+ .km-col-head-cell:nth-of-type(even) { background: #FBF6E8; }
2370
+ .km-col-head-cell.km-col-orphan { color: var(--danger, #7A3E26); }
2371
+
2372
+ /* SM-169: a swimlane stacks like the Story-Map's release rows — the header is
2373
+ the SHARED `.sm-release-label-row` component (chevron + name + pill, fully
2374
+ styled over in the Story-Map section), so no card chrome of its own. */
2375
+ .km-swimlane { background: transparent; }
2376
+ /* The count badge rides along in the shared header's `trailing` slot. Pushed
2377
+ to the right so the release name + pill stay left-aligned like the Map. */
2378
+ .km-swimlane .km-swimlane-count {
2379
+ margin-left: auto;
2380
+ font-family: ui-monospace, SFMono-Regular, monospace;
2381
+ font-size: 11px;
2382
+ color: var(--ink-faint);
2383
+ background: var(--rule-soft);
2384
+ border-radius: 3px;
2385
+ padding: 0 5px;
2386
+ }
2387
+ .km-swimlane-cells {
2388
+ display: grid;
2389
+ align-items: stretch;
2390
+ }
2391
+ /* Tinted column bands mirror the Story-Map's `.sm-cell` striping so the
2392
+ status columns read as distinct lanes (paper / warm) with a divider rule. */
2393
+ .km-cell {
2394
+ display: flex; flex-direction: column; gap: 6px;
2395
+ padding: 8px;
2396
+ min-height: 80px;
2397
+ background: var(--paper);
2398
+ border-right: 1px solid var(--rule);
2399
+ transition: background-color 120ms, box-shadow 120ms;
2400
+ }
2401
+ .km-cell:nth-of-type(even) { background: #FBF6E8; }
2402
+ .km-cell:last-child { border-right: none; }
2403
+ .km-cell:hover { background: var(--paper-warm); }
2404
+ .km-cell.km-cell-drop-target {
2405
+ background: rgba(15, 95, 79, 0.16);
2406
+ box-shadow: inset 0 0 0 2px var(--accent);
2407
+ }
2408
+
2409
+ /* Karten in der Kanban-Zelle sind die GLEICHEN `.sm-story-card`-Elemente
2410
+ wie in der Story-Map (renderer-card.js liefert beide Views). Hier nur
2411
+ eine kleine Anpassung: in der Zelle füllen sie die volle Breite. */
2412
+ .km-cell .sm-story-card {
2413
+ width: 100%;
2414
+ box-sizing: border-box;
2415
+ }
2416
+
2417
+ /* "+ Add Item" am Ende jeder Lane — Ghost-Variante, gleicher Stil wie
2418
+ die Per-Cell-Add-Buttons in der Story-Map (.sm-add-ticket). */
2419
+ .km-add-item {
2420
+ margin-top: auto;
2421
+ display: inline-flex;
2422
+ align-items: center;
2423
+ gap: 4px;
2424
+ align-self: stretch;
2425
+ padding: 5px 10px;
2426
+ font: inherit; font-size: 11px;
2427
+ color: var(--ink-faint);
2428
+ background: transparent;
2429
+ border: 1px dashed var(--rule);
2430
+ border-radius: 4px;
2431
+ cursor: pointer;
2432
+ transition: background 120ms, color 120ms, border-color 120ms;
2433
+ }
2434
+ .km-add-item:hover {
2435
+ background: var(--paper-warm);
2436
+ color: var(--ink);
2437
+ border-color: var(--ink-faint);
2438
+ border-style: solid;
2439
+ }
2440
+ .km-add-item .sm-add-icon {
2441
+ display: inline-flex;
2442
+ align-items: center; justify-content: center;
2443
+ width: 14px; height: 14px;
2444
+ background: var(--ink-faint);
2445
+ color: var(--paper);
2446
+ border-radius: 50%;
2447
+ font-size: 11px; line-height: 1; font-weight: 700;
2448
+ }
2449
+ .km-add-item:hover .sm-add-icon { background: var(--accent); }
2450
+ .km-add-item .sm-add-label { letter-spacing: 0.02em; }
2451
+
2452
+ /* SM-2/SM-3 — DoR/DoD editor inside the settings view (replaced the
2453
+ deprecated dialog version that used to live here, SM-3). */
2454
+ .vs-def-root { display: flex; flex-direction: column; gap: 14px; }
2455
+ .vs-def-block-title {
2456
+ font-family: var(--font-display, "Libre Caslon Text"), Georgia, serif;
2457
+ font-size: 15px; font-weight: 600;
2458
+ margin: 8px 0 4px;
2459
+ letter-spacing: -0.01em;
2460
+ }
2461
+ .vs-def-block { display: flex; flex-direction: column; gap: 4px; }
2462
+ .vs-def-block-title {
2463
+ font-size: 12px; font-weight: 600;
2464
+ color: var(--ink-soft);
2465
+ text-transform: uppercase; letter-spacing: 0.06em;
2466
+ margin: 4px 0 2px;
2467
+ }
2468
+ .vs-def-globals, .vs-def-per-type {
2469
+ display: flex; flex-direction: column; gap: 10px;
2470
+ }
2471
+ .vs-def-type-block {
2472
+ border: 1px solid var(--rule);
2473
+ border-radius: 4px;
2474
+ padding: 10px 12px;
2475
+ background: var(--paper-warm, #FAF7F0);
2476
+ }
2477
+ .vs-def-type-title {
2478
+ font-family: var(--font-display, "Libre Caslon Text"), Georgia, serif;
2479
+ font-size: 13px; font-weight: 600;
2480
+ margin: 0 0 6px;
2481
+ }
2482
+ .vs-def-gate-block { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--rule); }
2483
+ .vs-def-gate-block:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
2484
+ .vs-def-gate-header { display: flex; align-items: center; gap: 8px; }
2485
+ .vs-def-gate-toggle {
2486
+ display: inline-flex; align-items: center; gap: 6px;
2487
+ font-size: 12px;
2488
+ cursor: pointer;
2489
+ }
2490
+ .vs-def-gate-body {
2491
+ padding: 6px 0 0 22px;
2492
+ display: flex; flex-direction: column; gap: 6px;
2493
+ }
2494
+ .vs-def-mode-row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
2495
+ .vs-def-mode-label { color: var(--ink-soft); }
2496
+ .vs-def-items-list { display: flex; flex-direction: column; gap: 4px; }
2497
+ .vs-def-item-row {
2498
+ display: flex; align-items: center; gap: 6px;
2499
+ padding: 2px 4px;
2500
+ border-radius: 3px;
2501
+ transition: background 100ms, box-shadow 100ms;
2502
+ }
2503
+ .vs-def-item-row.sm-dragging { opacity: 0.4; }
2504
+ .vs-def-item-row.vs-def-item-drop-target {
2505
+ background: var(--paper);
2506
+ box-shadow: inset 0 2px 0 var(--accent);
2507
+ }
2508
+ .vs-def-item-grip {
2509
+ flex: 0 0 auto;
2510
+ color: var(--ink-faint);
2511
+ font-size: 11px;
2512
+ letter-spacing: -2px;
2513
+ cursor: grab;
2514
+ user-select: none;
2515
+ padding: 0 2px;
2516
+ }
2517
+ .vs-def-item-grip:active { cursor: grabbing; }
2518
+ .vs-def-item-required-wrap {
2519
+ display: inline-flex; align-items: center; gap: 3px;
2520
+ flex: 0 0 auto;
2521
+ font-size: 10px;
2522
+ color: var(--ink-soft);
2523
+ text-transform: uppercase; letter-spacing: 0.05em;
2524
+ cursor: pointer;
2525
+ user-select: none;
2526
+ }
2527
+ .vs-def-item-required-hint { font-size: 10px; }
2528
+ .vs-def-item-label {
2529
+ flex: 1;
2530
+ border: 1px solid var(--rule);
2531
+ border-radius: 3px;
2532
+ padding: 4px 6px;
2533
+ background: var(--paper);
2534
+ font: inherit; font-size: 12px;
2535
+ }
2536
+ .vs-def-item-label:focus { outline: none; border-color: var(--ink); }
2537
+ .vs-def-item-remove {
2538
+ flex: 0 0 auto;
2539
+ background: transparent;
2540
+ border: 1px solid var(--rule);
2541
+ border-radius: 3px;
2542
+ width: 22px; height: 22px;
2543
+ font-size: 14px;
2544
+ line-height: 1;
2545
+ color: var(--ink-faint);
2546
+ cursor: pointer;
2547
+ }
2548
+ .vs-def-item-remove:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
2549
+ .vs-def-add-item {
2550
+ align-self: flex-start;
2551
+ margin-top: 4px;
2552
+ padding: 3px 10px;
2553
+ font-size: 11px;
2554
+ color: var(--ink-faint);
2555
+ background: transparent;
2556
+ border: 1px dashed var(--rule);
2557
+ border-radius: 4px;
2558
+ cursor: pointer;
2559
+ }
2560
+ .vs-def-add-item:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
2561
+
2562
+ /* E19-followup — DoR/DoD per-Ticket-Editor (editable rows analog AC) */
2563
+ .tm-checklist-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
2564
+ .tm-checkitem-row {
2565
+ display: flex; align-items: center; gap: 6px;
2566
+ padding: 2px 4px;
2567
+ border-radius: 3px;
2568
+ transition: background 100ms, box-shadow 100ms;
2569
+ }
2570
+ .tm-checkitem-row.sm-dragging { opacity: 0.4; }
2571
+ .tm-checkitem-row.tm-checkitem-drop-target {
2572
+ background: var(--paper-warm);
2573
+ box-shadow: inset 0 2px 0 var(--accent);
2574
+ }
2575
+ .tm-checkitem-grip {
2576
+ flex: 0 0 auto;
2577
+ color: var(--ink-faint);
2578
+ font-size: 11px;
2579
+ letter-spacing: -2px;
2580
+ cursor: grab;
2581
+ user-select: none;
2582
+ padding: 0 2px;
2583
+ }
2584
+ .tm-checkitem-grip:active { cursor: grabbing; }
2585
+ .tm-checkitem-checked { flex: 0 0 auto; }
2586
+ .tm-checkitem-text-input { flex: 1; }
2587
+ .tm-checkitem-required-wrap {
2588
+ display: inline-flex; align-items: center; gap: 3px;
2589
+ flex: 0 0 auto;
2590
+ font-size: 10px;
2591
+ color: var(--ink-soft);
2592
+ text-transform: uppercase; letter-spacing: 0.05em;
2593
+ cursor: pointer;
2594
+ user-select: none;
2595
+ }
2596
+ .tm-checkitem-required-hint { font-size: 10px; }
2597
+ .tm-checkitem-remove {
2598
+ flex: 0 0 auto;
2599
+ background: transparent;
2600
+ border: 1px solid var(--rule);
2601
+ border-radius: 3px;
2602
+ width: 22px; height: 22px;
2603
+ font-size: 14px; line-height: 1;
2604
+ color: var(--ink-faint);
2605
+ cursor: pointer;
2606
+ }
2607
+ .tm-checkitem-remove:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
2608
+ .tm-checklist-add {
2609
+ display: inline-flex; align-items: center; gap: 4px;
2610
+ margin-top: 6px;
2611
+ padding: 4px 10px;
2612
+ font: inherit; font-size: 11px;
2613
+ color: var(--ink-faint);
2614
+ background: transparent;
2615
+ border: 1px dashed var(--rule);
2616
+ border-radius: 4px;
2617
+ cursor: pointer;
2618
+ }
2619
+ .tm-checklist-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
2620
+
2621
+ /* ---------------------------------------------------------------------------
2622
+ SM-54 — Prerequisites + Steps editor sections (test-definition type)
2623
+ Visually parallel to DoR/DoD (.tm-checkitem-row) so the editor feels
2624
+ uniform; Steps gets a 3-column grid because step / data / expected
2625
+ are three independent fields.
2626
+ --------------------------------------------------------------------------- */
2627
+
2628
+ .tm-prereqs { border-top: 1px dashed var(--rule); padding: 4px 0 0; }
2629
+ .tm-prereq-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
2630
+ .tm-prereq-row {
2631
+ display: flex; align-items: center; gap: 6px;
2632
+ padding: 2px 4px;
2633
+ border-radius: 3px;
2634
+ transition: background 100ms, box-shadow 100ms;
2635
+ }
2636
+ .tm-prereq-row.sm-dragging { opacity: 0.4; }
2637
+ .tm-prereq-row.tm-prereq-drop-target {
2638
+ background: var(--paper-warm);
2639
+ box-shadow: inset 0 2px 0 var(--accent);
2640
+ }
2641
+ .tm-prereq-grip {
2642
+ flex: 0 0 auto;
2643
+ color: var(--ink-faint);
2644
+ font-size: 11px;
2645
+ letter-spacing: -2px;
2646
+ cursor: grab;
2647
+ user-select: none;
2648
+ padding: 0 2px;
2649
+ }
2650
+ .tm-prereq-grip:active { cursor: grabbing; }
2651
+ .tm-prereq-checked { flex: 0 0 auto; }
2652
+ .tm-prereq-text-input { flex: 1; }
2653
+ .tm-prereq-required-wrap {
2654
+ display: inline-flex; align-items: center; gap: 3px;
2655
+ flex: 0 0 auto;
2656
+ font-size: 10px;
2657
+ color: var(--ink-soft);
2658
+ text-transform: uppercase; letter-spacing: 0.05em;
2659
+ cursor: pointer;
2660
+ user-select: none;
2661
+ }
2662
+ .tm-prereq-required-hint { font-size: 10px; }
2663
+ .tm-prereq-remove {
2664
+ flex: 0 0 auto;
2665
+ background: transparent;
2666
+ border: 1px solid var(--rule);
2667
+ border-radius: 3px;
2668
+ width: 22px; height: 22px;
2669
+ font-size: 14px; line-height: 1;
2670
+ color: var(--ink-faint);
2671
+ cursor: pointer;
2672
+ }
2673
+ .tm-prereq-remove:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
2674
+ .tm-prereq-add {
2675
+ display: inline-flex; align-items: center; gap: 4px;
2676
+ margin-top: 6px;
2677
+ padding: 4px 10px;
2678
+ font: inherit; font-size: 11px;
2679
+ color: var(--ink-faint);
2680
+ background: transparent;
2681
+ border: 1px dashed var(--rule);
2682
+ border-radius: 4px;
2683
+ cursor: pointer;
2684
+ }
2685
+ .tm-prereq-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
2686
+
2687
+ .tm-test-steps { border-top: 1px dashed var(--rule); padding: 4px 0 0; }
2688
+ /* Table header + rows share the SAME 6-column grid so labels sit exactly
2689
+ above their inputs: [grip] [Step] [Data] [Expected Result] [insert] [delete].
2690
+ SM-54-followup. */
2691
+ .tm-test-step-head {
2692
+ display: grid;
2693
+ grid-template-columns: 20px 1fr 1fr 1fr 26px 26px;
2694
+ gap: 6px;
2695
+ align-items: center;
2696
+ margin-top: 4px;
2697
+ padding: 2px 4px 4px;
2698
+ font-size: 10px;
2699
+ text-transform: uppercase;
2700
+ letter-spacing: 0.05em;
2701
+ color: var(--ink-soft);
2702
+ border-bottom: 1px solid var(--rule);
2703
+ }
2704
+ .tm-test-step-head-cell { text-align: left; }
2705
+ .tm-test-step-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
2706
+ .tm-test-step-row {
2707
+ display: grid;
2708
+ grid-template-columns: 20px 1fr 1fr 1fr 26px 26px;
2709
+ gap: 6px;
2710
+ /* Align tops so multi-line textareas grow downward without pushing the
2711
+ grip / button cells off-axis. */
2712
+ align-items: start;
2713
+ padding: 2px 4px;
2714
+ border-radius: 3px;
2715
+ transition: background 100ms, box-shadow 100ms;
2716
+ }
2717
+ .tm-test-step-row.sm-dragging { opacity: 0.4; }
2718
+ .tm-test-step-row.tm-test-step-drop-target {
2719
+ background: var(--paper-warm);
2720
+ box-shadow: inset 0 2px 0 var(--accent);
2721
+ }
2722
+ .tm-test-step-grip {
2723
+ color: var(--ink-faint);
2724
+ font-size: 11px;
2725
+ letter-spacing: -2px;
2726
+ cursor: grab;
2727
+ user-select: none;
2728
+ padding: 6px 2px 0; /* nudges down to align with first textarea row */
2729
+ }
2730
+ .tm-test-step-grip:active { cursor: grabbing; }
2731
+ /* Multi-line textareas, auto-grown by JS. min-height matches an input. */
2732
+ .tm-test-step-step,
2733
+ .tm-test-step-data,
2734
+ .tm-test-step-expected {
2735
+ width: 100%;
2736
+ min-height: 32px;
2737
+ font: inherit; font-size: 12px;
2738
+ padding: 4px 6px;
2739
+ border: 1px solid var(--rule);
2740
+ border-radius: 3px;
2741
+ background: var(--paper);
2742
+ resize: none; /* JS handles the resize via scrollHeight */
2743
+ line-height: 1.35;
2744
+ overflow-y: auto;
2745
+ box-sizing: border-box;
2746
+ }
2747
+ .tm-test-step-insert,
2748
+ .tm-test-step-remove {
2749
+ background: transparent;
2750
+ border: 1px solid var(--rule);
2751
+ border-radius: 3px;
2752
+ width: 22px; height: 22px;
2753
+ font-size: 12px; line-height: 1;
2754
+ color: var(--ink-faint);
2755
+ cursor: pointer;
2756
+ margin-top: 4px; /* tops with the textarea inner padding */
2757
+ padding: 0;
2758
+ }
2759
+ .tm-test-step-insert:hover { color: var(--accent); border-color: var(--accent); }
2760
+ .tm-test-step-remove:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
2761
+ .tm-test-step-add {
2762
+ display: inline-flex; align-items: center; gap: 4px;
2763
+ margin-top: 6px;
2764
+ padding: 4px 10px;
2765
+ font: inherit; font-size: 11px;
2766
+ color: var(--ink-faint);
2767
+ background: transparent;
2768
+ border: 1px dashed var(--rule);
2769
+ border-radius: 4px;
2770
+ cursor: pointer;
2771
+ }
2772
+ .tm-test-step-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
2773
+
2774
+ /* ---------------------------------------------------------------------------
2775
+ SM-57 — Test-Execution sections in ticket-detail modal
2776
+ --------------------------------------------------------------------------- */
2777
+
2778
+ /* Header pill: "executes → DEF-KEY: Title". Sits just under the description. */
2779
+ .tm-exec-header {
2780
+ margin: 6px 0 0;
2781
+ display: flex;
2782
+ align-items: center;
2783
+ }
2784
+ .tm-exec-header-pill {
2785
+ display: inline-flex; align-items: center; gap: 6px;
2786
+ background: var(--paper-warm);
2787
+ border: 1px solid var(--rule);
2788
+ border-radius: 999px;
2789
+ padding: 3px 10px;
2790
+ font-size: 12px;
2791
+ cursor: pointer;
2792
+ color: var(--ink);
2793
+ transition: background 120ms, border-color 120ms;
2794
+ }
2795
+ .tm-exec-header-pill:hover { background: var(--paper); border-color: var(--ink-faint); }
2796
+ .tm-exec-header-arrow { color: var(--ink-soft); font-size: 11px; }
2797
+ .tm-exec-header-key { font-weight: 600; }
2798
+ .tm-exec-header-title { color: var(--ink-soft); }
2799
+ .tm-exec-header-missing {
2800
+ color: var(--ink-faint);
2801
+ font-style: italic;
2802
+ font-size: 12px;
2803
+ }
2804
+
2805
+ /* Metadata strip: three small cells, label above value. */
2806
+ .tm-exec-meta {
2807
+ display: flex;
2808
+ gap: 18px;
2809
+ margin: 6px 0;
2810
+ padding: 6px 0;
2811
+ border-bottom: 1px dashed var(--rule);
2812
+ }
2813
+ .tm-exec-meta-cell { display: flex; flex-direction: column; gap: 1px; }
2814
+ .tm-exec-meta-label {
2815
+ font-size: 10px;
2816
+ text-transform: uppercase;
2817
+ letter-spacing: 0.05em;
2818
+ color: var(--ink-soft);
2819
+ }
2820
+ .tm-exec-meta-value { font-size: 12px; color: var(--ink); }
2821
+
2822
+ /* Execution-steps table: header + rows share a 6-column grid.
2823
+ [Step] [Data] [Expected] [Actual] [Status] [Note] */
2824
+ .tm-exec-steps { border-top: 1px dashed var(--rule); padding: 4px 0 0; margin-top: 8px; }
2825
+ .tm-exec-step-head {
2826
+ display: grid;
2827
+ grid-template-columns: 1fr 1fr 1fr 1fr 120px 1fr;
2828
+ gap: 6px;
2829
+ align-items: center;
2830
+ margin-top: 4px;
2831
+ padding: 2px 4px 4px;
2832
+ font-size: 10px;
2833
+ text-transform: uppercase;
2834
+ letter-spacing: 0.05em;
2835
+ color: var(--ink-soft);
2836
+ border-bottom: 1px solid var(--rule);
2837
+ }
2838
+ .tm-exec-step-head-cell { text-align: left; }
2839
+ .tm-exec-step-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
2840
+ .tm-exec-step-row {
2841
+ display: grid;
2842
+ grid-template-columns: 1fr 1fr 1fr 1fr 120px 1fr;
2843
+ gap: 6px;
2844
+ align-items: start;
2845
+ padding: 2px 4px;
2846
+ border-radius: 3px;
2847
+ }
2848
+ /* All step-row textareas share the multi-line look. The frozen ones
2849
+ (.tm-exec-step-ro) get a paper-warm background so it's obvious they
2850
+ can't be edited. */
2851
+ .tm-exec-step-step,
2852
+ .tm-exec-step-data,
2853
+ .tm-exec-step-expected,
2854
+ .tm-exec-step-actual,
2855
+ .tm-exec-step-note {
2856
+ width: 100%;
2857
+ min-height: 36px;
2858
+ font: inherit; font-size: 12px;
2859
+ padding: 4px 6px;
2860
+ border: 1px solid var(--rule);
2861
+ border-radius: 3px;
2862
+ background: var(--paper);
2863
+ resize: vertical;
2864
+ line-height: 1.35;
2865
+ box-sizing: border-box;
2866
+ }
2867
+ .tm-exec-step-ro {
2868
+ background: var(--paper-warm) !important;
2869
+ color: var(--ink-soft);
2870
+ cursor: not-allowed;
2871
+ }
2872
+ .tm-exec-step-status {
2873
+ font: inherit; font-size: 12px;
2874
+ padding: 4px 6px;
2875
+ border: 1px solid var(--rule);
2876
+ border-radius: 3px;
2877
+ background: var(--paper);
2878
+ width: 100%;
2879
+ height: 32px;
2880
+ box-sizing: border-box;
2881
+ }
2882
+ /* Status-coloured backgrounds: only on the picker itself, subtle so it
2883
+ doesn't fight the rest of the row. */
2884
+ .tm-exec-step-status[data-status="passed"] { background: rgba(34, 139, 34, 0.12); border-color: rgba(34, 139, 34, 0.5); }
2885
+ .tm-exec-step-status[data-status="failed"] { background: rgba(180, 60, 60, 0.12); border-color: rgba(180, 60, 60, 0.55); }
2886
+ .tm-exec-step-status[data-status="blocked"] { background: rgba(230, 180, 50, 0.18); border-color: rgba(180, 130, 30, 0.55); }
2887
+ .tm-exec-step-status[data-status="skipped"] { background: rgba(150, 150, 150, 0.14); border-color: rgba(120, 120, 120, 0.45); }
2888
+ .tm-exec-step-status[data-status="pending"] { background: var(--paper-warm); border-color: var(--rule); }
2889
+
2890
+ /* Outcome section: derived display + override picker. */
2891
+ .tm-exec-outcome { border-top: 1px dashed var(--rule); padding: 8px 0 0; margin-top: 8px; }
2892
+ .tm-exec-outcome-derived,
2893
+ .tm-exec-outcome-override-row {
2894
+ display: flex;
2895
+ align-items: center;
2896
+ gap: 8px;
2897
+ margin-top: 4px;
2898
+ font-size: 12px;
2899
+ }
2900
+ .tm-exec-outcome-derived-label {
2901
+ flex: 0 0 70px;
2902
+ font-size: 11px;
2903
+ color: var(--ink-soft);
2904
+ text-transform: uppercase;
2905
+ letter-spacing: 0.05em;
2906
+ }
2907
+ .tm-exec-outcome-pill {
2908
+ display: inline-block;
2909
+ padding: 2px 10px;
2910
+ border-radius: 999px;
2911
+ font-size: 12px; font-weight: 600;
2912
+ background: var(--paper-warm);
2913
+ border: 1px solid var(--rule);
2914
+ }
2915
+ .tm-exec-outcome-pill[data-outcome="passed"] { background: rgba(34, 139, 34, 0.16); border-color: rgba(34, 139, 34, 0.55); }
2916
+ .tm-exec-outcome-pill[data-outcome="failed"] { background: rgba(180, 60, 60, 0.16); border-color: rgba(180, 60, 60, 0.6); }
2917
+ .tm-exec-outcome-pill[data-outcome="blocked"] { background: rgba(230, 180, 50, 0.22); border-color: rgba(180, 130, 30, 0.6); }
2918
+ .tm-exec-outcome-pill[data-outcome="skipped"] { background: rgba(150, 150, 150, 0.18); border-color: rgba(120, 120, 120, 0.5); }
2919
+ .tm-exec-outcome-override {
2920
+ flex: 0 0 auto;
2921
+ font: inherit; font-size: 12px;
2922
+ padding: 4px 8px;
2923
+ border: 1px solid var(--rule);
2924
+ border-radius: 3px;
2925
+ background: var(--paper);
2926
+ }
2927
+ .tm-exec-outcome-override-hint {
2928
+ color: var(--accent);
2929
+ font-style: italic;
2930
+ font-size: 11px;
2931
+ }
2932
+
2933
+ /* ---------------------------------------------------------------------------
2934
+ SM-48 — Links section in ticket-detail modal
2935
+ --------------------------------------------------------------------------- */
2936
+
2937
+ .tm-links {
2938
+ margin-top: 16px;
2939
+ padding-top: 12px;
2940
+ border-top: 1px solid var(--rule);
2941
+ }
2942
+ .tm-links-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
2943
+ .tm-links-subhead {
2944
+ font-family: "Mona Sans", -apple-system, BlinkMacSystemFont, sans-serif;
2945
+ font-size: 11px;
2946
+ font-weight: 600;
2947
+ text-transform: uppercase;
2948
+ letter-spacing: 0.04em;
2949
+ color: var(--ink-soft);
2950
+ margin: 12px 0 4px;
2951
+ }
2952
+ .tm-link-row {
2953
+ display: grid;
2954
+ grid-template-columns: auto 1fr 28px;
2955
+ gap: 8px;
2956
+ align-items: center;
2957
+ padding: 4px 6px;
2958
+ border: 1px solid transparent;
2959
+ border-radius: 3px;
2960
+ }
2961
+ .tm-link-row:hover { background: var(--ink-faint-bg, #FAF8F2); border-color: var(--rule); }
2962
+ .tm-link-row-readonly { grid-template-columns: auto 1fr; }
2963
+ .tm-link-pill {
2964
+ display: inline-block;
2965
+ padding: 1px 8px;
2966
+ font-size: 11px;
2967
+ font-weight: 500;
2968
+ border-radius: 10px;
2969
+ background: var(--ink-faint-bg, #EFEAE0);
2970
+ color: var(--ink);
2971
+ white-space: nowrap;
2972
+ }
2973
+ .tm-link-pill-backward { font-style: italic; }
2974
+ .tm-link-target { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2975
+ .tm-link-key {
2976
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2977
+ font-size: 12px;
2978
+ color: var(--ink-soft);
2979
+ margin-right: 4px;
2980
+ }
2981
+ .tm-link-missing { color: var(--ink-faint); font-style: italic; }
2982
+ .tm-link-title { color: var(--ink); }
2983
+ .tm-link-delete {
2984
+ width: 24px; height: 24px;
2985
+ display: inline-flex; align-items: center; justify-content: center;
2986
+ font-size: 16px; line-height: 1;
2987
+ color: var(--ink-faint);
2988
+ background: transparent;
2989
+ border: 1px solid transparent;
2990
+ border-radius: 3px;
2991
+ cursor: pointer; padding: 0;
2992
+ }
2993
+ .tm-link-delete:hover { color: var(--danger, #B43C3C); border-color: var(--danger, #B43C3C); }
2994
+ .tm-links-empty {
2995
+ font-style: italic; font-size: 12px; color: var(--ink-faint); padding: 4px 2px;
2996
+ }
2997
+ .tm-link-add {
2998
+ margin-top: 6px;
2999
+ padding: 4px 10px;
3000
+ font: inherit; font-size: 11px;
3001
+ color: var(--ink-faint);
3002
+ background: transparent;
3003
+ border: 1px dashed var(--rule);
3004
+ border-radius: 4px;
3005
+ cursor: pointer;
3006
+ }
3007
+ .tm-link-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
3008
+
3009
+ /* SM-48 (combobox-inline): the Add-Link form lives INSIDE the ticket
3010
+ modal's Links section. No second modal — the ticket the user is editing
3011
+ stays visible. */
3012
+ .tm-link-add-wrap { margin-top: 6px; }
3013
+ .tm-link-add-form {
3014
+ display: flex; flex-direction: column; gap: 8px;
3015
+ padding: 8px 10px; margin-top: 4px;
3016
+ background: var(--paper-warm, #F7F2E8);
3017
+ border: 1px solid var(--rule);
3018
+ border-radius: 4px;
3019
+ }
3020
+ .tm-link-add-row { display: grid; grid-template-columns: 60px 1fr; gap: 10px; align-items: center; }
3021
+ .tm-link-add-row .tm-label { font-size: 12px; color: var(--ink-soft); margin: 0; }
3022
+ .tm-link-add-row select.tm-add-link-type {
3023
+ font: inherit; font-size: 13px;
3024
+ padding: 4px 22px 4px 8px;
3025
+ background: var(--paper);
3026
+ border: 1px solid var(--rule);
3027
+ border-radius: 3px;
3028
+ }
3029
+ .tm-link-combo { position: relative; }
3030
+ .tm-link-combo input.tm-add-link-filter {
3031
+ font: inherit; font-size: 13px;
3032
+ padding: 4px 8px;
3033
+ width: 100%;
3034
+ background: var(--paper);
3035
+ border: 1px solid var(--rule);
3036
+ border-radius: 3px;
3037
+ box-sizing: border-box;
3038
+ }
3039
+ .tm-link-combo-popover {
3040
+ position: absolute;
3041
+ top: calc(100% + 2px);
3042
+ left: 0; right: 0;
3043
+ max-height: 200px;
3044
+ overflow-y: auto;
3045
+ background: var(--paper);
3046
+ border: 1px solid var(--rule);
3047
+ border-radius: 3px;
3048
+ box-shadow: 0 4px 14px rgba(0,0,0,0.12);
3049
+ z-index: 5;
3050
+ }
3051
+ .tm-link-combo-option {
3052
+ padding: 4px 8px;
3053
+ font-size: 12px;
3054
+ color: var(--ink);
3055
+ cursor: pointer;
3056
+ }
3057
+ .tm-link-combo-option:hover { background: var(--ink-faint-bg, #F0EBE0); }
3058
+ .tm-link-add-actions { display: flex; gap: 6px; justify-content: flex-end; }
3059
+ .tm-link-add-actions .btn { font-size: 12px; padding: 4px 12px; }
3060
+ .tm-link-add-submit { background: var(--accent, #0F5F4F); color: var(--paper); border-color: var(--accent, #0F5F4F); }
3061
+ .tm-link-add-submit:hover { filter: brightness(0.93); }
3062
+
3063
+ /* ---------------------------------------------------------------------------
3064
+ E21.D — Settings view
3065
+ --------------------------------------------------------------------------- */
3066
+
3067
+ /* E23.B — Settings as an overlay over Map/Kanban (no longer a third view).
3068
+ Fixed full-screen, paper-tinted backdrop, close-X in the top-right. */
3069
+ #settings-host.vs-overlay {
3070
+ position: fixed;
3071
+ inset: 0;
3072
+ z-index: 90;
3073
+ padding: 24px 32px;
3074
+ overflow-y: auto;
3075
+ background: rgba(245,241,234,0.96);
3076
+ backdrop-filter: blur(2px);
3077
+ -webkit-backdrop-filter: blur(2px);
3078
+ }
3079
+ #settings-host[hidden] { display: none; }
3080
+ .vs-overlay-close {
3081
+ position: fixed;
3082
+ top: 16px;
3083
+ right: 20px;
3084
+ z-index: 91;
3085
+ width: 32px;
3086
+ height: 32px;
3087
+ font: inherit;
3088
+ font-size: 20px;
3089
+ line-height: 1;
3090
+ color: var(--ink-soft);
3091
+ background: var(--paper);
3092
+ border: 1px solid var(--rule);
3093
+ border-radius: 4px;
3094
+ cursor: pointer;
3095
+ }
3096
+ .vs-overlay-close:hover {
3097
+ color: var(--ink);
3098
+ border-color: var(--ink-faint);
3099
+ }
3100
+ .vs-overlay-content {
3101
+ /* Wide enough to host 5 board columns side-by-side at 200px each
3102
+ (5×200 + 4×12 gap = 1048px) with breathing room on the sides. */
3103
+ max-width: 1200px;
3104
+ margin: 0 auto;
3105
+ }
3106
+ .vs-root {
3107
+ /* Width is governed by the outer .vs-overlay-content (1200px). Don't
3108
+ re-cap it here or the board section can't show 5 columns inline. */
3109
+ display: flex;
3110
+ flex-direction: column;
3111
+ gap: 24px;
3112
+ }
3113
+
3114
+ /* SM-41: horizontal tab-bar at the top of .vs-root. Pattern is intentionally
3115
+ its own (.vs-tab-bar / .vs-tab-btn) and NOT shared with .view-toggle from
3116
+ the toolbar — different context, different concerns. Sticky so it stays
3117
+ visible when the user scrolls inside a long section. */
3118
+ .vs-tab-bar {
3119
+ display: flex;
3120
+ flex-direction: row;
3121
+ gap: 0;
3122
+ position: sticky;
3123
+ top: 0;
3124
+ z-index: 5;
3125
+ background: var(--paper);
3126
+ border-bottom: 1px solid var(--rule);
3127
+ padding: 4px 0;
3128
+ }
3129
+ .vs-tab-btn {
3130
+ font: inherit; font-size: 13px;
3131
+ color: var(--ink-soft);
3132
+ background: transparent;
3133
+ border: 0;
3134
+ padding: 8px 14px;
3135
+ cursor: pointer;
3136
+ border-bottom: 2px solid transparent;
3137
+ transition: color 120ms, border-color 120ms, background 120ms;
3138
+ }
3139
+ .vs-tab-btn:hover { color: var(--ink); background: var(--paper-warm); }
3140
+ .vs-tab-btn.active {
3141
+ color: var(--ink);
3142
+ border-bottom-color: var(--accent);
3143
+ font-weight: 600;
3144
+ }
3145
+ .vs-section {
3146
+ border: 1px solid var(--rule);
3147
+ border-radius: 6px;
3148
+ background: var(--paper);
3149
+ }
3150
+ /* SM-41: explicit hide for tab-inactive sections. The HTML5 `hidden`
3151
+ attribute should imply `display:none`, but in a flex-column container
3152
+ browsers occasionally still reserve flex-line space / gap for hidden
3153
+ children. Force display:none so only one card is visible at a time —
3154
+ anything else makes the tabs feel like anchor links instead of real
3155
+ tabs. */
3156
+ .vs-section[hidden] { display: none !important; }
3157
+ .vs-section-header {
3158
+ padding: 16px 20px 8px;
3159
+ border-bottom: 1px solid var(--rule);
3160
+ }
3161
+ .vs-section-header h2 {
3162
+ margin: 0 0 4px;
3163
+ font-family: "Libre Caslon Text", Georgia, serif;
3164
+ font-size: 18px;
3165
+ font-weight: 700;
3166
+ color: var(--ink);
3167
+ }
3168
+ .vs-section-hint {
3169
+ margin: 0;
3170
+ font-size: 12px;
3171
+ color: var(--ink-soft);
3172
+ }
3173
+ .vs-section-body {
3174
+ padding: 16px 20px;
3175
+ }
3176
+ .vs-placeholder {
3177
+ margin: 0;
3178
+ font-size: 12px;
3179
+ color: var(--ink-faint);
3180
+ font-style: italic;
3181
+ }
3182
+ .vs-empty {
3183
+ text-align: center;
3184
+ margin: 64px 0;
3185
+ font-size: 13px;
3186
+ color: var(--ink-soft);
3187
+ }
3188
+
3189
+ /* ---------------------------------------------------------------------------
3190
+ E21.E — Status editor in Settings view
3191
+ --------------------------------------------------------------------------- */
3192
+
3193
+ .vs-status-editor {
3194
+ display: flex;
3195
+ flex-direction: column;
3196
+ gap: 6px;
3197
+ }
3198
+ .vs-status-rows {
3199
+ display: flex;
3200
+ flex-direction: column;
3201
+ gap: 4px;
3202
+ }
3203
+ .vs-status-row {
3204
+ display: grid;
3205
+ grid-template-columns: 20px 1fr 160px 28px;
3206
+ align-items: center;
3207
+ gap: 12px;
3208
+ padding: 4px 6px;
3209
+ border: 1px solid transparent;
3210
+ border-radius: 4px;
3211
+ }
3212
+ .vs-status-row:hover { background: var(--ink-faint-bg, #FAF8F2); border-color: var(--rule); }
3213
+ .vs-status-row.vs-status-drop-target { box-shadow: inset 0 3px 0 var(--accent); }
3214
+ .vs-status-row.sm-dragging { opacity: 0.4; }
3215
+ .vs-status-row .vs-grip {
3216
+ color: var(--ink-faint);
3217
+ cursor: grab;
3218
+ user-select: none;
3219
+ font-size: 12px;
3220
+ text-align: center;
3221
+ }
3222
+ .vs-status-row input.vs-status-name {
3223
+ font: inherit;
3224
+ font-size: 13px;
3225
+ padding: 4px 8px;
3226
+ border: 1px solid var(--rule);
3227
+ border-radius: 3px;
3228
+ background: transparent;
3229
+ color: var(--ink);
3230
+ }
3231
+ .vs-status-row input.vs-status-name:focus {
3232
+ outline: none;
3233
+ border-color: var(--accent);
3234
+ background: var(--paper);
3235
+ }
3236
+ .vs-status-row select.vs-status-category {
3237
+ font: inherit;
3238
+ font-size: 12px;
3239
+ padding: 3px 24px 3px 8px;
3240
+ border: 1px solid var(--rule);
3241
+ border-radius: 3px;
3242
+ background: var(--paper);
3243
+ color: var(--ink-soft);
3244
+ text-transform: capitalize;
3245
+ appearance: auto;
3246
+ }
3247
+ .vs-status-row button.vs-status-delete {
3248
+ font: inherit;
3249
+ font-size: 16px;
3250
+ line-height: 1;
3251
+ width: 24px;
3252
+ height: 24px;
3253
+ color: var(--ink-faint);
3254
+ background: transparent;
3255
+ border: 1px solid transparent;
3256
+ border-radius: 3px;
3257
+ cursor: pointer;
3258
+ }
3259
+ .vs-status-row button.vs-status-delete:hover {
3260
+ color: var(--danger, #B43C3C);
3261
+ border-color: var(--danger, #B43C3C);
3262
+ }
3263
+ .vs-status-add {
3264
+ align-self: flex-start;
3265
+ display: inline-flex;
3266
+ align-items: center;
3267
+ gap: 4px;
3268
+ margin-top: 6px;
3269
+ padding: 4px 10px;
3270
+ font: inherit;
3271
+ font-size: 11px;
3272
+ color: var(--ink-faint);
3273
+ background: transparent;
3274
+ border: 1px dashed var(--rule);
3275
+ border-radius: 4px;
3276
+ cursor: pointer;
3277
+ }
3278
+ .vs-status-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
3279
+
3280
+ /* ---------------------------------------------------------------------------
3281
+ E21.F — Transition editor in Settings view
3282
+ --------------------------------------------------------------------------- */
3283
+
3284
+ .vs-subhead {
3285
+ font-family: "Mona Sans", -apple-system, BlinkMacSystemFont, sans-serif;
3286
+ font-size: 12px;
3287
+ font-weight: 600;
3288
+ text-transform: uppercase;
3289
+ letter-spacing: 0.04em;
3290
+ color: var(--ink-soft);
3291
+ margin: 20px 0 8px;
3292
+ padding-bottom: 4px;
3293
+ border-bottom: 1px solid var(--rule);
3294
+ }
3295
+ .vs-subhead:first-child { margin-top: 0; }
3296
+
3297
+ /* SM-107 — read-only "Rules per type" list */
3298
+ .vs-rules-type { margin-bottom: 18px; }
3299
+ .vs-rules-list { list-style: none; margin: 0; padding: 0; }
3300
+ .vs-rules-item {
3301
+ display: flex;
3302
+ align-items: baseline;
3303
+ gap: 8px;
3304
+ padding: 4px 0;
3305
+ font-size: 13px;
3306
+ }
3307
+ .vs-rules-item.inactive { color: var(--ink-soft); opacity: 0.7; }
3308
+ .vs-rules-state { width: 1em; text-align: center; flex: 0 0 auto; }
3309
+ .vs-rules-item.active .vs-rules-state { color: var(--accent); }
3310
+ .vs-rules-id {
3311
+ font-size: 11px;
3312
+ color: var(--ink-soft);
3313
+ background: var(--paper-dim, rgba(0,0,0,0.04));
3314
+ padding: 1px 5px;
3315
+ border-radius: 3px;
3316
+ flex: 0 0 auto;
3317
+ }
3318
+ .vs-rules-label { flex: 1 1 auto; }
3319
+ .vs-rules-na {
3320
+ font-size: 11px;
3321
+ font-style: italic;
3322
+ color: var(--ink-soft);
3323
+ flex: 0 0 auto;
3324
+ }
3325
+
3326
+ .vs-transition-editor {
3327
+ display: flex;
3328
+ flex-direction: column;
3329
+ gap: 6px;
3330
+ }
3331
+ .vs-transition-rows {
3332
+ display: flex;
3333
+ flex-direction: column;
3334
+ gap: 8px;
3335
+ }
3336
+ .vs-transition-row {
3337
+ display: flex;
3338
+ flex-direction: column;
3339
+ gap: 6px;
3340
+ padding: 8px 10px;
3341
+ border: 1px solid var(--rule);
3342
+ border-radius: 4px;
3343
+ background: var(--paper);
3344
+ }
3345
+ .vs-transition-row:hover { border-color: var(--ink-faint); }
3346
+ .vs-transition-row.vs-transition-drop-target { box-shadow: inset 0 3px 0 var(--accent); }
3347
+ .vs-transition-row.sm-dragging { opacity: 0.4; }
3348
+
3349
+ .vs-transition-header {
3350
+ display: grid;
3351
+ grid-template-columns: 20px 1fr 28px;
3352
+ align-items: center;
3353
+ gap: 8px;
3354
+ }
3355
+ .vs-transition-header .vs-grip {
3356
+ color: var(--ink-faint);
3357
+ cursor: grab;
3358
+ user-select: none;
3359
+ text-align: center;
3360
+ font-size: 12px;
3361
+ }
3362
+ .vs-transition-row input.vs-transition-name {
3363
+ font: inherit;
3364
+ font-size: 13px;
3365
+ padding: 4px 8px;
3366
+ border: 1px solid var(--rule);
3367
+ border-radius: 3px;
3368
+ background: transparent;
3369
+ color: var(--ink);
3370
+ }
3371
+ .vs-transition-row input.vs-transition-name:focus {
3372
+ outline: none;
3373
+ border-color: var(--accent);
3374
+ background: var(--paper);
3375
+ }
3376
+ .vs-transition-row button.vs-transition-delete {
3377
+ font: inherit;
3378
+ font-size: 16px;
3379
+ line-height: 1;
3380
+ width: 24px; height: 24px;
3381
+ color: var(--ink-faint);
3382
+ background: transparent;
3383
+ border: 1px solid transparent;
3384
+ border-radius: 3px;
3385
+ cursor: pointer;
3386
+ }
3387
+ .vs-transition-row button.vs-transition-delete:hover {
3388
+ color: var(--danger, #B43C3C);
3389
+ border-color: var(--danger, #B43C3C);
3390
+ }
3391
+
3392
+ .vs-transition-body {
3393
+ display: flex;
3394
+ align-items: center;
3395
+ flex-wrap: wrap;
3396
+ gap: 8px;
3397
+ padding-left: 28px; /* align with name input after grip */
3398
+ font-size: 12px;
3399
+ color: var(--ink-soft);
3400
+ }
3401
+ .vs-transition-arrow { font-size: 14px; color: var(--ink-faint); }
3402
+ .vs-transition-row select.vs-transition-target,
3403
+ .vs-transition-row select.vs-transition-gate {
3404
+ font: inherit;
3405
+ font-size: 12px;
3406
+ padding: 3px 24px 3px 8px;
3407
+ border: 1px solid var(--rule);
3408
+ border-radius: 3px;
3409
+ background: var(--paper);
3410
+ color: var(--ink);
3411
+ appearance: auto;
3412
+ }
3413
+ .vs-transition-gate-label { color: var(--ink-faint); }
3414
+
3415
+ .vs-transition-any-wrap {
3416
+ display: inline-flex;
3417
+ align-items: center;
3418
+ gap: 4px;
3419
+ margin-left: auto;
3420
+ font-size: 12px;
3421
+ color: var(--ink-soft);
3422
+ cursor: pointer;
3423
+ }
3424
+ .vs-transition-any-wrap input[type="checkbox"] { accent-color: var(--accent); }
3425
+
3426
+ .vs-transition-sources {
3427
+ padding-left: 28px;
3428
+ display: flex;
3429
+ flex-wrap: wrap;
3430
+ gap: 4px 12px;
3431
+ font-size: 12px;
3432
+ color: var(--ink-soft);
3433
+ }
3434
+ .vs-transition-sources[hidden] { display: none; }
3435
+ .vs-transition-sources .vs-transition-source-item {
3436
+ display: inline-flex;
3437
+ align-items: center;
3438
+ gap: 4px;
3439
+ cursor: pointer;
3440
+ }
3441
+ .vs-transition-sources input[type="checkbox"] { accent-color: var(--accent); }
3442
+
3443
+ .vs-transition-add {
3444
+ align-self: flex-start;
3445
+ display: inline-flex;
3446
+ align-items: center;
3447
+ gap: 4px;
3448
+ margin-top: 6px;
3449
+ padding: 4px 10px;
3450
+ font: inherit;
3451
+ font-size: 11px;
3452
+ color: var(--ink-faint);
3453
+ background: transparent;
3454
+ border: 1px dashed var(--rule);
3455
+ border-radius: 4px;
3456
+ cursor: pointer;
3457
+ }
3458
+ .vs-transition-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
3459
+
3460
+ /* ---------------------------------------------------------------------------
3461
+ E21.G — Board (Kanban-Column) editor in Settings view
3462
+ --------------------------------------------------------------------------- */
3463
+
3464
+ .vs-board-editor {
3465
+ display: flex;
3466
+ flex-direction: column;
3467
+ gap: 16px;
3468
+ }
3469
+ .vs-board-unassigned {
3470
+ padding: 10px 12px;
3471
+ border: 1px dashed var(--rule);
3472
+ border-radius: 4px;
3473
+ background: rgba(218,215,209,0.18);
3474
+ }
3475
+ .vs-board-unassigned-label {
3476
+ font-size: 11px;
3477
+ text-transform: uppercase;
3478
+ letter-spacing: 0.04em;
3479
+ color: var(--ink-soft);
3480
+ margin-bottom: 6px;
3481
+ }
3482
+ .vs-board-unassigned.vs-board-drop-target {
3483
+ box-shadow: inset 0 0 0 2px var(--accent);
3484
+ background: var(--accent-soft, rgba(15,95,79,0.06));
3485
+ }
3486
+
3487
+ .vs-board-columns {
3488
+ /* Wrap columns at fixed 200px so a row that breaks (e.g. 6th column on
3489
+ a 1200px-wide overlay) doesn't stretch to fill the remaining row —
3490
+ it keeps the same lane width as every sibling. */
3491
+ display: flex;
3492
+ gap: 12px;
3493
+ flex-wrap: wrap;
3494
+ /* Pin row direction so the start-aligned items wrap cleanly. */
3495
+ align-items: flex-start;
3496
+ }
3497
+ .vs-board-column {
3498
+ flex: 0 0 200px; /* don't grow, don't shrink — uniform lane width */
3499
+ width: 200px;
3500
+ display: flex;
3501
+ flex-direction: column;
3502
+ gap: 8px;
3503
+ padding: 10px;
3504
+ border: 1px solid var(--rule);
3505
+ border-radius: 4px;
3506
+ background: var(--paper);
3507
+ }
3508
+ .vs-board-column.vs-board-drop-target {
3509
+ border-color: var(--accent);
3510
+ box-shadow: inset 0 0 0 1px var(--accent);
3511
+ }
3512
+ .vs-board-column-header {
3513
+ display: grid;
3514
+ /* minmax(0, 1fr) prevents the input's implicit min-content size from
3515
+ pushing the grid track wider than the lane — without it, a long
3516
+ status name in the input made the delete X overflow the card. */
3517
+ grid-template-columns: minmax(0, 1fr) 24px;
3518
+ gap: 8px;
3519
+ align-items: center;
3520
+ }
3521
+ .vs-board-column input.vs-board-column-name {
3522
+ font: inherit;
3523
+ font-size: 13px;
3524
+ font-weight: 600;
3525
+ padding: 4px 8px;
3526
+ border: 1px solid var(--rule);
3527
+ border-radius: 3px;
3528
+ background: transparent;
3529
+ color: var(--ink);
3530
+ /* Belt-and-suspenders with the grid track above: tell the input it's
3531
+ OK to be narrower than its content. */
3532
+ min-width: 0;
3533
+ width: 100%;
3534
+ }
3535
+ .vs-board-column input.vs-board-column-name:focus {
3536
+ outline: none;
3537
+ border-color: var(--accent);
3538
+ background: var(--paper);
3539
+ }
3540
+ .vs-board-column button.vs-board-column-delete {
3541
+ font: inherit;
3542
+ font-size: 14px;
3543
+ line-height: 1;
3544
+ width: 22px;
3545
+ height: 22px;
3546
+ color: var(--ink-faint);
3547
+ background: transparent;
3548
+ border: 1px solid transparent;
3549
+ border-radius: 3px;
3550
+ cursor: pointer;
3551
+ }
3552
+ .vs-board-column button.vs-board-column-delete:hover {
3553
+ color: var(--danger, #B43C3C);
3554
+ border-color: var(--danger, #B43C3C);
3555
+ }
3556
+
3557
+ .vs-board-chips {
3558
+ display: flex;
3559
+ flex-wrap: wrap;
3560
+ gap: 4px;
3561
+ min-height: 30px;
3562
+ padding: 4px;
3563
+ border: 1px dashed transparent;
3564
+ border-radius: 3px;
3565
+ }
3566
+ .vs-board-column .vs-board-chips:empty::before {
3567
+ content: "Drop a status here";
3568
+ color: var(--ink-faint);
3569
+ font-style: italic;
3570
+ font-size: 11px;
3571
+ padding: 2px 4px;
3572
+ }
3573
+ .vs-board-chip {
3574
+ display: inline-flex;
3575
+ align-items: center;
3576
+ padding: 3px 8px;
3577
+ font-size: 11px;
3578
+ font-weight: 500;
3579
+ color: var(--ink);
3580
+ background: var(--paper-warm);
3581
+ border: 1px solid var(--rule);
3582
+ border-radius: 999px;
3583
+ cursor: grab;
3584
+ }
3585
+ .vs-board-chip.sm-dragging { opacity: 0.4; }
3586
+ .vs-board-empty {
3587
+ font-style: italic;
3588
+ font-size: 11px;
3589
+ color: var(--ink-faint);
3590
+ padding: 2px 4px;
3591
+ }
3592
+
3593
+ .vs-board-add-column {
3594
+ align-self: flex-start;
3595
+ display: inline-flex;
3596
+ align-items: center;
3597
+ gap: 4px;
3598
+ padding: 4px 10px;
3599
+ font: inherit;
3600
+ font-size: 11px;
3601
+ color: var(--ink-faint);
3602
+ background: transparent;
3603
+ border: 1px dashed var(--rule);
3604
+ border-radius: 4px;
3605
+ cursor: pointer;
3606
+ }
3607
+ .vs-board-add-column:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
3608
+
3609
+ /* ---------------------------------------------------------------------------
3610
+ E24 — card animations for MCP/WS-driven changes (FLIP + pulse)
3611
+ --------------------------------------------------------------------------- */
3612
+
3613
+ /* Pulse: brief accent-toned glow on cards whose non-position fields just
3614
+ changed via an external commit (MCP, second browser, …). Matches the
3615
+ --accent green so it stays on-theme; rendered with box-shadow so it
3616
+ doesn't shift layout. */
3617
+ @keyframes sm-card-flash {
3618
+ 0% { box-shadow: 0 0 0 0 rgba(15, 95, 79, 0.0); }
3619
+ 20% { box-shadow: 0 0 0 6px rgba(15, 95, 79, 0.35); }
3620
+ 100% { box-shadow: 0 0 0 12px rgba(15, 95, 79, 0.0); }
3621
+ }
3622
+ /* SM-20: the pulse must reach every entity type that the Story-Map renderer
3623
+ registers for animation (tickets/epics/process-steps/releases). Until this
3624
+ was widened, only `.sm-story-card.sm-card-flash` triggered the keyframe,
3625
+ so MCP-driven moves on epic cards, backbone columns, and release rows
3626
+ looked like instant rerenders. The .sm-card-flash class is the single
3627
+ write-target the card-animate helper uses; binding the keyframe to it
3628
+ generically covers everything. */
3629
+ .sm-card-flash {
3630
+ animation: sm-card-flash 900ms ease-out;
3631
+ }
3632
+
3633
+ /* The FLIP helper sets inline `transform: translate(...)` + `transition`,
3634
+ so no CSS rule is needed beyond willChange for performance — but we
3635
+ don't add will-change globally to avoid GPU layer thrash. */
3636
+
3637
+ /* ---------------------------------------------------------------------------
3638
+ SM-47 — Link-Types editor in Settings view
3639
+ --------------------------------------------------------------------------- */
3640
+
3641
+ .vs-linktypes-editor {
3642
+ display: flex;
3643
+ flex-direction: column;
3644
+ gap: 4px;
3645
+ }
3646
+
3647
+ .vs-linktype-row {
3648
+ display: grid;
3649
+ grid-template-columns: 140px 1fr 1fr 140px 36px 28px;
3650
+ align-items: center;
3651
+ gap: 12px;
3652
+ padding: 6px 8px;
3653
+ border: 1px solid transparent;
3654
+ border-radius: 4px;
3655
+ }
3656
+ .vs-linktype-row:hover { background: var(--ink-faint-bg, #FAF8F2); border-color: var(--rule); }
3657
+ .vs-linktype-row.vs-linktype-head:hover { background: transparent; border-color: transparent; }
3658
+ .vs-linktype-head {
3659
+ font-family: "Mona Sans", -apple-system, BlinkMacSystemFont, sans-serif;
3660
+ font-size: 10px;
3661
+ text-transform: uppercase;
3662
+ letter-spacing: 0.04em;
3663
+ color: var(--ink-soft);
3664
+ padding-bottom: 2px;
3665
+ }
3666
+ .vs-linktype-head .vs-linktype-id { color: var(--ink-soft); cursor: default; }
3667
+ .vs-linktype-id {
3668
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
3669
+ font-size: 11px;
3670
+ color: var(--ink-soft);
3671
+ overflow: hidden;
3672
+ text-overflow: ellipsis;
3673
+ white-space: nowrap;
3674
+ cursor: help;
3675
+ }
3676
+ .vs-linktype-row input.vs-linktype-label,
3677
+ .vs-linktype-row input.vs-linktype-inverse {
3678
+ font: inherit;
3679
+ font-size: 13px;
3680
+ padding: 4px 8px;
3681
+ border: 1px solid transparent;
3682
+ border-radius: 3px;
3683
+ background: transparent;
3684
+ color: var(--ink);
3685
+ width: 100%;
3686
+ box-sizing: border-box;
3687
+ }
3688
+ .vs-linktype-row input.vs-linktype-label:hover,
3689
+ .vs-linktype-row input.vs-linktype-inverse:hover { border-color: var(--rule); }
3690
+ .vs-linktype-row input.vs-linktype-label:focus,
3691
+ .vs-linktype-row input.vs-linktype-inverse:focus {
3692
+ outline: none;
3693
+ border-color: var(--accent);
3694
+ background: var(--paper);
3695
+ }
3696
+ .vs-linktype-row select.vs-linktype-semantic {
3697
+ font: inherit;
3698
+ font-size: 12px;
3699
+ padding: 4px 24px 4px 8px;
3700
+ background: var(--paper);
3701
+ border: 1px solid var(--rule);
3702
+ border-radius: 3px;
3703
+ color: var(--ink);
3704
+ }
3705
+ .vs-linktype-row input.vs-linktype-color {
3706
+ width: 32px;
3707
+ height: 24px;
3708
+ padding: 0;
3709
+ border: 1px solid var(--rule);
3710
+ border-radius: 3px;
3711
+ background: transparent;
3712
+ cursor: pointer;
3713
+ }
3714
+ .vs-linktype-row button.vs-linktype-delete {
3715
+ width: 24px;
3716
+ height: 24px;
3717
+ display: inline-flex;
3718
+ align-items: center;
3719
+ justify-content: center;
3720
+ font-size: 16px;
3721
+ line-height: 1;
3722
+ color: var(--ink-faint);
3723
+ background: transparent;
3724
+ border: 1px solid transparent;
3725
+ border-radius: 3px;
3726
+ cursor: pointer;
3727
+ padding: 0;
3728
+ }
3729
+ .vs-linktype-row button.vs-linktype-delete:hover {
3730
+ color: var(--danger, #B33A3A);
3731
+ border-color: var(--danger, #B33A3A);
3732
+ }
3733
+ .vs-linktype-col-label { font: inherit; }
3734
+
3735
+ .vs-linktype-add-wrap { margin-top: 8px; }
3736
+ .vs-linktype-add {
3737
+ display: inline-flex;
3738
+ align-items: center;
3739
+ gap: 4px;
3740
+ padding: 4px 10px;
3741
+ font: inherit;
3742
+ font-size: 11px;
3743
+ color: var(--ink-faint);
3744
+ background: transparent;
3745
+ border: 1px dashed var(--rule);
3746
+ border-radius: 4px;
3747
+ cursor: pointer;
3748
+ }
3749
+ .vs-linktype-add:hover { color: var(--ink); border-style: solid; border-color: var(--ink-faint); }
3750
+
3751
+ /* ---------------------------------------------------------------------------
3752
+ SM-7 — Ticket-Types editor in Settings view
3753
+ --------------------------------------------------------------------------- */
3754
+
3755
+ .vs-tickettypes-editor { display: flex; flex-direction: column; gap: 4px; }
3756
+ .vs-tickettype-rows { display: flex; flex-direction: column; gap: 4px; }
3757
+ .vs-tickettype-row {
3758
+ display: grid;
3759
+ grid-template-columns: 20px 1fr 28px;
3760
+ align-items: center;
3761
+ gap: 12px;
3762
+ padding: 6px 8px;
3763
+ border: 1px solid transparent;
3764
+ border-radius: 4px;
3765
+ }
3766
+ .vs-tickettype-row:hover { background: var(--ink-faint-bg, #FAF8F2); border-color: var(--rule); }
3767
+ .vs-tickettype-row.vs-tickettype-drop-target { box-shadow: inset 0 3px 0 var(--accent); }
3768
+ .vs-tickettype-row.sm-dragging { opacity: 0.4; }
3769
+ .vs-tickettype-row input.vs-tickettype-name {
3770
+ font: inherit;
3771
+ font-size: 13px;
3772
+ padding: 4px 8px;
3773
+ border: 1px solid transparent;
3774
+ border-radius: 3px;
3775
+ background: transparent;
3776
+ color: var(--ink);
3777
+ }
3778
+ .vs-tickettype-row input.vs-tickettype-name:hover { border-color: var(--rule); }
3779
+ .vs-tickettype-row input.vs-tickettype-name:focus {
3780
+ outline: none; border-color: var(--accent); background: var(--paper);
3781
+ }
3782
+ .vs-tickettype-row button.vs-tickettype-delete {
3783
+ width: 24px; height: 24px;
3784
+ display: inline-flex; align-items: center; justify-content: center;
3785
+ font-size: 16px; line-height: 1;
3786
+ color: var(--ink-faint);
3787
+ background: transparent;
3788
+ border: 1px solid transparent;
3789
+ border-radius: 3px;
3790
+ cursor: pointer; padding: 0;
3791
+ }
3792
+ .vs-tickettype-row button.vs-tickettype-delete:hover {
3793
+ color: var(--danger, #B33A3A); border-color: var(--danger, #B33A3A);
3794
+ }
3795
+ .vs-tickettype-add, .vs-label-add {
3796
+ align-self: flex-start;
3797
+ display: inline-flex; align-items: center; gap: 4px;
3798
+ margin-top: 8px; padding: 4px 10px;
3799
+ font: inherit; font-size: 11px;
3800
+ color: var(--ink-faint);
3801
+ background: transparent;
3802
+ border: 1px dashed var(--rule);
3803
+ border-radius: 4px;
3804
+ cursor: pointer;
3805
+ }
3806
+ .vs-tickettype-add:hover, .vs-label-add:hover {
3807
+ color: var(--ink); border-style: solid; border-color: var(--ink-faint);
3808
+ }
3809
+
3810
+ /* ---------------------------------------------------------------------------
3811
+ SM-8 — Labels editor in Settings view
3812
+ --------------------------------------------------------------------------- */
3813
+
3814
+ .vs-labels-editor { display: flex; flex-direction: column; gap: 4px; }
3815
+ .vs-label-rows { display: flex; flex-direction: column; gap: 4px; }
3816
+ .vs-label-row {
3817
+ display: grid;
3818
+ grid-template-columns: 20px 18px 1fr 36px 28px;
3819
+ align-items: center;
3820
+ gap: 10px;
3821
+ padding: 6px 8px;
3822
+ border: 1px solid transparent;
3823
+ border-radius: 4px;
3824
+ }
3825
+ .vs-label-row:hover { background: var(--ink-faint-bg, #FAF8F2); border-color: var(--rule); }
3826
+ .vs-label-row.vs-label-drop-target { box-shadow: inset 0 3px 0 var(--accent); }
3827
+ .vs-label-row.sm-dragging { opacity: 0.4; }
3828
+ .vs-label-swatch {
3829
+ width: 14px; height: 14px;
3830
+ border-radius: 50%;
3831
+ border: 1px solid var(--rule);
3832
+ display: inline-block;
3833
+ }
3834
+ .vs-label-row input.vs-label-name {
3835
+ font: inherit; font-size: 13px;
3836
+ padding: 4px 8px;
3837
+ border: 1px solid transparent;
3838
+ border-radius: 3px;
3839
+ background: transparent;
3840
+ color: var(--ink);
3841
+ }
3842
+ .vs-label-row input.vs-label-name:hover { border-color: var(--rule); }
3843
+ .vs-label-row input.vs-label-name:focus {
3844
+ outline: none; border-color: var(--accent); background: var(--paper);
3845
+ }
3846
+ .vs-label-row input.vs-label-color {
3847
+ width: 32px; height: 24px;
3848
+ padding: 0;
3849
+ border: 1px solid var(--rule);
3850
+ border-radius: 3px;
3851
+ background: transparent;
3852
+ cursor: pointer;
3853
+ }
3854
+ .vs-label-row button.vs-label-delete {
3855
+ width: 24px; height: 24px;
3856
+ display: inline-flex; align-items: center; justify-content: center;
3857
+ font-size: 16px; line-height: 1;
3858
+ color: var(--ink-faint);
3859
+ background: transparent;
3860
+ border: 1px solid transparent;
3861
+ border-radius: 3px;
3862
+ cursor: pointer; padding: 0;
3863
+ }
3864
+ .vs-label-row button.vs-label-delete:hover {
3865
+ color: var(--danger, #B33A3A); border-color: var(--danger, #B33A3A);
3866
+ }
3867
+
3868
+ .vs-empty { color: var(--ink-faint); font-style: italic; padding: 6px 2px; font-size: 12px; }
3869
+
3870
+ /* ============================================================
3871
+ SM-50 — Dependencies View
3872
+ ============================================================ */
3873
+
3874
+ #deps-host {
3875
+ position: relative;
3876
+ width: 100%;
3877
+ height: 100%;
3878
+ overflow: auto;
3879
+ background: var(--paper);
3880
+ }
3881
+ .deps-root {
3882
+ display: flex;
3883
+ flex-direction: column;
3884
+ min-height: 100%;
3885
+ }
3886
+ .deps-toolbar {
3887
+ display: flex;
3888
+ gap: 10px;
3889
+ align-items: center;
3890
+ padding: 10px 14px;
3891
+ border-bottom: 1px solid var(--rule);
3892
+ background: var(--paper-warm, var(--paper));
3893
+ position: sticky;
3894
+ top: 0;
3895
+ z-index: 2;
3896
+ }
3897
+ .deps-toolbar select,
3898
+ .deps-toolbar input[type="search"] {
3899
+ font: inherit;
3900
+ font-size: 12px;
3901
+ padding: 4px 8px;
3902
+ border: 1px solid var(--rule);
3903
+ border-radius: 4px;
3904
+ background: var(--paper);
3905
+ color: var(--ink);
3906
+ }
3907
+ .deps-toolbar input[type="search"] {
3908
+ min-width: 220px;
3909
+ }
3910
+ .deps-canvas {
3911
+ flex: 1;
3912
+ overflow: auto;
3913
+ padding: 0;
3914
+ position: relative; /* SM-161: coordinate root for SVG edges + HTML nodes */
3915
+ }
3916
+ .deps-svg {
3917
+ display: block;
3918
+ font-family: inherit;
3919
+ }
3920
+
3921
+ /* ----- Nodes: the SHARED story-cards (SM-161) --------------- */
3922
+ /* The .deps-node wrapper is absolutely positioned (inline left/top/width) and
3923
+ carries the hover/critical/band hooks; the .sm-story-card inside provides the
3924
+ visuals (cluster border, status pill, badges) + the ticket-key editor link. */
3925
+ .deps-node {
3926
+ transition: opacity 120ms ease, filter 120ms ease;
3927
+ }
3928
+ .deps-node .sm-story-card { width: 100% !important; box-sizing: border-box; }
3929
+
3930
+ /* ----- Hover-highlight: ring the hovered + its up/down chain ----- */
3931
+ .deps-node-hovered .sm-story-card {
3932
+ box-shadow: 0 0 0 2px var(--accent, #0F5F4F), 0 2px 8px rgba(15,95,79,0.18);
3933
+ }
3934
+ .deps-node-pred .sm-story-card { box-shadow: 0 0 0 2px #6B8E9B; }
3935
+ .deps-node-succ .sm-story-card { box-shadow: 0 0 0 2px #B88B4A; }
3936
+ /* Dim non-neighbours only while a node is ACTIVELY hovered (JS toggles
3937
+ .deps-hover-active on the canvas). Edges sit in the gaps, not behind card
3938
+ bodies, so a light opacity fade reads cleanly. */
3939
+ .deps-canvas.deps-hover-active .deps-node:not(.deps-node-hovered):not(.deps-node-pred):not(.deps-node-succ) {
3940
+ opacity: 0.4;
3941
+ }
3942
+
3943
+ /* ----- Edges ----------------------------------------------- */
3944
+ /* SM-64-followup: no opacity on edges either — opacity blends edges that
3945
+ pass behind nodes "through" them visually. Use a mid-tone stroke colour
3946
+ as the baseline; semantic-class strokes override per type. */
3947
+ .deps-edge {
3948
+ stroke: #C2B9A5;
3949
+ transition: stroke 120ms ease, stroke-width 120ms ease;
3950
+ }
3951
+ .deps-edge-active {
3952
+ stroke-width: 2.5;
3953
+ }
3954
+ /* Semantic-driven dashes/colour for read-at-a-glance. */
3955
+ .deps-edge-precedence { stroke: var(--accent, #0F5F4F); }
3956
+ .deps-edge-blocking { stroke: #B85C5C; stroke-dasharray: 6 4; }
3957
+ .deps-edge-sequence { stroke: var(--accent, #0F5F4F); stroke-dasharray: 2 4; }
3958
+ .deps-edge-containment { stroke: #B9A0B5; }
3959
+ .deps-edge-validation { stroke: #2E4665; stroke-dasharray: 4 3; }
3960
+ .deps-edge-freeform { stroke: #C2B9A5; }
3961
+ /* Dimmed semantic strokes when SVG is in hover-active mode + edge is
3962
+ not part of the highlighted closure. Pure colour shift, no opacity. */
3963
+ .deps-canvas.deps-hover-active .deps-edge:not(.deps-edge-active):not(.deps-edge-critical) {
3964
+ stroke: #D8D1BE;
3965
+ }
3966
+
3967
+ /* ---------------------------------------------------------------------------
3968
+ SM-50 follow-up — Dep-View separator + isolated-band styling
3969
+ --------------------------------------------------------------------------- */
3970
+
3971
+ .deps-band-separator { stroke: #C7BFAE; }
3972
+ .deps-band-separator-label {
3973
+ font-family: "Mona Sans", -apple-system, BlinkMacSystemFont, sans-serif;
3974
+ font-size: 11px;
3975
+ font-weight: 600;
3976
+ text-transform: uppercase;
3977
+ letter-spacing: 0.06em;
3978
+ fill: var(--ink-soft, #717071);
3979
+ }
3980
+
3981
+ /* SM-161: isolated nodes read dimmer than the connected backbone (the card
3982
+ itself is unchanged; the wrapper mutes it). */
3983
+ .deps-node-band-isolated { opacity: 0.72; }
3984
+
3985
+ /* ---------------------------------------------------------------------------
3986
+ SM-64 — Analysis controls in the deps-toolbar + critical-path highlight
3987
+ --------------------------------------------------------------------------- */
3988
+
3989
+ /* Critical-path overlay: a warm red ring around the card + a thick red edge so
3990
+ the chain reads through everything. SM-161: ring the card via box-shadow. */
3991
+ .deps-node-critical .sm-story-card {
3992
+ box-shadow: 0 0 0 3px #B85C5C, 0 2px 8px rgba(184,92,92,0.18);
3993
+ }
3994
+ .deps-edge-critical {
3995
+ stroke: #B85C5C !important;
3996
+ stroke-width: 2.5 !important;
3997
+ }
3998
+
3999
+ /* SM-64-followup: containing epic of any critical-path ticket — dashed-ish
4000
+ red accent so it reads as "context" rather than "on the path". */
4001
+ .deps-node-critical-context .sm-story-card {
4002
+ box-shadow: 0 0 0 2px rgba(184,92,92,0.55);
4003
+ }
4004
+
4005
+ /* ----- Status / type legend ------------------------------------------ */
4006
+ .deps-legend {
4007
+ display: flex;
4008
+ align-items: center;
4009
+ gap: 14px;
4010
+ margin-left: auto;
4011
+ padding-right: 8px;
4012
+ font-size: 11px;
4013
+ color: var(--ink-soft);
4014
+ }
4015
+ .deps-legend-item { display: inline-flex; align-items: center; gap: 5px; }
4016
+ .deps-legend-swatch {
4017
+ display: inline-block;
4018
+ width: 14px; height: 10px;
4019
+ border-radius: 3px;
4020
+ background: var(--paper);
4021
+ border: 2px solid var(--rule);
4022
+ box-sizing: border-box;
4023
+ }
4024
+ .deps-legend-swatch.legend-todo { border-color: #B0AAA0; }
4025
+ .deps-legend-swatch.legend-doing { border-color: var(--accent, #0F5F4F); }
4026
+ .deps-legend-swatch.legend-blocked { border-color: #B85C5C; }
4027
+ .deps-legend-swatch.legend-done { border-color: #4F7A4F; background: #F0EBE0; }
4028
+ .deps-legend-swatch.legend-critical { border-color: #B85C5C; border-width: 3px; }
4029
+ .deps-legend-swatch.legend-context { border-color: #B85C5C; border-style: dashed; }
4030
+
4031
+ /* Vertical hairline between toolbar control groups. Inherits the same rule
4032
+ colour as everything else in the toolbar. */
4033
+ .deps-toolbar .deps-toolbar-divider {
4034
+ display: inline-block;
4035
+ width: 1px;
4036
+ align-self: stretch;
4037
+ background: var(--rule);
4038
+ margin: 4px 4px;
4039
+ }
4040
+
4041
+ /* Buttons in the deps-toolbar — match the .btn convention but live in their
4042
+ own namespace so they don't bleed into the main toolbar styling. */
4043
+ .deps-toolbar-action {
4044
+ font: inherit;
4045
+ font-size: 12px;
4046
+ padding: 4px 10px;
4047
+ border: 1px solid var(--rule);
4048
+ border-radius: 4px;
4049
+ background: var(--paper);
4050
+ color: var(--ink);
4051
+ cursor: pointer;
4052
+ }
4053
+ .deps-toolbar-action:hover {
4054
+ background: var(--paper-warm, var(--paper));
4055
+ }
4056
+ .deps-toolbar-action.toggled {
4057
+ background: var(--accent, #0F5F4F);
4058
+ color: var(--paper);
4059
+ border-color: var(--accent, #0F5F4F);
4060
+ }
4061
+
4062
+ /* ---------------------------------------------------------------------------
4063
+ SM-9 — Type Config editor in Settings view
4064
+ --------------------------------------------------------------------------- */
4065
+
4066
+ .vs-typeconfig-editor { display: flex; flex-direction: column; gap: 14px; }
4067
+ .vs-typeconfig-picker-row {
4068
+ display: grid;
4069
+ grid-template-columns: 60px 1fr;
4070
+ gap: 12px;
4071
+ align-items: center;
4072
+ }
4073
+ .vs-typeconfig-picker-row .tm-label { font-size: 12px; color: var(--ink-soft); margin: 0; }
4074
+ .vs-typeconfig-type-select {
4075
+ font: inherit; font-size: 13px;
4076
+ padding: 4px 22px 4px 8px;
4077
+ background: var(--paper);
4078
+ border: 1px solid var(--rule);
4079
+ border-radius: 3px;
4080
+ max-width: 280px;
4081
+ }
4082
+
4083
+ .vs-typeconfig-flags { display: flex; flex-direction: column; gap: 10px; }
4084
+ .vs-typeconfig-flag {
4085
+ display: flex; flex-direction: column; gap: 2px;
4086
+ padding: 8px 10px;
4087
+ border: 1px solid var(--rule);
4088
+ border-radius: 4px;
4089
+ background: var(--paper);
4090
+ }
4091
+ .vs-typeconfig-flag-label {
4092
+ display: inline-flex; align-items: center; gap: 8px;
4093
+ cursor: pointer;
4094
+ font-size: 13px;
4095
+ }
4096
+ .vs-typeconfig-flag-input { margin: 0; cursor: pointer; }
4097
+ .vs-typeconfig-flag-text { color: var(--ink); }
4098
+ .vs-typeconfig-flag-hint {
4099
+ font-size: 11px;
4100
+ color: var(--ink-soft);
4101
+ margin-left: 22px;
4102
+ }
4103
+ .vs-typeconfig-flag-locked { background: var(--ink-faint-bg, #FAF8F2); }
4104
+ .vs-typeconfig-flag-locked .vs-typeconfig-flag-label { cursor: not-allowed; opacity: 0.7; }
4105
+ .vs-typeconfig-flag-locked-note {
4106
+ font-size: 11px;
4107
+ font-style: italic;
4108
+ color: var(--ink-faint);
4109
+ margin-left: 22px;
4110
+ }
4111
+
4112
+ /* SM-71: Test-Execution starter dialog. */
4113
+ .tm-test-exec-host { padding: 4px 0; }
4114
+ .tm-test-exec-banner {
4115
+ background: var(--danger-soft, #FBE3E3);
4116
+ color: var(--danger, #B43C3C);
4117
+ border: 1px solid var(--danger, #B43C3C);
4118
+ padding: 8px 10px;
4119
+ border-radius: 4px;
4120
+ margin-bottom: 8px;
4121
+ font-size: 12px;
4122
+ }
4123
+ .tm-test-exec-empty {
4124
+ padding: 16px;
4125
+ border: 1px dashed var(--rule);
4126
+ border-radius: 4px;
4127
+ text-align: center;
4128
+ color: var(--ink-soft);
4129
+ font-size: 12px;
4130
+ font-style: italic;
4131
+ }
4132
+ .tm-test-exec-def-select,
4133
+ .tm-test-exec-env {
4134
+ flex: 1;
4135
+ padding: 5px 8px;
4136
+ border: 1px solid var(--rule);
4137
+ border-radius: 4px;
4138
+ background: var(--paper);
4139
+ font: inherit; font-size: 12px;
4140
+ color: var(--ink);
4141
+ }
4142
+ .tm-test-exec-def-select:focus,
4143
+ .tm-test-exec-env:focus { outline: none; border-color: var(--ink); }
4144
+
4145
+ /* ---------------------------------------------------------------------------
4146
+ * Full-page ticket editor (Epic B). SM-117 page + SM-118 content-first layout:
4147
+ * the description gets the main column; type/status/position/labels/links sit in
4148
+ * a meta sidebar. Reuses the .tm-* form controls from the modal (shared
4149
+ * builders), so the visual hierarchy can be adopted by the modal too.
4150
+ * ------------------------------------------------------------------------- */
4151
+ .te-page { background: var(--paper); }
4152
+ /* SM-160: the editor is a normal scrolling DOCUMENT, not the board's fixed
4153
+ 100vh grid. The global `body { height:100vh; overflow:hidden; display:grid }`
4154
+ and `main { overflow:hidden }` (board chrome) would otherwise clip a long
4155
+ description with no scrollbar. Override both for this page. */
4156
+ body.te-page { display: block; height: auto; min-height: 100vh; overflow: visible; }
4157
+ main.te-host { display: block; overflow: visible; max-width: 1080px; margin: 0 auto; padding: 22px 24px 72px; }
4158
+ .te-root { display: block; }
4159
+ .te-header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
4160
+ .te-back { font-size: 12px; color: var(--accent); text-decoration: none; font-weight: 600; }
4161
+ .te-back:hover { text-decoration: underline; }
4162
+ .te-key { font-size: 12px; color: var(--ink-soft); font-weight: 700; letter-spacing: 0.04em; }
4163
+
4164
+ /* SM-122/B7: the modal title is a link to the full-page editor. */
4165
+ .tm-fullpage-link { color: inherit; text-decoration: none; }
4166
+ .tm-fullpage-link:hover { text-decoration: underline; }
4167
+ .tm-fullpage-icon { color: var(--ink-soft); font-size: 0.65em; vertical-align: super; }
4168
+
4169
+ /* SM-158: board ticket-key links to the full-page editor (cards + epics). */
4170
+ .sm-key-link { color: inherit; text-decoration: none; cursor: pointer; }
4171
+ .sm-key-link:hover { text-decoration: underline; color: var(--accent); }
4172
+ .te-missing { padding: 40px 0; color: var(--ink-soft); font-size: 14px; }
4173
+
4174
+ /* Two-pane content-first grid. Collapses to one column on narrow viewports. */
4175
+ .te-layout {
4176
+ display: grid;
4177
+ grid-template-columns: minmax(0, 1fr) 320px;
4178
+ gap: 28px;
4179
+ align-items: start;
4180
+ }
4181
+ @media (max-width: 820px) { .te-layout { grid-template-columns: 1fr; } }
4182
+
4183
+ .te-main { min-width: 0; display: block; }
4184
+ .te-side {
4185
+ display: block;
4186
+ border: 1px solid var(--rule);
4187
+ border-radius: 8px;
4188
+ padding: 14px 16px;
4189
+ background: var(--paper-soft, rgba(0,0,0,0.015));
4190
+ position: sticky; top: 16px;
4191
+ }
4192
+
4193
+ /* Title hero — borderless, large, label hidden, WRAPS full width (textarea). */
4194
+ .te-title-row { display: block; margin: 0 0 14px; }
4195
+ .te-title-row .tm-label { display: none; }
4196
+ .te-title-row .tm-input {
4197
+ display: block;
4198
+ box-sizing: border-box;
4199
+ width: 100%;
4200
+ font-size: 24px; font-weight: 700; line-height: 1.25;
4201
+ color: var(--ink);
4202
+ border: 1px solid transparent;
4203
+ border-radius: 6px;
4204
+ padding: 4px 6px; margin-left: -6px;
4205
+ background: transparent;
4206
+ }
4207
+ /* The textarea variant wraps + auto-grows (set by buildTitleField). */
4208
+ .te-title-row textarea.tm-input {
4209
+ resize: none; overflow: hidden;
4210
+ white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
4211
+ min-height: 0;
4212
+ }
4213
+ .te-title-row .tm-input:hover { border-color: var(--rule); }
4214
+ .te-title-row .tm-input:focus { border-color: var(--ink); background: var(--paper); }
4215
+
4216
+ /* The description is the hero block in the main column. */
4217
+ .te-main .tm-row:has(.tm-description-edit) { flex-direction: column; align-items: stretch; }
4218
+ .te-main .tm-row:has(.tm-description-edit) > .tm-label {
4219
+ flex: none; margin-bottom: 4px; font-weight: 600; color: var(--ink-soft);
4220
+ }
4221
+ .te-main .tm-description-edit { min-height: 160px; }
4222
+ .te-main .tm-desc-actions { margin-left: 0; }
4223
+
4224
+ /* Sidebar fields stack (label above control) so they fit the narrow column. */
4225
+ .te-side .tm-row { flex-direction: column; align-items: stretch; gap: 3px; margin: 0 0 12px; }
4226
+ .te-side .tm-label { flex: none; font-weight: 600; }
4227
+ .te-side .tm-field-hint { margin-left: 0; }
4228
+ .te-side .tm-links { margin-top: 4px; }
4229
+ /* SM-244: editor action row (Delete + Cancel). */
4230
+ .te-side-actions {
4231
+ display: flex;
4232
+ gap: 8px;
4233
+ margin-top: 16px;
4234
+ padding-top: 12px;
4235
+ border-top: 1px solid var(--rule);
4236
+ }
4237
+ .te-side-actions .btn { flex: 1; }
4238
+
4239
+ /* SM-203 R-7 — Requirements module view (DOORS-style multi-column document).
4240
+ Full-page view (like Map / Kanban / Dependencies), not an overlay. */
4241
+ #requirements-view-host { padding: 18px 24px; }
4242
+ /* SM-206: full page width — the document should use the whole view area. */
4243
+ .vr-root { width: 100%; display: flex; flex-direction: column; gap: 14px; }
4244
+ .vr-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
4245
+ .vr-title { font-family: "Libre Caslon Text", Georgia, serif; font-size: 20px; }
4246
+ .vr-module-picker { font: inherit; font-size: 12px; padding: 4px 8px; border: 1px solid var(--rule);
4247
+ border-radius: 4px; background: var(--paper); }
4248
+ .vr-note { font-size: 11px; color: var(--ink-soft); }
4249
+ .vr-note-ok { color: #3D5639; }
4250
+ .vr-note-warn { color: #7A3E26; }
4251
+ .vr-empty, .vr-reqs-empty { color: var(--ink-soft); font-size: 13px; padding: 16px 0; }
4252
+ .vr-summary { display: flex; flex-wrap: wrap; gap: 6px; }
4253
+ .vr-chip { font-size: 11px; padding: 2px 8px; border-radius: 10px; border: 1px solid var(--rule); }
4254
+ .vr-chip-total { background: var(--paper-warm); }
4255
+
4256
+ /* The document: a scrollable multi-column grid (Source | ID | Requirement |
4257
+ Coverage). Text columns stay pure; ID + Coverage are separate attributes. */
4258
+ .vr-grid { display: grid; border: 1px solid var(--rule); border-radius: 6px; background: var(--paper);
4259
+ max-height: 72vh; overflow: auto; }
4260
+ /* SM-206: Source default WIDER than Requirement; both flexible, ID + Coverage
4261
+ fixed. The inline gridTemplateColumns (set by the resizer) overrides this. */
4262
+ .vr-grid-doc { grid-template-columns: 0.6fr 52px 0.4fr 112px; }
4263
+ .vr-grid-list { grid-template-columns: 52px minmax(0,1fr) 112px; }
4264
+ /* Draggable Source/Requirement divider on the Source header cell. */
4265
+ .vr-th-resizable { position: relative; }
4266
+ .vr-resizer {
4267
+ position: absolute; top: 0; right: -4px; width: 8px; height: 100%;
4268
+ cursor: col-resize; z-index: 3; touch-action: none;
4269
+ }
4270
+ .vr-resizer::after {
4271
+ content: ""; position: absolute; top: 4px; bottom: 4px; left: 3px; width: 2px;
4272
+ background: var(--rule);
4273
+ }
4274
+ .vr-resizer:hover::after { background: var(--accent); }
4275
+ .vr-cell { padding: 7px 12px; min-width: 0; }
4276
+ .vr-th { position: sticky; top: 0; z-index: 1; background: var(--paper-warm);
4277
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); }
4278
+ /* A section's first row gets a top rule that spans all columns. */
4279
+ .vr-section-start { border-top: 1px solid var(--rule); }
4280
+ .vr-grid-doc .vr-th, .vr-grid-list .vr-th { border-top: none; }
4281
+
4282
+ .vr-cell-source { background: rgba(15,95,79,0.02); }
4283
+ .vr-h { display: flex; align-items: baseline; gap: 8px; }
4284
+ .vr-h-path { font-size: 11px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
4285
+ .vr-h-text { font-weight: 700; font-size: 13px; }
4286
+ .vr-h1 .vr-h-text { font-size: 15px; }
4287
+ .vr-src-prose { font-size: 12.5px; color: var(--ink); white-space: pre-wrap; line-height: 1.5; margin-top: 3px; }
4288
+
4289
+ .vr-cell-id { font-size: 11px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
4290
+ .vr-cell-req { font-size: 13px; }
4291
+ .vr-req-text { line-height: 1.45; }
4292
+ .vr-req-text[contenteditable="true"] { cursor: text; }
4293
+ .vr-req-text:focus { outline: 1px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
4294
+ .vr-clickable { cursor: pointer; }
4295
+ .vr-clickable:hover { background: rgba(15,95,79,0.04); }
4296
+ .vr-cell-req.vr-open { box-shadow: inset 3px 0 0 var(--accent); }
4297
+ .vr-cell-cov { white-space: nowrap; }
4298
+ .vr-gap-cell { background: rgba(176,56,43,0.06); }
4299
+ .vr-gap-text { font-size: 12px; color: #7A3E26; }
4300
+
4301
+ /* Expandable detail: traceability chips (clickable → open ticket) + actions. */
4302
+ .vr-detail { margin-top: 6px; display: flex; flex-direction: column; gap: 5px; }
4303
+ .vr-trace-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
4304
+ .vr-trace-rel { font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); }
4305
+ .vr-trace-chip { display: inline-flex; align-items: baseline; gap: 6px; font: inherit; font-size: 12px;
4306
+ padding: 2px 8px; border: 1px solid var(--rule); border-radius: 4px; background: var(--paper); cursor: pointer; }
4307
+ .vr-trace-chip:hover { border-color: var(--accent); color: var(--accent); }
4308
+ .vr-trace-key { font-size: 11px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
4309
+ .vr-action { font: inherit; font-size: 11px; align-self: flex-start; padding: 3px 10px;
4310
+ border: 1px solid var(--accent); border-radius: 4px; background: transparent; color: var(--accent); cursor: pointer; }
4311
+ .vr-action:hover { background: var(--accent); color: var(--paper); }
4312
+
4313
+ .vr-badge { font-size: 10px; padding: 1px 7px; border-radius: 9px; text-transform: lowercase; white-space: nowrap; }
4314
+ .vr-badge-covered { background: #E7EDE3; color: #3D5639; }
4315
+ .vr-badge-over-covered { background: #E8EEF5; color: #2E4665; }
4316
+ .vr-badge-orphan { background: #F1DCD2; color: #7A3E26; }
4317
+ .vr-badge-suspect { background: #EAE0EA; color: #5A3957; }
4318
+
4319
+ /* ===========================================================================
4320
+ SM-248/256 — Process-Step editor view (.psv-*). A SECOND surface onto the
4321
+ map: process steps render as cards via the SHARED .sm-story-card component
4322
+ (teal cluster) and reorder with the same dnd mechanic as the item cards.
4323
+ Only the layout chrome + quick-add are PS-specific here — there is NO
4324
+ parallel card style (DRY). The journey is ONE horizontal row; the PAGE
4325
+ (#view-host) scrolls, not the row.
4326
+ =========================================================================== */
4327
+ .psv-root { padding: 18px 22px; }
4328
+ .psv-header { margin-bottom: 16px; }
4329
+ .psv-heading { font-size: 18px; margin: 0 0 4px; }
4330
+ .psv-sub { color: var(--ink-soft); font-size: 12px; margin: 0; max-width: 720px; }
4331
+ .psv-empty {
4332
+ color: var(--ink-soft); font-size: 13px; padding: 32px;
4333
+ border: 1px dashed var(--rule); border-radius: 6px; text-align: center;
4334
+ }
4335
+ /* One horizontal row, no wrap. NO overflow here — the row grows to its
4336
+ content width and #view-host (the view scroll container) scrolls the page,
4337
+ so the whole surface moves, not just the strip. */
4338
+ .psv-flow {
4339
+ display: flex; flex-wrap: nowrap; gap: 14px; align-items: flex-start;
4340
+ width: max-content;
4341
+ }
4342
+ /* The step cards wear the SAME colour as the story-map backbone spine (shared
4343
+ --backbone-* tokens): green band + light text. So the two surfaces read as
4344
+ the same process steps — the Map draws them as the header, this view as
4345
+ cards. The `process` cluster carries no own colour; we paint it here. */
4346
+ .psv-flow .sm-story-card[data-process-step-id] {
4347
+ background: var(--backbone-bg);
4348
+ border-color: var(--backbone-edge);
4349
+ color: var(--backbone-ink);
4350
+ cursor: grab;
4351
+ }
4352
+ .psv-flow .sm-story-card[data-process-step-id] .sm-story-title { color: var(--backbone-ink); }
4353
+ /* Quick-add: a dashed end-of-row affordance sized like a step card. */
4354
+ .psv-quickadd {
4355
+ flex: 0 0 220px; width: 220px;
4356
+ display: flex; align-items: center; justify-content: center;
4357
+ border: 1px dashed var(--rule); border-radius: 6px; padding: 10px 12px;
4358
+ background: transparent;
4359
+ }
4360
+ .psv-quickadd-input {
4361
+ width: 100%; box-sizing: border-box; font-size: 13px;
4362
+ border: 1px solid transparent; background: transparent; padding: 6px 4px; border-radius: 4px;
4363
+ }
4364
+ .psv-quickadd-input:hover { border-color: var(--rule); }
4365
+ .psv-quickadd-input:focus { border-color: var(--accent); background: var(--paper-warm); outline: none; }
4366
+
4367
+ /* ===========================================================================
4368
+ SM-274/SM-275: Map-Eingang (Bodenstreifen) — sticky bottom inbox for
4369
+ release-LESS tickets (the work SM-253 dropped from the map; epics can't show
4370
+ in Kanban).
4371
+
4372
+ It is the LAST child of .sm-grid (NOT a sibling of the grid). The grid is
4373
+ `width: max-content` — the full map scroll width — so this plain block child
4374
+ gets `width: auto` = that same width for free: the background spans the whole
4375
+ map with pure CSS, no measured width, and it scrolls horizontally WITH the
4376
+ grid so the background always covers the full width. The card body is a
4377
+ `overflow-x: auto` scroll container, so the cards don't feed back into the
4378
+ grid's max-content width. Row count is viewport-derived (SM-275, JS).
4379
+
4380
+ NOT sticky (SM-276 follow-up): it sits in normal flow BELOW the last release.
4381
+ On a short board the viewport-derived row count fills the space down to the
4382
+ window bottom (no empty gap); on a tall board it sits under the last release
4383
+ and you scroll down to it — it never overlays the release rows.
4384
+ =========================================================================== */
4385
+ .sm-tray {
4386
+ background: var(--paper-warm);
4387
+ border-top: 2px solid var(--rule);
4388
+ /* SM-279: the strip is a block child of the width:max-content grid. With
4389
+ width:auto its wide card row fed its own max-content back into the grid,
4390
+ widening it past the process-step columns and breaking the map's horizontal
4391
+ scroll (the user could no longer reach off-screen process steps). Size
4392
+ containment on the inline axis makes the strip's width independent of its
4393
+ content, so it fills the grid width (background still spans) WITHOUT growing
4394
+ it; its own cards scroll inside .sm-tray-body (overflow-x:auto). */
4395
+ contain: inline-size;
4396
+ width: 100%;
4397
+ }
4398
+ .sm-tray-header {
4399
+ display: flex;
4400
+ align-items: center;
4401
+ gap: 8px;
4402
+ padding: 6px 12px;
4403
+ font-size: 12px;
4404
+ font-weight: 600;
4405
+ color: var(--ink-soft);
4406
+ border-bottom: 1px solid var(--rule);
4407
+ }
4408
+ .sm-tray-collapsed .sm-tray-header { border-bottom: none; }
4409
+ .sm-tray-toggle {
4410
+ border: none;
4411
+ background: transparent;
4412
+ cursor: pointer;
4413
+ font-size: 12px;
4414
+ color: var(--ink-soft);
4415
+ padding: 0 4px;
4416
+ line-height: 1;
4417
+ }
4418
+ .sm-tray-toggle:hover { color: var(--ink); }
4419
+ .sm-tray-title { letter-spacing: -0.01em; }
4420
+ .sm-tray-count {
4421
+ display: inline-flex;
4422
+ align-items: center;
4423
+ justify-content: center;
4424
+ min-width: 18px;
4425
+ height: 18px;
4426
+ padding: 0 5px;
4427
+ background: var(--rule);
4428
+ color: var(--ink);
4429
+ border-radius: 9px;
4430
+ font-size: 11px;
4431
+ font-weight: 600;
4432
+ }
4433
+ .sm-tray-body {
4434
+ overflow-x: auto;
4435
+ overflow-y: hidden;
4436
+ padding: 8px 12px;
4437
+ }
4438
+ .sm-tray-grid {
4439
+ display: grid;
4440
+ grid-auto-flow: column;
4441
+ gap: 8px;
4442
+ align-content: start;
4443
+ width: max-content;
4444
+ }
4445
+
4446
+ /* ===========================================================================
4447
+ SM-282 — Table view (.tv-*): Jira-Issue-Navigator-artige Sicht. Eigene
4448
+ Query-Zeile oben (gleiche SM-187-Sprache wie Smart-Bar + MCP query_tickets),
4449
+ Ergebnis-Tabelle darunter. Spalten kommen aus QUERY_FIELDS — der Renderer
4450
+ kennt keine eigene Feld-Logik.
4451
+ =========================================================================== */
4452
+ /* SM-292: full available width (user finding — no max-width cap). */
4453
+ .tv-root { padding: 16px 22px; }
4454
+ .tv-query-row { display: flex; gap: 8px; margin-bottom: 8px; }
4455
+ .tv-query-input {
4456
+ flex: 1;
4457
+ font: 13px ui-monospace, "SF Mono", Menlo, monospace;
4458
+ padding: 7px 10px;
4459
+ border: 1px solid var(--rule);
4460
+ border-radius: 4px;
4461
+ background: var(--paper-warm);
4462
+ color: var(--ink);
4463
+ }
4464
+ .tv-query-input:focus { border-color: var(--accent); outline: none; background: #fff; }
4465
+ .tv-query-error {
4466
+ color: var(--danger, #A3352B);
4467
+ font-size: 12px;
4468
+ padding: 4px 2px 8px;
4469
+ }
4470
+ .tv-table-host { overflow-x: auto; }
4471
+ .tv-table {
4472
+ width: 100%;
4473
+ table-layout: fixed; /* SM-292: colgroup widths rule; title takes the rest */
4474
+ border-collapse: collapse;
4475
+ font-size: 13px;
4476
+ background: #fff;
4477
+ border: 1px solid var(--rule);
4478
+ }
4479
+ .tv-table thead th {
4480
+ text-align: left;
4481
+ font-size: 11px;
4482
+ font-weight: 600;
4483
+ text-transform: uppercase;
4484
+ letter-spacing: 0.04em;
4485
+ color: var(--ink-soft);
4486
+ background: var(--paper-warm);
4487
+ border-bottom: 2px solid var(--rule);
4488
+ padding: 8px 10px;
4489
+ white-space: nowrap;
4490
+ }
4491
+ .tv-table tbody td {
4492
+ padding: 7px 10px;
4493
+ border-bottom: 1px solid var(--rule);
4494
+ vertical-align: top;
4495
+ overflow-wrap: anywhere;
4496
+ }
4497
+ .tv-table tbody td[data-col="key"] { white-space: nowrap; font-weight: 600; color: var(--accent); }
4498
+ .tv-table tbody tr { cursor: pointer; }
4499
+ .tv-table tbody tr:hover { background: var(--accent-soft, #EDF3F1); }
4500
+ .tv-count { color: var(--ink-soft); font-size: 12px; padding: 8px 2px; }
4501
+
4502
+ /* SM-283 — column picker panel */
4503
+ .tv-columns-btn { flex: 0 0 auto; }
4504
+ .tv-columns-panel {
4505
+ border: 1px solid var(--rule);
4506
+ border-radius: 4px;
4507
+ background: #fff;
4508
+ padding: 8px 10px;
4509
+ margin-bottom: 8px;
4510
+ max-width: 420px;
4511
+ }
4512
+ .tv-columns-row {
4513
+ display: grid;
4514
+ grid-template-columns: 20px 20px 1fr;
4515
+ align-items: center;
4516
+ gap: 8px;
4517
+ padding: 3px 2px;
4518
+ font-size: 13px;
4519
+ }
4520
+ .tv-columns-row:not(.tv-columns-row-active) { grid-template-columns: 20px 1fr; color: var(--ink-soft); }
4521
+ .tv-columns-grip { cursor: grab; color: var(--ink-soft); font-size: 11px; }
4522
+ .tv-columns-row.tv-columns-drop-target { box-shadow: inset 0 3px 0 0 var(--accent); }
4523
+ .tv-columns-row.sm-dragging { opacity: 0.4; }
4524
+ .tv-columns-footer { border-top: 1px solid var(--rule); margin-top: 6px; padding-top: 8px; }
4525
+
4526
+ /* SM-284 — sortable headers */
4527
+ .tv-table thead th { cursor: pointer; user-select: none; }
4528
+ .tv-table thead th[data-sort-dir]::after { margin-left: 5px; font-size: 9px; }
4529
+ .tv-table thead th[data-sort-dir="asc"]::after { content: "▲"; }
4530
+ .tv-table thead th[data-sort-dir="desc"]::after { content: "▼"; }
4531
+
4532
+ /* SM-292 — column resize handles + no-wrap columns */
4533
+ .tv-table thead th { position: relative; }
4534
+ .tv-resize-handle {
4535
+ position: absolute;
4536
+ top: 0; right: -4px; bottom: 0;
4537
+ width: 9px;
4538
+ cursor: col-resize;
4539
+ z-index: 1;
4540
+ }
4541
+ .tv-table td[data-col="epic"],
4542
+ .tv-table td[data-col="updated"],
4543
+ .tv-table td[data-col="created"],
4544
+ .tv-table td[data-col="status"] { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
4545
+ .tv-table td { overflow: hidden; text-overflow: ellipsis; }
4546
+
4547
+ /* SM-293 — Table view: visible column separators + resize affordance,
4548
+ and the global smart-bar row hides while the Table view is active
4549
+ (it has its own query line — two query inputs were confusing). */
4550
+ .toolbar-action[hidden] { display: none; }
4551
+ .tv-table thead th,
4552
+ .tv-table tbody td { border-right: 1px solid var(--rule); }
4553
+ .tv-table thead th:last-child,
4554
+ .tv-table tbody td:last-child { border-right: none; }
4555
+ /* Resize affordance: the handle shows as soon as the header is hovered,
4556
+ not only when the 9px hit zone itself is hit. */
4557
+ .tv-table thead th:hover .tv-resize-handle {
4558
+ background: linear-gradient(90deg, transparent 3px, var(--ink-soft) 3px, var(--ink-soft) 5px, transparent 5px);
4559
+ opacity: 0.5;
4560
+ }
4561
+ .tv-table thead th .tv-resize-handle:hover {
4562
+ background: linear-gradient(90deg, transparent 3px, var(--accent) 3px, var(--accent) 5px, transparent 5px);
4563
+ opacity: 1;
4564
+ }
4565
+
4566
+ /* SM-289 — ticket-import dialog (.ti-*) */
4567
+ .ti-root { display: flex; flex-direction: column; gap: 10px; margin: 12px 0; }
4568
+ .ti-source { display: flex; flex-direction: column; gap: 8px; }
4569
+ .ti-text {
4570
+ font: 12px ui-monospace, "SF Mono", Menlo, monospace;
4571
+ border: 1px solid var(--rule); border-radius: 4px;
4572
+ padding: 8px; background: var(--paper-warm); color: var(--ink);
4573
+ resize: vertical;
4574
+ }
4575
+ .ti-text:focus { border-color: var(--accent); outline: none; background: #fff; }
4576
+ /* SM-298 (review): a hand-enlarged textarea must never squeeze the scroll
4577
+ region to zero and clip the pinned action row. */
4578
+ .ti-text { max-height: 40vh; }
4579
+ .ti-mode { display: flex; gap: 18px; font-size: 13px; }
4580
+ .ti-mode-opt { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
4581
+ .ti-parse-error { color: var(--danger, #A3352B); font-size: 12px; }
4582
+ .ti-summary { font-size: 13px; font-weight: 600; }
4583
+ .ti-preview-host { /* SM-298: scrolls inside .ti-scroll, no nested scroller */ }
4584
+ .ti-preview { width: 100%; border-collapse: collapse; font-size: 12px; }
4585
+ .ti-preview th {
4586
+ text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em;
4587
+ color: var(--ink-soft); border-bottom: 1px solid var(--rule); padding: 4px 6px;
4588
+ }
4589
+ .ti-preview td { padding: 4px 6px; border-bottom: 1px solid var(--rule); vertical-align: top; overflow-wrap: anywhere; }
4590
+ .ti-row-error td { color: var(--danger, #A3352B); }
4591
+ .ti-row-create td:nth-child(2) { color: var(--accent); font-weight: 600; }
4592
+ .ti-truncated { color: var(--ink-soft); font-size: 12px; padding: 6px 2px; }
4593
+ /* SM-298: the import dialog is content-heavy AND resizable — flex column
4594
+ (title + source fixed, ONE scroll region takes the rest, actions pinned),
4595
+ native resize handle bottom-right with sane bounds. */
4596
+ #modal-host .modal:has(.ti-root) {
4597
+ width: 820px; max-width: 95vw;
4598
+ min-width: 560px; min-height: 420px;
4599
+ max-height: 92vh;
4600
+ resize: both; overflow: hidden;
4601
+ display: flex; flex-direction: column;
4602
+ }
4603
+ #modal-host .modal:has(.ti-root) .modal-actions { margin-top: auto; flex: 0 0 auto; }
4604
+
4605
+ /* SM-295 — unified import (project branch) + export chooser */
4606
+ .ti-project-preview { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px solid var(--rule); border-radius: 4px; background: var(--paper-warm); font-size: 13px; }
4607
+ .ti-project-counts { color: var(--ink-soft); }
4608
+ .ti-project-conflict { color: var(--danger, #A3352B); font-weight: 600; }
4609
+ .ti-destructive { color: var(--danger, #A3352B); }
4610
+ .ti-export-info { margin: 12px 0; font-size: 13px; color: var(--ink-soft); display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
4611
+
4612
+ /* SM-296 — import column mapping panel */
4613
+ .ti-mapping {
4614
+ border: 1px solid var(--rule); border-radius: 4px;
4615
+ background: var(--paper-warm);
4616
+ padding: 8px 10px;
4617
+ display: flex; flex-direction: column; gap: 4px;
4618
+ /* SM-298: no own mini-scroller — .ti-scroll owns the scrolling */
4619
+ }
4620
+ .ti-mapping-title { font-size: 12px; font-weight: 600; color: var(--ink-soft); margin-bottom: 2px; }
4621
+ .ti-mapping-row {
4622
+ display: grid; grid-template-columns: 1fr 200px;
4623
+ align-items: center; gap: 10px; font-size: 12px; cursor: default;
4624
+ }
4625
+ .ti-mapping-raw { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, Menlo, monospace; }
4626
+ .ti-mapping-select { font-size: 12px; padding: 2px 4px; border: 1px solid var(--rule); border-radius: 3px; background: #fff; }
4627
+ .ti-unused { color: var(--ink-soft); font-size: 12px; padding: 6px 2px; }
4628
+
4629
+ /* SM-298 — resizable import dialog: one scroll region over mapping + preview */
4630
+ .ti-root { flex: 1 1 auto; min-height: 0; }
4631
+ .ti-scroll {
4632
+ flex: 1 1 auto; min-height: 0;
4633
+ overflow-y: auto;
4634
+ display: flex; flex-direction: column; gap: 10px;
4635
+ }
4636
+ .ti-mapping-value-row { padding-left: 26px; }
4637
+ .ti-mapping-value-row .ti-mapping-raw { color: var(--ink-soft); }