@zigrivers/scaffold 3.29.0 → 3.31.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 (86) hide show
  1. package/content/guides/AUTHORING.md +146 -0
  2. package/content/guides/cli/index.html +1855 -0
  3. package/content/guides/cli/index.md +206 -0
  4. package/content/guides/concepts/index.html +1970 -0
  5. package/content/guides/concepts/index.md +347 -0
  6. package/content/guides/dashboard/index.html +1913 -0
  7. package/content/guides/dashboard/index.md +264 -0
  8. package/content/guides/index.html +368 -15
  9. package/content/guides/install/.diagrams/diagram-0.svg +1 -0
  10. package/content/guides/install/.diagrams/manifest.json +3 -0
  11. package/content/guides/install/index.html +1653 -0
  12. package/content/guides/install/index.md +186 -0
  13. package/content/guides/knowledge/.diagrams/diagram-0.svg +1 -0
  14. package/content/guides/knowledge/.diagrams/manifest.json +3 -0
  15. package/content/guides/knowledge/index.html +1765 -0
  16. package/content/guides/knowledge/index.md +209 -0
  17. package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -0
  18. package/content/guides/knowledge-freshness/.diagrams/manifest.json +3 -0
  19. package/content/guides/knowledge-freshness/index.html +2795 -0
  20. package/content/guides/knowledge-freshness/index.md +893 -0
  21. package/content/guides/mmr/index.html +407 -36
  22. package/content/guides/mmr/index.md +39 -16
  23. package/content/guides/multi-agent/.diagrams/diagram-0.svg +1 -0
  24. package/content/guides/multi-agent/.diagrams/manifest.json +3 -0
  25. package/content/guides/multi-agent/index.html +1715 -0
  26. package/content/guides/multi-agent/index.md +243 -0
  27. package/content/guides/observability/.diagrams/diagram-0.svg +1 -0
  28. package/content/guides/observability/.diagrams/diagram-1.svg +1 -0
  29. package/content/guides/observability/.diagrams/diagram-2.svg +1 -0
  30. package/content/guides/observability/.diagrams/diagram-3.svg +1 -0
  31. package/content/guides/observability/.diagrams/manifest.json +6 -0
  32. package/content/guides/observability/index.html +3257 -0
  33. package/content/guides/observability/index.md +1097 -0
  34. package/content/guides/pipeline/.diagrams/diagram-0.svg +1 -0
  35. package/content/guides/pipeline/.diagrams/diagram-1.svg +1 -0
  36. package/content/guides/pipeline/.diagrams/manifest.json +4 -0
  37. package/content/guides/pipeline/index.html +1973 -0
  38. package/content/guides/pipeline/index.md +387 -0
  39. package/content/guides/review-workflow/.diagrams/diagram-0.svg +1 -0
  40. package/content/guides/review-workflow/.diagrams/diagram-1.svg +1 -0
  41. package/content/guides/review-workflow/.diagrams/manifest.json +4 -0
  42. package/content/guides/review-workflow/index.html +1790 -0
  43. package/content/guides/review-workflow/index.md +248 -0
  44. package/dist/guides/build.d.ts +1 -1
  45. package/dist/guides/build.d.ts.map +1 -1
  46. package/dist/guides/build.js +21 -9
  47. package/dist/guides/build.js.map +1 -1
  48. package/dist/guides/build.test.js +47 -0
  49. package/dist/guides/build.test.js.map +1 -1
  50. package/dist/guides/chrome.d.ts.map +1 -1
  51. package/dist/guides/chrome.js +83 -12
  52. package/dist/guides/chrome.js.map +1 -1
  53. package/dist/guides/dashboard-theme.css +8 -0
  54. package/dist/guides/directives-cite.test.d.ts +2 -0
  55. package/dist/guides/directives-cite.test.d.ts.map +1 -0
  56. package/dist/guides/directives-cite.test.js +26 -0
  57. package/dist/guides/directives-cite.test.js.map +1 -0
  58. package/dist/guides/directives-tabs.test.js +47 -0
  59. package/dist/guides/directives-tabs.test.js.map +1 -1
  60. package/dist/guides/directives.d.ts +1 -0
  61. package/dist/guides/directives.d.ts.map +1 -1
  62. package/dist/guides/directives.js +38 -0
  63. package/dist/guides/directives.js.map +1 -1
  64. package/dist/guides/guides.css +268 -0
  65. package/dist/guides/index-page.d.ts.map +1 -1
  66. package/dist/guides/index-page.js +41 -8
  67. package/dist/guides/index-page.js.map +1 -1
  68. package/dist/guides/links.d.ts +14 -0
  69. package/dist/guides/links.d.ts.map +1 -0
  70. package/dist/guides/links.js +56 -0
  71. package/dist/guides/links.js.map +1 -0
  72. package/dist/guides/links.test.d.ts +2 -0
  73. package/dist/guides/links.test.d.ts.map +1 -0
  74. package/dist/guides/links.test.js +72 -0
  75. package/dist/guides/links.test.js.map +1 -0
  76. package/dist/guides/render.d.ts +1 -0
  77. package/dist/guides/render.d.ts.map +1 -1
  78. package/dist/guides/render.js +1 -1
  79. package/dist/guides/render.js.map +1 -1
  80. package/dist/guides/sanitize.d.ts.map +1 -1
  81. package/dist/guides/sanitize.js +5 -0
  82. package/dist/guides/sanitize.js.map +1 -1
  83. package/dist/guides/template.d.ts.map +1 -1
  84. package/dist/guides/template.js +7 -2
  85. package/dist/guides/template.js.map +1 -1
  86. package/package.json +2 -2
@@ -0,0 +1,1653 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-chrome-version="1">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Install, Update &amp; Adopt</title>
7
+ <!-- scaffold:chrome v1 -->
8
+ <style>/* Scaffold Dashboard Theme
9
+ * All CSS for the generated pipeline dashboard.
10
+ * Embedded into HTML by scripts/generate-dashboard.sh.
11
+ * Design system reference: docs/design-system.md
12
+ *
13
+ * Aesthetic: "Precision Industrial" — Swiss-typographic control room.
14
+ * Deep navy dark mode with indigo accents, clean cool-white light mode.
15
+ */
16
+
17
+ /* ─── Design Tokens (Light Mode) ──────────────── */
18
+ :root {
19
+ /* Surface */
20
+ --bg: #f5f6fa;
21
+ --bg-card: #ffffff;
22
+ --bg-hover: #eef0f6;
23
+ --bg-inset: #e8eaf2;
24
+
25
+ /* Text */
26
+ --text: #1a1d2e;
27
+ --text-muted: #6b7294;
28
+ --text-faint: #9ba1c0;
29
+
30
+ /* Borders & Structure */
31
+ --border: #dde0ed;
32
+ --border-light: #eceef5;
33
+ --radius: 10px;
34
+ --radius-sm: 6px;
35
+
36
+ /* Accent */
37
+ --accent: #4f46e5;
38
+ --accent-hover: #4338ca;
39
+ --accent-glow: rgba(79, 70, 229, 0.10);
40
+
41
+ /* Semantic: Status */
42
+ --green: #059669;
43
+ --green-bg: #ecfdf5;
44
+ --green-border: #a7f3d0;
45
+ --blue: #2563eb;
46
+ --blue-bg: #eff6ff;
47
+ --blue-border: #bfdbfe;
48
+ --yellow: #d97706;
49
+ --yellow-bg: #fffbeb;
50
+ --yellow-border:#fde68a;
51
+ --red: #dc2626;
52
+ --red-bg: #fef2f2;
53
+ --red-border: #fecaca;
54
+ --gray: #9ca3af;
55
+ --gray-bg: #f3f4f6;
56
+ --gray-border: #e5e7eb;
57
+ --scrim: rgba(15, 17, 23, 0.45);
58
+
59
+ /* Semantic: Next Banner */
60
+ --next-bg: #eef2ff;
61
+ --next-border: #4f46e5;
62
+ --next-glow: rgba(79, 70, 229, 0.06);
63
+
64
+ /* Semantic: Progress */
65
+ --progress-bg: #e5e7eb;
66
+ --progress-h: 10px;
67
+
68
+ /* Depth */
69
+ --shadow-sm: 0 1px 2px rgba(30, 34, 60, 0.04);
70
+ --shadow: 0 1px 3px rgba(30, 34, 60, 0.07), 0 1px 2px rgba(30, 34, 60, 0.04);
71
+ --shadow-md: 0 4px 12px rgba(30, 34, 60, 0.08), 0 1px 3px rgba(30, 34, 60, 0.05);
72
+ --shadow-lg: 0 8px 24px rgba(30, 34, 60, 0.10), 0 2px 6px rgba(30, 34, 60, 0.04);
73
+
74
+ /* Spacing scale (4px base) */
75
+ --sp-1: 4px;
76
+ --sp-2: 8px;
77
+ --sp-3: 12px;
78
+ --sp-4: 16px;
79
+ --sp-5: 20px;
80
+ --sp-6: 24px;
81
+ --sp-8: 32px;
82
+ --sp-10: 40px;
83
+
84
+ /* Typography */
85
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
86
+ --font-mono: "SF Mono", "Cascadia Code", "Fira Code", "JetBrains Mono", Menlo, Consolas, monospace;
87
+ --text-xs: 0.75rem;
88
+ --text-sm: 0.8125rem;
89
+ --text-base: 0.9375rem;
90
+ --text-lg: 1.125rem;
91
+ --text-xl: 1.375rem;
92
+ --text-2xl: 1.75rem;
93
+ --lh-tight: 1.25;
94
+ --lh-normal: 1.5;
95
+ --lh-relaxed: 1.625;
96
+ --ls-tight: -0.01em;
97
+ --ls-wide: 0.025em;
98
+ --fw-normal: 400;
99
+ --fw-medium: 500;
100
+ --fw-semi: 600;
101
+ --fw-bold: 700;
102
+
103
+ /* Layout */
104
+ --max-w: 960px;
105
+ --page-pad: 24px;
106
+ }
107
+
108
+ /* ─── Design Tokens (Dark Mode) ───────────────── */
109
+ [data-theme="dark"] {
110
+ /* Surface */
111
+ --bg: #0f1117;
112
+ --bg-card: #1a1d2e;
113
+ --bg-hover: #252940;
114
+ --bg-inset: #141724;
115
+
116
+ /* Text */
117
+ --text: #e2e5f0;
118
+ --text-muted: #7c82a8;
119
+ --text-faint: #555c80;
120
+
121
+ /* Borders & Structure */
122
+ --border: #2a2f45;
123
+ --border-light: #21253a;
124
+
125
+ /* Accent */
126
+ --accent: #818cf8;
127
+ --accent-hover: #a5b4fc;
128
+ --accent-glow: rgba(129, 140, 248, 0.12);
129
+
130
+ /* Semantic: Status */
131
+ --green: #34d399;
132
+ --green-bg: rgba(6, 78, 59, 0.25);
133
+ --green-border: rgba(52, 211, 153, 0.25);
134
+ --blue: #60a5fa;
135
+ --blue-bg: rgba(30, 58, 95, 0.30);
136
+ --blue-border: rgba(96, 165, 250, 0.25);
137
+ --yellow: #fbbf24;
138
+ --yellow-bg: rgba(120, 53, 15, 0.25);
139
+ --yellow-border:rgba(251, 191, 36, 0.20);
140
+ --red: #f87171;
141
+ --red-bg: rgba(127, 29, 29, 0.25);
142
+ --red-border: rgba(248, 113, 113, 0.22);
143
+ --gray: #6b7294;
144
+ --gray-bg: #252940;
145
+ --gray-border: #363c58;
146
+ --scrim: rgba(0, 0, 0, 0.6);
147
+
148
+ /* Semantic: Next Banner */
149
+ --next-bg: rgba(30, 27, 75, 0.50);
150
+ --next-border: #818cf8;
151
+ --next-glow: rgba(129, 140, 248, 0.08);
152
+
153
+ /* Semantic: Progress */
154
+ --progress-bg: #1f2337;
155
+
156
+ /* Depth */
157
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.20);
158
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.30), 0 1px 2px rgba(0, 0, 0, 0.15);
159
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.20);
160
+ --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.40), 0 2px 6px rgba(0, 0, 0, 0.20);
161
+ }
162
+
163
+ /* ─── Theme Toggle ───────────────────────────── */
164
+ .theme-toggle {
165
+ background: var(--bg-inset);
166
+ border: 1px solid var(--border);
167
+ border-radius: var(--radius-sm);
168
+ padding: var(--sp-1) var(--sp-2);
169
+ cursor: pointer;
170
+ font-size: var(--text-base);
171
+ line-height: 1;
172
+ color: var(--text-muted);
173
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
174
+ display: flex;
175
+ align-items: center;
176
+ margin-left: auto;
177
+ }
178
+
179
+ .theme-toggle:hover {
180
+ border-color: var(--accent);
181
+ color: var(--accent);
182
+ background: var(--accent-glow);
183
+ }
184
+
185
+ /* ─── Base ────────────────────────────────────── */
186
+ *, *::before, *::after {
187
+ margin: 0;
188
+ padding: 0;
189
+ box-sizing: border-box;
190
+ }
191
+
192
+ body {
193
+ font-family: var(--font-sans);
194
+ font-size: var(--text-base);
195
+ line-height: var(--lh-normal);
196
+ color: var(--text);
197
+ background: var(--bg);
198
+ -webkit-font-smoothing: antialiased;
199
+ -moz-osx-font-smoothing: grayscale;
200
+ }
201
+
202
+ /* ─── Layout ──────────────────────────────────── */
203
+ .wrap {
204
+ max-width: var(--max-w);
205
+ margin: 0 auto;
206
+ padding: var(--sp-8) var(--page-pad);
207
+ }
208
+
209
+ /* ─── Header ──────────────────────────────────── */
210
+ .header {
211
+ display: flex;
212
+ align-items: baseline;
213
+ gap: var(--sp-3);
214
+ margin-bottom: var(--sp-2);
215
+ flex-wrap: wrap;
216
+ }
217
+
218
+ h1 {
219
+ font-size: var(--text-2xl);
220
+ font-weight: var(--fw-bold);
221
+ letter-spacing: var(--ls-tight);
222
+ line-height: var(--lh-tight);
223
+ }
224
+
225
+ h2 {
226
+ font-size: var(--text-lg);
227
+ font-weight: var(--fw-semi);
228
+ letter-spacing: var(--ls-tight);
229
+ line-height: var(--lh-tight);
230
+ margin-bottom: var(--sp-3);
231
+ }
232
+
233
+ .header-meta {
234
+ font-size: var(--text-xs);
235
+ color: var(--text-faint);
236
+ margin-bottom: var(--sp-6);
237
+ letter-spacing: var(--ls-wide);
238
+ text-transform: uppercase;
239
+ }
240
+
241
+ /* ─── Badge ───────────────────────────────────── */
242
+ .badge {
243
+ display: inline-block;
244
+ padding: 2px var(--sp-2);
245
+ border-radius: 99px;
246
+ font-size: var(--text-xs);
247
+ font-weight: var(--fw-semi);
248
+ letter-spacing: var(--ls-wide);
249
+ background: var(--accent);
250
+ color: #fff;
251
+ text-transform: uppercase;
252
+ }
253
+
254
+ .badge-optional {
255
+ background: var(--yellow-bg);
256
+ color: var(--yellow);
257
+ border: 1px solid var(--yellow-border);
258
+ }
259
+
260
+ /* ─── Progress Bar ────────────────────────────── */
261
+ .progress-bar {
262
+ width: 100%;
263
+ height: var(--progress-h);
264
+ background: var(--progress-bg);
265
+ border-radius: 99px;
266
+ overflow: hidden;
267
+ margin-bottom: var(--sp-6);
268
+ display: flex;
269
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.06);
270
+ }
271
+
272
+ .progress-bar .seg-done {
273
+ background: linear-gradient(135deg, var(--green), #10b981);
274
+ box-shadow: 0 0 8px rgba(5, 150, 105, 0.3);
275
+ }
276
+
277
+ .progress-bar .seg-likely {
278
+ background: linear-gradient(135deg, var(--blue), #3b82f6);
279
+ box-shadow: 0 0 8px rgba(37, 99, 235, 0.25);
280
+ }
281
+
282
+ .progress-bar .seg-skip {
283
+ background: var(--gray);
284
+ opacity: 0.7;
285
+ }
286
+
287
+ /* ─── Summary Cards ───────────────────────────── */
288
+ .cards {
289
+ display: grid;
290
+ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
291
+ gap: var(--sp-3);
292
+ margin-bottom: var(--sp-6);
293
+ }
294
+
295
+ .card {
296
+ background: var(--bg-card);
297
+ border: 1px solid var(--border);
298
+ border-radius: var(--radius);
299
+ padding: var(--sp-4) var(--sp-5);
300
+ box-shadow: var(--shadow);
301
+ transition: box-shadow 0.15s ease, transform 0.15s ease;
302
+ }
303
+
304
+ .card:hover {
305
+ box-shadow: var(--shadow-md);
306
+ transform: translateY(-1px);
307
+ }
308
+
309
+ .card-num {
310
+ font-size: var(--text-2xl);
311
+ font-weight: var(--fw-bold);
312
+ font-family: var(--font-mono);
313
+ letter-spacing: var(--ls-tight);
314
+ line-height: 1;
315
+ }
316
+
317
+ .card-lbl {
318
+ font-size: var(--text-xs);
319
+ color: var(--text-muted);
320
+ margin-top: var(--sp-1);
321
+ letter-spacing: var(--ls-wide);
322
+ text-transform: uppercase;
323
+ font-weight: var(--fw-medium);
324
+ }
325
+
326
+ /* ─── What's Next Banner ──────────────────────── */
327
+ .next-banner {
328
+ background: var(--next-bg);
329
+ border: 1px solid var(--next-border);
330
+ border-left: 4px solid var(--next-border);
331
+ border-radius: var(--radius);
332
+ padding: var(--sp-5) var(--sp-6);
333
+ margin-bottom: var(--sp-6);
334
+ box-shadow: 0 0 0 1px var(--next-glow), var(--shadow);
335
+ position: relative;
336
+ overflow: hidden;
337
+ }
338
+
339
+ .next-banner::before {
340
+ content: "";
341
+ position: absolute;
342
+ top: 0;
343
+ left: 0;
344
+ width: 4px;
345
+ height: 100%;
346
+ background: var(--next-border);
347
+ animation: pulse-border 2.5s ease-in-out infinite;
348
+ }
349
+
350
+ @keyframes pulse-border {
351
+ 0%, 100% { opacity: 1; }
352
+ 50% { opacity: 0.5; }
353
+ }
354
+
355
+ .next-banner h2 {
356
+ color: var(--accent);
357
+ margin-bottom: var(--sp-1);
358
+ font-size: var(--text-base);
359
+ font-weight: var(--fw-semi);
360
+ letter-spacing: var(--ls-wide);
361
+ text-transform: uppercase;
362
+ }
363
+
364
+ .next-banner p {
365
+ color: var(--text);
366
+ font-size: var(--text-base);
367
+ }
368
+
369
+ .next-cmd {
370
+ font-family: var(--font-mono);
371
+ background: var(--bg-card);
372
+ padding: var(--sp-1) var(--sp-3);
373
+ border-radius: var(--radius-sm);
374
+ font-size: var(--text-sm);
375
+ display: inline-flex;
376
+ align-items: center;
377
+ gap: var(--sp-2);
378
+ margin-top: var(--sp-3);
379
+ border: 1px solid var(--border);
380
+ }
381
+
382
+ /* ─── Phase Headers (Collapsible) ─────────────── */
383
+ .phase {
384
+ margin-bottom: var(--sp-6);
385
+ }
386
+
387
+ .phase-hdr {
388
+ display: flex;
389
+ align-items: center;
390
+ gap: var(--sp-2);
391
+ cursor: pointer;
392
+ padding: var(--sp-2) 0;
393
+ user-select: none;
394
+ border-bottom: 2px solid var(--border);
395
+ margin-bottom: var(--sp-3);
396
+ transition: border-color 0.15s ease;
397
+ }
398
+
399
+ .phase-hdr:hover {
400
+ border-bottom-color: var(--accent);
401
+ }
402
+
403
+ .phase-hdr:hover h2 {
404
+ color: var(--accent);
405
+ }
406
+
407
+ .phase-hdr h2 {
408
+ transition: color 0.15s ease;
409
+ }
410
+
411
+ .phase-hdr .arr {
412
+ transition: transform 0.2s ease;
413
+ font-size: var(--text-xs);
414
+ color: var(--text-muted);
415
+ }
416
+
417
+ .phase-hdr.closed .arr {
418
+ transform: rotate(-90deg);
419
+ }
420
+
421
+ .phase-cnt {
422
+ font-size: var(--text-xs);
423
+ font-family: var(--font-mono);
424
+ color: var(--text-faint);
425
+ margin-left: auto;
426
+ letter-spacing: var(--ls-wide);
427
+ }
428
+
429
+ /* ─── Prompt List ─────────────────────────────── */
430
+ .plist {
431
+ display: flex;
432
+ flex-direction: column;
433
+ gap: var(--sp-2);
434
+ }
435
+
436
+ /* ─── Prompt Cards ────────────────────────────── */
437
+ .pcard {
438
+ background: var(--bg-card);
439
+ border: 1px solid var(--border);
440
+ border-radius: var(--radius);
441
+ padding: var(--sp-3) var(--sp-4);
442
+ box-shadow: var(--shadow-sm);
443
+ display: grid;
444
+ grid-template-columns: auto 1fr auto;
445
+ gap: var(--sp-2) var(--sp-3);
446
+ align-items: start;
447
+ transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
448
+ }
449
+
450
+ .pcard:hover {
451
+ box-shadow: var(--shadow);
452
+ transform: translateY(-1px);
453
+ border-color: var(--accent-glow);
454
+ }
455
+
456
+ /* ─── Status Badges ──────────────────────────── */
457
+ .status-badge {
458
+ display: inline-flex;
459
+ align-items: center;
460
+ gap: var(--sp-1);
461
+ font-size: var(--text-xs);
462
+ font-weight: var(--fw-medium);
463
+ padding: 2px var(--sp-2);
464
+ border-radius: 99px;
465
+ white-space: nowrap;
466
+ flex-shrink: 0;
467
+ letter-spacing: var(--ls-wide);
468
+ line-height: var(--lh-tight);
469
+ }
470
+
471
+ .st-completed {
472
+ background: var(--green-bg);
473
+ color: var(--green);
474
+ border: 1px solid var(--green-border);
475
+ }
476
+
477
+ .st-likely-completed {
478
+ background: var(--blue-bg);
479
+ color: var(--blue);
480
+ border: 1px solid var(--blue-border);
481
+ }
482
+
483
+ .st-skipped {
484
+ background: var(--gray-bg);
485
+ color: var(--gray);
486
+ border: 1px solid var(--gray-border);
487
+ }
488
+
489
+ .st-pending {
490
+ background: var(--bg-inset);
491
+ color: var(--text-faint);
492
+ border: 1px solid var(--border);
493
+ }
494
+
495
+ /* ─── Status Legend ──────────────────────────── */
496
+ .status-legend {
497
+ display: flex;
498
+ flex-wrap: wrap;
499
+ gap: var(--sp-2);
500
+ margin-bottom: var(--sp-4);
501
+ padding: var(--sp-2) 0;
502
+ }
503
+
504
+ .status-legend .status-badge {
505
+ cursor: default;
506
+ }
507
+
508
+ /* ─── Prompt Card Inner ───────────────────────── */
509
+ .pinfo {
510
+ min-width: 0;
511
+ }
512
+
513
+ .pname {
514
+ font-weight: var(--fw-semi);
515
+ font-size: var(--text-base);
516
+ }
517
+
518
+ .pstep {
519
+ font-size: var(--text-xs);
520
+ font-family: var(--font-mono);
521
+ color: var(--text-faint);
522
+ letter-spacing: var(--ls-wide);
523
+ }
524
+
525
+ .pdesc {
526
+ font-size: var(--text-sm);
527
+ color: var(--text-muted);
528
+ margin-top: 2px;
529
+ line-height: var(--lh-relaxed);
530
+ }
531
+
532
+ .pdesc-long {
533
+ font-size: var(--text-xs);
534
+ color: var(--text-faint);
535
+ margin-top: 2px;
536
+ }
537
+
538
+ .pdeps {
539
+ font-size: var(--text-xs);
540
+ color: var(--yellow);
541
+ margin-top: var(--sp-1);
542
+ font-weight: var(--fw-medium);
543
+ }
544
+
545
+ /* ─── Copy Command Button ─────────────────────── */
546
+ .pcmd {
547
+ font-family: var(--font-mono);
548
+ font-size: var(--text-xs);
549
+ background: var(--bg-inset);
550
+ padding: 3px var(--sp-2);
551
+ border-radius: var(--radius-sm);
552
+ cursor: pointer;
553
+ border: 1px solid var(--border);
554
+ white-space: nowrap;
555
+ align-self: center;
556
+ color: var(--text-muted);
557
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
558
+ letter-spacing: var(--ls-wide);
559
+ }
560
+
561
+ .pcmd:hover {
562
+ border-color: var(--accent);
563
+ color: var(--accent);
564
+ background: var(--accent-glow);
565
+ }
566
+
567
+ .pcmd.copied {
568
+ border-color: var(--green);
569
+ color: var(--green);
570
+ background: var(--green-bg);
571
+ }
572
+
573
+ /* ─── Prompt Modal ────────────────────────────── */
574
+ .modal-overlay {
575
+ position: fixed;
576
+ inset: 0;
577
+ background: rgba(0, 0, 0, 0.6);
578
+ display: flex;
579
+ align-items: center;
580
+ justify-content: center;
581
+ z-index: 1000;
582
+ padding: var(--sp-4);
583
+ }
584
+
585
+ .modal {
586
+ background: var(--bg-card);
587
+ border: 1px solid var(--border);
588
+ border-radius: var(--radius);
589
+ box-shadow: var(--shadow-lg);
590
+ max-width: 720px;
591
+ width: 100%;
592
+ max-height: 85vh;
593
+ display: flex;
594
+ flex-direction: column;
595
+ }
596
+
597
+ .modal-header {
598
+ display: flex;
599
+ align-items: center;
600
+ gap: var(--sp-3);
601
+ padding: var(--sp-4) var(--sp-5);
602
+ border-bottom: 1px solid var(--border);
603
+ flex-shrink: 0;
604
+ }
605
+
606
+ .modal-header h3 {
607
+ font-size: var(--text-lg);
608
+ font-weight: var(--fw-semi);
609
+ flex: 1;
610
+ min-width: 0;
611
+ }
612
+
613
+ .modal-close {
614
+ background: var(--bg-inset);
615
+ border: 1px solid var(--border);
616
+ border-radius: var(--radius-sm);
617
+ padding: var(--sp-1) var(--sp-2);
618
+ cursor: pointer;
619
+ font-size: var(--text-base);
620
+ color: var(--text-muted);
621
+ line-height: 1;
622
+ transition: border-color 0.15s ease, color 0.15s ease;
623
+ }
624
+
625
+ .modal-close:hover {
626
+ border-color: var(--accent);
627
+ color: var(--accent);
628
+ }
629
+
630
+ .modal-body {
631
+ padding: var(--sp-5);
632
+ overflow-y: auto;
633
+ flex: 1;
634
+ }
635
+
636
+ .modal-body pre {
637
+ font-family: var(--font-mono);
638
+ font-size: var(--text-sm);
639
+ line-height: var(--lh-relaxed);
640
+ white-space: pre-wrap;
641
+ word-break: break-word;
642
+ color: var(--text);
643
+ }
644
+
645
+ .modal-body pre .md-heading {
646
+ font-weight: var(--fw-bold);
647
+ color: var(--accent);
648
+ }
649
+
650
+ .modal-body pre .md-code {
651
+ background: var(--bg-inset);
652
+ padding: 1px 4px;
653
+ border-radius: 3px;
654
+ font-size: var(--text-xs);
655
+ }
656
+
657
+ .modal-footer {
658
+ display: flex;
659
+ gap: var(--sp-2);
660
+ padding: var(--sp-3) var(--sp-5);
661
+ border-top: 1px solid var(--border);
662
+ flex-shrink: 0;
663
+ }
664
+
665
+ .modal-copy-btn {
666
+ background: var(--accent);
667
+ color: #fff;
668
+ border: none;
669
+ border-radius: var(--radius-sm);
670
+ padding: var(--sp-2) var(--sp-4);
671
+ font-size: var(--text-sm);
672
+ font-weight: var(--fw-medium);
673
+ cursor: pointer;
674
+ transition: background 0.15s ease;
675
+ }
676
+
677
+ .modal-copy-btn:hover {
678
+ background: var(--accent-hover);
679
+ }
680
+
681
+ .modal-copy-btn.copied {
682
+ background: var(--green);
683
+ }
684
+
685
+ /* ─── Beads Task Section ─────────────────────── */
686
+ .beads-section {
687
+ margin-top: var(--sp-8);
688
+ margin-bottom: var(--sp-6);
689
+ }
690
+
691
+ .beads-filters {
692
+ display: flex;
693
+ gap: var(--sp-2);
694
+ margin-bottom: var(--sp-3);
695
+ }
696
+
697
+ .beads-filter {
698
+ background: var(--bg-inset);
699
+ border: 1px solid var(--border);
700
+ border-radius: 99px;
701
+ padding: var(--sp-1) var(--sp-3);
702
+ font-size: var(--text-xs);
703
+ font-weight: var(--fw-medium);
704
+ color: var(--text-muted);
705
+ cursor: pointer;
706
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
707
+ letter-spacing: var(--ls-wide);
708
+ }
709
+
710
+ .beads-filter:hover {
711
+ border-color: var(--accent);
712
+ color: var(--accent);
713
+ }
714
+
715
+ .beads-filter.active {
716
+ background: var(--accent);
717
+ color: #fff;
718
+ border-color: var(--accent);
719
+ }
720
+
721
+ /* ─── Beads Status Badges ────────────────────── */
722
+ .st-bead-open {
723
+ background: var(--accent-glow);
724
+ color: var(--accent);
725
+ border: 1px solid var(--accent);
726
+ }
727
+
728
+ .st-bead-progress {
729
+ background: var(--blue-bg);
730
+ color: var(--blue);
731
+ border: 1px solid var(--blue-border);
732
+ }
733
+
734
+ .st-bead-blocked {
735
+ background: var(--yellow-bg);
736
+ color: var(--yellow);
737
+ border: 1px solid var(--yellow-border);
738
+ }
739
+
740
+ .st-bead-deferred {
741
+ background: var(--gray-bg);
742
+ color: var(--gray);
743
+ border: 1px solid var(--gray-border);
744
+ }
745
+
746
+ .st-bead-closed {
747
+ background: var(--green-bg);
748
+ color: var(--green);
749
+ border: 1px solid var(--green-border);
750
+ }
751
+
752
+ /* ─── Beads Filter Separator ─────────────────── */
753
+ .beads-filter-sep {
754
+ width: 1px;
755
+ background: var(--border);
756
+ align-self: stretch;
757
+ margin: 0 var(--sp-1);
758
+ }
759
+
760
+ /* ─── Beads Priority Filter ──────────────────── */
761
+ .beads-prio-filter {
762
+ background: var(--bg-inset);
763
+ border: 1px solid var(--border);
764
+ border-radius: 99px;
765
+ padding: var(--sp-1) var(--sp-3);
766
+ font-size: var(--text-xs);
767
+ font-weight: var(--fw-medium);
768
+ color: var(--text-muted);
769
+ cursor: pointer;
770
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
771
+ letter-spacing: var(--ls-wide);
772
+ }
773
+
774
+ .beads-prio-filter:hover {
775
+ border-color: var(--accent);
776
+ color: var(--accent);
777
+ }
778
+
779
+ .beads-prio-filter.active {
780
+ background: var(--accent);
781
+ color: #fff;
782
+ border-color: var(--accent);
783
+ }
784
+
785
+ /* ─── Beads Task Detail Modal ────────────────── */
786
+ .bead-meta-grid {
787
+ display: grid;
788
+ grid-template-columns: 1fr 1fr;
789
+ gap: var(--sp-3);
790
+ padding: var(--sp-4) 0;
791
+ border-bottom: 1px solid var(--border-light);
792
+ }
793
+
794
+ .bead-meta-item {
795
+ display: flex;
796
+ flex-direction: column;
797
+ gap: 2px;
798
+ }
799
+
800
+ .bead-meta-label {
801
+ font-size: var(--text-xs);
802
+ color: var(--text-faint);
803
+ text-transform: uppercase;
804
+ letter-spacing: var(--ls-wide);
805
+ font-weight: var(--fw-medium);
806
+ }
807
+
808
+ .bead-meta-value {
809
+ font-size: var(--text-sm);
810
+ font-weight: var(--fw-medium);
811
+ color: var(--text);
812
+ }
813
+
814
+ .bead-description {
815
+ padding: var(--sp-4) 0;
816
+ border-bottom: 1px solid var(--border-light);
817
+ white-space: pre-wrap;
818
+ font-size: var(--text-sm);
819
+ line-height: var(--lh-relaxed);
820
+ color: var(--text-muted);
821
+ }
822
+
823
+ .bead-deps {
824
+ padding: var(--sp-4) 0;
825
+ border-bottom: 1px solid var(--border-light);
826
+ }
827
+
828
+ .bead-dep-group {
829
+ display: flex;
830
+ flex-wrap: wrap;
831
+ align-items: center;
832
+ gap: var(--sp-2);
833
+ margin-bottom: var(--sp-2);
834
+ }
835
+
836
+ .bead-dep-group:last-child {
837
+ margin-bottom: 0;
838
+ }
839
+
840
+ .bead-dep-label {
841
+ font-size: var(--text-xs);
842
+ color: var(--text-faint);
843
+ text-transform: uppercase;
844
+ letter-spacing: var(--ls-wide);
845
+ font-weight: var(--fw-medium);
846
+ min-width: 80px;
847
+ }
848
+
849
+ .bead-dep-link {
850
+ display: inline-block;
851
+ font-family: var(--font-mono);
852
+ font-size: var(--text-xs);
853
+ padding: 2px var(--sp-2);
854
+ border-radius: 99px;
855
+ background: var(--accent-glow);
856
+ color: var(--accent);
857
+ border: 1px solid var(--accent);
858
+ cursor: pointer;
859
+ transition: background 0.15s ease, color 0.15s ease;
860
+ text-decoration: none;
861
+ }
862
+
863
+ .bead-dep-link:hover {
864
+ background: var(--accent);
865
+ color: #fff;
866
+ }
867
+
868
+ .bead-timestamps {
869
+ display: flex;
870
+ flex-wrap: wrap;
871
+ gap: var(--sp-4);
872
+ padding: var(--sp-4) 0;
873
+ }
874
+
875
+ .bead-ts-item {
876
+ display: flex;
877
+ flex-direction: column;
878
+ gap: 2px;
879
+ }
880
+
881
+ .bead-ts-label {
882
+ font-size: var(--text-xs);
883
+ color: var(--text-faint);
884
+ text-transform: uppercase;
885
+ letter-spacing: var(--ls-wide);
886
+ font-weight: var(--fw-medium);
887
+ }
888
+
889
+ .bead-ts-value {
890
+ font-size: var(--text-sm);
891
+ color: var(--text-muted);
892
+ }
893
+
894
+ .bead-ts-value[title] {
895
+ border-bottom: 1px dotted var(--text-faint);
896
+ cursor: help;
897
+ }
898
+
899
+ /* ─── Standalone Commands Section ─────────────── */
900
+ .ongoing {
901
+ margin-top: var(--sp-10);
902
+ }
903
+
904
+ .ongoing h2 {
905
+ letter-spacing: var(--ls-wide);
906
+ text-transform: uppercase;
907
+ font-size: var(--text-sm);
908
+ color: var(--text-muted);
909
+ margin-bottom: var(--sp-4);
910
+ border-bottom: 2px solid var(--border);
911
+ padding-bottom: var(--sp-2);
912
+ }
913
+
914
+ /* ─── Footer ──────────────────────────────────── */
915
+ .footer {
916
+ text-align: center;
917
+ font-size: var(--text-xs);
918
+ color: var(--text-faint);
919
+ margin-top: var(--sp-10);
920
+ padding-top: var(--sp-4);
921
+ border-top: 1px solid var(--border-light);
922
+ letter-spacing: var(--ls-wide);
923
+ }
924
+
925
+ /* ─── Utilities ───────────────────────────────── */
926
+ .hidden {
927
+ display: none;
928
+ }
929
+
930
+ /* Build-observability severity + verdict tokens (Plan 4) */
931
+ :root {
932
+ --sev-p0: #dc2626; /* red 600 */
933
+ --sev-p1: #ea580c; /* orange 600 */
934
+ --sev-p2: #ca8a04; /* yellow 600 */
935
+ --sev-p3: #2563eb; /* blue 600 */
936
+ --sev-pass: #16a34a; /* green 600 */
937
+ }
938
+ [data-theme="dark"] {
939
+ --sev-p0: #f87171;
940
+ --sev-p1: #fb923c;
941
+ --sev-p2: #facc15;
942
+ --sev-p3: #60a5fa;
943
+ --sev-pass: #4ade80;
944
+ }
945
+
946
+ /* Build-observability panel layout */
947
+ .panel {
948
+ background: var(--bg-card);
949
+ border: 1px solid var(--border);
950
+ border-radius: var(--radius);
951
+ padding: var(--sp-4) var(--sp-6);
952
+ margin-bottom: var(--sp-6);
953
+ }
954
+ .panel > header {
955
+ display: flex;
956
+ align-items: center;
957
+ gap: var(--sp-3);
958
+ margin-bottom: var(--sp-4);
959
+ flex-wrap: wrap;
960
+ }
961
+ .panel > header h2 {
962
+ margin: 0;
963
+ font-size: var(--text-base);
964
+ font-weight: var(--fw-semi);
965
+ }
966
+ .panel .meta {
967
+ color: var(--text-muted);
968
+ font-size: var(--text-sm);
969
+ }
970
+ .grid { display: grid; gap: var(--sp-4); }
971
+ .grid-2 { grid-template-columns: repeat(2, 1fr); }
972
+ @media (max-width: 640px) { .grid-2 { grid-template-columns: 1fr; } }
973
+
974
+ /* Finding filters */
975
+ .finding-filters {
976
+ display: flex;
977
+ gap: var(--sp-2);
978
+ flex-wrap: wrap;
979
+ margin-bottom: var(--sp-4);
980
+ }
981
+ .finding-filters button {
982
+ padding: var(--sp-1) var(--sp-3);
983
+ border: 1px solid var(--border);
984
+ border-radius: var(--radius-sm);
985
+ background: var(--bg-inset);
986
+ color: var(--text);
987
+ font-size: var(--text-sm);
988
+ cursor: pointer;
989
+ }
990
+ .finding-filters button:hover,
991
+ .finding-filters button.active {
992
+ background: var(--accent);
993
+ border-color: var(--accent);
994
+ color: #fff;
995
+ }
996
+
997
+ /* Findings list */
998
+ .findings {
999
+ list-style: none;
1000
+ padding: 0;
1001
+ margin: 0;
1002
+ display: flex;
1003
+ flex-direction: column;
1004
+ gap: var(--sp-3);
1005
+ }
1006
+ .finding {
1007
+ background: var(--bg-inset);
1008
+ border: 1px solid var(--border-light);
1009
+ border-radius: var(--radius-sm);
1010
+ padding: var(--sp-3) var(--sp-4);
1011
+ }
1012
+ .finding header {
1013
+ display: flex;
1014
+ align-items: center;
1015
+ gap: var(--sp-2);
1016
+ flex-wrap: wrap;
1017
+ margin-bottom: var(--sp-2);
1018
+ }
1019
+ .finding-id {
1020
+ font-family: var(--font-mono, monospace);
1021
+ font-size: var(--text-xs);
1022
+ color: var(--text-muted);
1023
+ background: var(--bg-card);
1024
+ border: 1px solid var(--border);
1025
+ border-radius: 4px;
1026
+ padding: 1px var(--sp-1);
1027
+ }
1028
+ .finding .lens {
1029
+ font-size: var(--text-xs);
1030
+ color: var(--text-muted);
1031
+ }
1032
+ .finding .title {
1033
+ font-size: var(--text-sm);
1034
+ font-weight: var(--fw-semi);
1035
+ flex: 1;
1036
+ }
1037
+ .finding p { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }
1038
+ .empty { color: var(--text-muted); font-size: var(--text-sm); text-align: center; padding: var(--sp-4); }
1039
+
1040
+ /* ── Mermaid diagrams ─────────────────────────────────────────────────────────
1041
+ The build renders mermaid to inline SVG via mmdc, then sanitizeSvg() +
1042
+ rehype-sanitize strip the SVG's own <script>, <foreignObject>, AND <style>
1043
+ for security. Stripping <style> means the diagram arrives unstyled (nodes
1044
+ default to a black fill). These theme-token rules restyle the SVG so nodes,
1045
+ edges, arrowheads, and labels render correctly — and follow light/dark mode.
1046
+ Authors must render with htmlLabels:false (the generator forces this) so node
1047
+ labels are native <text>/<tspan> rather than stripped <foreignObject> HTML. */
1048
+ figure.mermaid { margin: var(--sp-5) 0; text-align: center; }
1049
+ figure.mermaid svg { max-width: 100%; height: auto; }
1050
+ /* Node shapes */
1051
+ figure.mermaid svg .node rect,
1052
+ figure.mermaid svg .node circle,
1053
+ figure.mermaid svg .node ellipse,
1054
+ figure.mermaid svg .node polygon,
1055
+ figure.mermaid svg .node path {
1056
+ fill: var(--bg-inset);
1057
+ stroke: var(--border);
1058
+ stroke-width: 1px;
1059
+ }
1060
+ /* Background helper rects mermaid emits behind labels */
1061
+ figure.mermaid svg .node .label-container { fill: var(--bg-inset); stroke: var(--border); }
1062
+ figure.mermaid svg rect.background { fill: none; stroke: none; }
1063
+ /* Labels (rendered as <text>/<tspan> when htmlLabels:false) */
1064
+ figure.mermaid svg .nodeLabel,
1065
+ figure.mermaid svg .node text,
1066
+ figure.mermaid svg text.nodeLabel,
1067
+ figure.mermaid svg .label text,
1068
+ figure.mermaid svg span.nodeLabel {
1069
+ fill: var(--text);
1070
+ color: var(--text);
1071
+ font-family: var(--font-sans);
1072
+ }
1073
+ /* Edges: thin strokes, not filled blobs */
1074
+ figure.mermaid svg .edgePath path,
1075
+ figure.mermaid svg path.flowchart-link,
1076
+ figure.mermaid svg .flowchart-link {
1077
+ fill: none;
1078
+ stroke: var(--text-faint);
1079
+ stroke-width: 1.5px;
1080
+ }
1081
+ /* Arrowheads */
1082
+ figure.mermaid svg marker path,
1083
+ figure.mermaid svg .marker {
1084
+ fill: var(--text-faint);
1085
+ stroke: var(--text-faint);
1086
+ }
1087
+ figure.mermaid svg .edgeLabel,
1088
+ figure.mermaid svg .edgeLabel text { fill: var(--text-muted); color: var(--text-muted); }
1089
+
1090
+ /* ============================================================================
1091
+ * guides.css — component + layout styles for `scaffold guides` reference pages.
1092
+ *
1093
+ * Pairs with lib/dashboard-theme.css (the token source) and src/guides/chrome.ts
1094
+ * (the behavior). Styles the guide CHROME (.topbar, .layout, .rail, nav.toc,
1095
+ * .content) and the markdown DIRECTIVES (callouts, sev chips, filter-tables,
1096
+ * charts, tabs, citations) plus base prose typography.
1097
+ *
1098
+ * DESIGN SYSTEM: all COLORS come from dashboard-theme.css tokens, and spacing
1099
+ * uses the --sp-* scale wherever it maps. The few structural layout constants
1100
+ * (topbar height, rail/drawer width, chart label column, card min) are declared
1101
+ * as local custom properties below; a handful of sub-scale UI values (chip/bar
1102
+ * sizing, em-based inline-code padding) and the responsive breakpoint are
1103
+ * literal because no token expresses them. Both themes are covered because every
1104
+ * color is a token.
1105
+ * ==========================================================================*/
1106
+
1107
+ :root {
1108
+ --topbar-h: 52px; /* sticky topbar height; rail sticky offset keys off it */
1109
+ --rail-w: 260px; /* desktop TOC sidebar column */
1110
+ --drawer-w: 280px; /* mobile off-canvas TOC drawer */
1111
+ --card-min: 260px; /* index card min track width */
1112
+ --chart-label-w: 90px; /* chart row label column min */
1113
+ }
1114
+
1115
+ /* ── Base / reset on top of the token base in dashboard-theme.css ─────────── */
1116
+ .content a { color: var(--accent); text-decoration: none; }
1117
+ .content a:hover { text-decoration: underline; }
1118
+ .content strong { font-weight: var(--fw-semi); }
1119
+ .content hr { border: 0; border-top: 1px solid var(--border-light); margin: var(--sp-6) 0; }
1120
+
1121
+ /* Consistent keyboard focus for every interactive control (a11y). */
1122
+ .topbar button:focus-visible,
1123
+ .copy-btn:focus-visible,
1124
+ .tab-btn:focus-visible,
1125
+ .filter-input:focus-visible,
1126
+ nav.toc a:focus-visible,
1127
+ .guide-card:focus-visible,
1128
+ .content a:focus-visible {
1129
+ outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm);
1130
+ }
1131
+
1132
+ /* ── Topbar ────────────────────────────────────────────────────────────────*/
1133
+ .topbar {
1134
+ position: sticky; top: 0; z-index: 60; height: var(--topbar-h);
1135
+ display: flex; align-items: center; gap: var(--sp-3);
1136
+ padding: 0 var(--page-pad);
1137
+ background: var(--bg-card); border-bottom: 1px solid var(--border);
1138
+ }
1139
+ .topbar h1 {
1140
+ flex: 1; min-width: 0; margin: 0;
1141
+ font-size: var(--text-lg); font-weight: var(--fw-bold);
1142
+ letter-spacing: var(--ls-tight);
1143
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1144
+ }
1145
+ .topbar button {
1146
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
1147
+ border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
1148
+ line-height: 1; cursor: pointer; font-family: inherit;
1149
+ }
1150
+ .topbar button:hover { background: var(--bg-hover); border-color: var(--accent); }
1151
+ .nav-toggle { display: none; }
1152
+
1153
+ /* ── Layout: sticky sidebar TOC + reading-width content ──────────────────── */
1154
+ .layout {
1155
+ max-width: var(--max-w); margin: 0 auto;
1156
+ display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr);
1157
+ gap: var(--sp-8); padding: 0 var(--page-pad);
1158
+ }
1159
+ .rail {
1160
+ position: sticky; top: var(--topbar-h); align-self: start;
1161
+ height: calc(100vh - var(--topbar-h)); overflow-y: auto;
1162
+ padding: var(--sp-5) 0; border-right: 1px solid var(--border-light);
1163
+ }
1164
+ .content { min-width: 0; padding: var(--sp-6) 0 var(--sp-10); }
1165
+
1166
+ /* Backdrop behind the mobile drawer (toggled with the rail via chrome.ts). */
1167
+ .rail-backdrop { display: none; }
1168
+ /* In-drawer close button — hidden on desktop (the rail is a static sidebar). */
1169
+ .rail-close { display: none; }
1170
+
1171
+ /* ── Table of contents (scrollspy marks a.active) ────────────────────────── */
1172
+ nav.toc ul { list-style: none; margin: 0; padding: 0; }
1173
+ nav.toc li { margin: 0; }
1174
+ nav.toc a {
1175
+ display: block; padding: var(--sp-1) var(--sp-3); line-height: 1.35;
1176
+ color: var(--text-muted); font-size: var(--text-sm);
1177
+ text-decoration: none; border-left: 2px solid transparent;
1178
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
1179
+ }
1180
+ nav.toc a:hover { color: var(--text); background: var(--bg-hover); }
1181
+ nav.toc a.active {
1182
+ color: var(--accent); border-left-color: var(--accent);
1183
+ background: var(--accent-glow); font-weight: var(--fw-medium);
1184
+ }
1185
+ nav.toc li.toc-3 a { padding-left: var(--sp-6); font-size: var(--text-xs); }
1186
+
1187
+ /* ── Prose typography ──────────────────────────────────────────────────────*/
1188
+ .content h2 {
1189
+ font-size: var(--text-xl); letter-spacing: var(--ls-tight);
1190
+ margin: var(--sp-8) 0 var(--sp-3); padding-bottom: var(--sp-2);
1191
+ border-bottom: 1px solid var(--border-light); scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
1192
+ }
1193
+ .content h3 {
1194
+ font-size: var(--text-lg); margin: var(--sp-5) 0 var(--sp-2);
1195
+ scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
1196
+ }
1197
+ .content p { margin: var(--sp-3) 0; line-height: var(--lh-relaxed); }
1198
+ .content ul, .content ol { margin: var(--sp-3) 0; padding-left: var(--sp-6); }
1199
+ .content li { margin: var(--sp-1) 0; line-height: var(--lh-relaxed); }
1200
+ .content blockquote {
1201
+ margin: var(--sp-3) 0; padding: var(--sp-1) var(--sp-4);
1202
+ border-left: 3px solid var(--border); color: var(--text-muted);
1203
+ }
1204
+
1205
+ /* ── Inline code + code blocks (chrome.ts wraps <pre> in .code + .copy-btn) ──*/
1206
+ .content code {
1207
+ font-family: var(--font-mono); font-size: 0.85em;
1208
+ background: var(--bg-inset); padding: 0.12em 0.4em; border-radius: var(--radius-sm);
1209
+ }
1210
+ .content .code { position: relative; margin: var(--sp-3) 0; }
1211
+ .content .code pre {
1212
+ margin: 0; padding: var(--sp-3) var(--sp-4); overflow-x: auto;
1213
+ background: var(--bg-inset); border: 1px solid var(--border-light);
1214
+ border-radius: var(--radius-sm); font-family: var(--font-mono);
1215
+ font-size: var(--text-sm); line-height: var(--lh-relaxed);
1216
+ }
1217
+ .content .code pre code { background: none; padding: 0; font-size: inherit; }
1218
+ .copy-btn {
1219
+ position: absolute; top: var(--sp-1); right: var(--sp-1);
1220
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text-muted);
1221
+ border-radius: var(--radius-sm); font-size: var(--text-xs); padding: var(--sp-1) var(--sp-2);
1222
+ cursor: pointer; opacity: 0.85; font-family: inherit;
1223
+ }
1224
+ .copy-btn:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }
1225
+
1226
+ /* ── Callouts ─ (border-color BEFORE border-left-color so the accent wins) ── */
1227
+ .callout {
1228
+ margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4);
1229
+ border: 1px solid var(--border); border-left-width: 3px;
1230
+ border-radius: var(--radius-sm); background: var(--bg-card);
1231
+ }
1232
+ .callout > :first-child { margin-top: 0; }
1233
+ .callout > :last-child { margin-bottom: 0; }
1234
+ .callout-note, .callout-info { background: var(--blue-bg); border-color: var(--blue-border); border-left-color: var(--blue); }
1235
+ .callout-tip { background: var(--green-bg); border-color: var(--green-border); border-left-color: var(--green); }
1236
+ .callout-warning { background: var(--yellow-bg); border-color: var(--yellow-border); border-left-color: var(--yellow); }
1237
+ .callout-danger { background: var(--red-bg); border-color: var(--red-border); border-left-color: var(--red); }
1238
+
1239
+ /* ── Severity chips (:sev) — tight pill, sub-scale vertical padding ───────── */
1240
+ .sev {
1241
+ display: inline-block; font-size: var(--text-xs); font-weight: var(--fw-semi);
1242
+ padding: 1px var(--sp-2); border-radius: 999px; line-height: 1.5;
1243
+ border: 1px solid var(--border); background: var(--bg-inset); color: var(--text-muted);
1244
+ white-space: nowrap;
1245
+ }
1246
+ .sev-p0 { color: var(--sev-p0); border-color: var(--sev-p0); }
1247
+ .sev-p1 { color: var(--sev-p1); border-color: var(--sev-p1); }
1248
+ .sev-p2 { color: var(--sev-p2); border-color: var(--sev-p2); }
1249
+ .sev-p3 { color: var(--sev-p3); border-color: var(--sev-p3); }
1250
+ .sev-pass { color: var(--sev-pass); border-color: var(--sev-pass); }
1251
+
1252
+ /* ── Citations (:cite) — inline provenance refs ──────────────────────────── */
1253
+ .fp, .cite-advisory {
1254
+ font-family: var(--font-mono); font-size: 0.82em;
1255
+ padding: 0.05em 0.35em; border-radius: var(--radius-sm);
1256
+ background: var(--bg-inset); border: 1px solid var(--border-light);
1257
+ }
1258
+ .fp { color: var(--accent); }
1259
+ .cite-advisory { color: var(--text-faint); border-style: dashed; }
1260
+
1261
+ /* ── Tables + filter-tables ──────────────────────────────────────────────── */
1262
+ .content table { width: 100%; border-collapse: collapse; margin: var(--sp-3) 0; font-size: var(--text-sm); }
1263
+ .content th, .content td { text-align: left; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border-light); vertical-align: top; }
1264
+ .content th {
1265
+ color: var(--text-muted); font-weight: var(--fw-semi); font-size: var(--text-xs);
1266
+ text-transform: uppercase; letter-spacing: var(--ls-wide); border-bottom-color: var(--border);
1267
+ }
1268
+ .content tbody tr:hover { background: var(--bg-hover); }
1269
+ .content td code { white-space: nowrap; }
1270
+ .filter-table { margin: var(--sp-4) 0; }
1271
+ .filter-input {
1272
+ width: 100%; max-width: 320px; margin-bottom: var(--sp-2);
1273
+ padding: var(--sp-2) var(--sp-3); font-family: inherit; font-size: var(--text-sm);
1274
+ color: var(--text); background: var(--bg-card);
1275
+ border: 1px solid var(--border); border-radius: var(--radius-sm);
1276
+ }
1277
+ .filter-input:focus { border-color: var(--accent); }
1278
+
1279
+ /* ── Charts (:::chart) — label + proportional bar (fill carries inline width%) */
1280
+ .chart-block { margin: var(--sp-4) 0; }
1281
+ .chart-row {
1282
+ display: grid; grid-template-columns: minmax(var(--chart-label-w), 24%) 1fr;
1283
+ align-items: center; gap: var(--sp-3); margin: var(--sp-1) 0;
1284
+ }
1285
+ .chart-label {
1286
+ font-size: var(--text-sm); color: var(--text-muted); text-align: right;
1287
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1288
+ }
1289
+ .chart-row .chart-bar {
1290
+ height: 0.9em; min-width: 2px; background: var(--accent);
1291
+ border-radius: var(--radius-sm);
1292
+ }
1293
+
1294
+ /* ── Tabs (::::tabs / :::tab) ─────────────────────────────────────────────── */
1295
+ .tabs { margin: var(--sp-4) 0; }
1296
+ .tablist { display: flex; flex-wrap: wrap; gap: var(--sp-1); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-3); }
1297
+ .tab-btn {
1298
+ background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
1299
+ padding: var(--sp-2) var(--sp-3); color: var(--text-muted);
1300
+ font-family: inherit; font-size: var(--text-sm); font-weight: var(--fw-medium); cursor: pointer;
1301
+ }
1302
+ .tab-btn:hover { color: var(--text); }
1303
+ .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
1304
+ .tabpane { display: none; }
1305
+ .tabpane.active { display: block; }
1306
+
1307
+ /* ── Mermaid diagrams ────────────────────────────────────────────────────── */
1308
+ .content figure { margin: var(--sp-4) 0; text-align: center; }
1309
+ .content figure svg, .content > svg, .content .mermaid svg { max-width: 100%; height: auto; }
1310
+
1311
+ /* ── Index page: category card grid ──────────────────────────────────────── */
1312
+ .content .lead { color: var(--text-muted); font-size: var(--text-base); max-width: 60ch; margin-top: var(--sp-2); }
1313
+ .guide-cards {
1314
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--card-min), 100%), 1fr));
1315
+ gap: var(--sp-4); margin: var(--sp-4) 0 var(--sp-6);
1316
+ }
1317
+ .guide-card {
1318
+ display: flex; flex-direction: column; gap: var(--sp-2);
1319
+ padding: var(--sp-4); background: var(--bg-card);
1320
+ border: 1px solid var(--border); border-radius: var(--radius);
1321
+ color: inherit; text-decoration: none;
1322
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
1323
+ }
1324
+ .guide-card:hover { border-color: var(--accent); box-shadow: var(--shadow-md); text-decoration: none; }
1325
+ .guide-card-title { font-weight: var(--fw-semi); color: var(--accent); font-size: var(--text-base); }
1326
+ .guide-card-desc { color: var(--text-muted); font-size: var(--text-sm); line-height: var(--lh-normal); }
1327
+
1328
+ /* ── Responsive: TOC becomes an off-canvas drawer (chrome.ts toggles .open) ──*/
1329
+ /* 860px is literal — media queries cannot read custom properties. Revisit it if
1330
+ --topbar-h / --rail-w / --drawer-w change (the drawer sticky offsets key off them). */
1331
+ @media (max-width: 860px) {
1332
+ .nav-toggle { display: inline-flex; align-items: center; }
1333
+ .layout { grid-template-columns: 1fr; gap: 0; }
1334
+ .rail {
1335
+ position: fixed; top: var(--topbar-h); left: 0; bottom: 0; width: var(--drawer-w); z-index: 50;
1336
+ height: auto; background: var(--bg-card); border-right: 1px solid var(--border);
1337
+ padding: var(--sp-4); box-shadow: var(--shadow-lg);
1338
+ transform: translateX(-100%); transition: transform 0.2s ease, visibility 0.2s ease;
1339
+ /* Closed drawer is off-screen AND removed from tab order / pointer events. */
1340
+ visibility: hidden; pointer-events: none;
1341
+ }
1342
+ .rail.open { transform: translateX(0); visibility: visible; pointer-events: auto; }
1343
+ .rail-close {
1344
+ display: block; margin-left: auto; margin-bottom: var(--sp-2);
1345
+ background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
1346
+ border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
1347
+ line-height: 1; cursor: pointer; font-family: inherit;
1348
+ }
1349
+ .rail-close:hover { background: var(--bg-hover); border-color: var(--accent); }
1350
+ .rail-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1351
+ .rail-backdrop {
1352
+ display: block; position: fixed; inset: var(--topbar-h) 0 0 0;
1353
+ background: var(--scrim); z-index: 49;
1354
+ opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
1355
+ }
1356
+ .rail.open ~ .rail-backdrop { opacity: 1; pointer-events: auto; }
1357
+ }
1358
+ </style>
1359
+ <script>(function(){try{var t=localStorage.getItem('guide-theme');if(!t&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches)t='dark';if(t)document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
1360
+ </head>
1361
+ <body>
1362
+ <header class="topbar">
1363
+ <button data-action="nav" class="nav-toggle" aria-label="Toggle navigation"
1364
+ aria-expanded="false" aria-controls="guide-toc">☰</button>
1365
+ <h1>Install, Update &amp; Adopt</h1>
1366
+ <button data-action="theme" class="theme-toggle" aria-label="Toggle theme">◐</button>
1367
+ </header>
1368
+ <div class="layout">
1369
+ <aside class="rail" id="guide-toc">
1370
+ <button class="rail-close" data-action="nav" aria-label="Close navigation">✕</button>
1371
+ <nav class="toc" aria-label="Table of contents"><ul><li class="toc-2"><a href="#overview">Overview</a></li><li class="toc-2"><a href="#installing">Installing</a></li><li class="toc-2"><a href="#keeping-current">Keeping current</a></li><li class="toc-2"><a href="#starting-fresh-vs-adopting">Starting fresh vs. adopting</a></li><li class="toc-3"><a href="#scaffold-init">scaffold init</a></li><li class="toc-3"><a href="#scaffold-adopt">scaffold adopt</a></li><li class="toc-2"><a href="#troubleshooting">Troubleshooting</a></li><li class="toc-2"><a href="#see-also">See also</a></li></ul></nav>
1372
+ </aside>
1373
+ <main class="content"><h2 id="overview">Overview</h2>
1374
+ <p>Scaffold ships as one tool with two install surfaces: the <strong><code>scaffold</code> CLI</strong>
1375
+ (distributed on npm as <code>@zigrivers/scaffold</code> and via Homebrew) and an optional
1376
+ <strong>Claude Code plugin</strong> that auto-activates the runner and pipeline-reference
1377
+ skills. The CLI is the core; the plugin only adds interactive guidance on top of
1378
+ it. The published npm package exposes the single <code>scaffold</code> binary
1379
+ (<span class="fp" data-path="package.json:37">package.json:37</span>) and bundles the compiled CLI plus the pipeline content,
1380
+ knowledge entries, and skills (<span class="fp" data-path="package.json:27">package.json:27</span>).</p>
1381
+ <p>This guide is for <strong>users</strong> installing, updating, and bootstrapping projects.
1382
+ Maintainers releasing Scaffold itself follow a separate flow in
1383
+ <code>docs/architecture/operations-runbook.md</code> <span class="cite-advisory" data-path="docs/architecture/operations-runbook.md:326">docs/architecture/operations-runbook.md:326</span>.</p>
1384
+ <div class="callout callout-note"><p><strong>Prerequisite — Node.js 18.17+.</strong> The CLI requires Node.js <code>>=18.17.0</code>
1385
+ (<span class="fp" data-path="package.json:34">package.json:34</span>). The Homebrew formula installs and manages Node as a
1386
+ dependency, so you don't install Node separately; the npm install uses your
1387
+ system Node.</p></div>
1388
+ <h2 id="installing">Installing</h2>
1389
+ <p>Pick one of the three surfaces below. The npm and Homebrew tabs install the CLI;
1390
+ the plugin tab adds the optional Claude Code skills on top.</p>
1391
+ <div class="tabs"><div class="tablist" role="tablist"><button id="tab-0-0" class="tab-btn active" role="tab" data-tab="0" aria-controls="tabpane-0-0" aria-selected="true" tabindex="0">npm</button><button id="tab-0-1" class="tab-btn" role="tab" data-tab="1" aria-controls="tabpane-0-1" aria-selected="false" tabindex="-1">Homebrew</button><button id="tab-0-2" class="tab-btn" role="tab" data-tab="2" aria-controls="tabpane-0-2" aria-selected="false" tabindex="-1">Claude Code plugin</button></div><div id="tabpane-0-0" class="tabpane active" role="tabpanel" data-tab="0" aria-labelledby="tab-0-0" tabindex="0"><pre><code class="language-bash">npm install -g @zigrivers/scaffold
1392
+ </code></pre><p>Verify with <code>scaffold version</code>. This is the recommended path — it uses your
1393
+ system Node and updates with <code>npm update -g</code>.</p></div><div id="tabpane-0-1" class="tabpane" role="tabpanel" data-tab="1" aria-labelledby="tab-0-1" tabindex="0"><pre><code class="language-bash">brew tap zigrivers/scaffold
1394
+ brew install scaffold
1395
+ </code></pre><p>The formula installs and manages Node as a dependency, so no separate Node
1396
+ install is needed. Verify with <code>scaffold version</code>.</p></div><div id="tabpane-0-2" class="tabpane" role="tabpanel" data-tab="2" aria-labelledby="tab-0-2" tabindex="0"><p>Inside a Claude Code session:</p><pre><code class="language-text">/plugin marketplace add zigrivers/scaffold
1397
+ /plugin install scaffold@zigrivers-scaffold
1398
+ </code></pre><p>This is <strong>optional</strong> and complements the CLI — it auto-activates the Scaffold
1399
+ Runner and pipeline-reference skills for interactive guidance. Everything the
1400
+ plugin does is also reachable via <code>scaffold run &#x3C;step></code>. CLI-only users don't
1401
+ need it: <code>scaffold init</code> installs the skills automatically and later CLI
1402
+ commands keep them current.</p></div></div>
1403
+ <h2 id="keeping-current">Keeping current</h2>
1404
+ <p>Check what you have and whether a newer release exists with <code>scaffold version</code>
1405
+ (or <code>scaffold update --check-only</code>); both query the npm registry for the latest
1406
+ <code>@zigrivers/scaffold</code> (<span class="fp" data-path="src/cli/commands/version.ts:93">src/cli/commands/version.ts:93</span>). <code>scaffold update</code>
1407
+ detects how you installed and prints the right upgrade command rather than
1408
+ running the install for you — Homebrew installs map to <code>brew upgrade scaffold</code>
1409
+ (<span class="fp" data-path="src/cli/commands/update.ts:47">src/cli/commands/update.ts:47</span>), npm-global installs to
1410
+ <code>npm update -g @zigrivers/scaffold</code> (<span class="fp" data-path="src/cli/commands/update.ts:51">src/cli/commands/update.ts:51</span>), and
1411
+ anything else falls back to <code>npx @zigrivers/scaffold@latest</code>
1412
+ (<span class="fp" data-path="src/cli/commands/update.ts:55">src/cli/commands/update.ts:55</span>).</p>
1413
+ <p>The Homebrew command it prints is exactly <code>brew upgrade scaffold</code> — it does
1414
+ <strong>not</strong> include the <code>brew update</code> prefix. Homebrew users must prepend it
1415
+ themselves and run <code>brew update &#x26;&#x26; brew upgrade scaffold</code>, or the upgrade can
1416
+ silently no-op against a stale tap cache (see the danger callout below).</p>
1417
+ <p>Run the upgrade for your channel:</p>
1418
+ <div class="tabs"><div class="tablist" role="tablist"><button id="tab-1-0" class="tab-btn active" role="tab" data-tab="0" aria-controls="tabpane-1-0" aria-selected="true" tabindex="0">npm</button><button id="tab-1-1" class="tab-btn" role="tab" data-tab="1" aria-controls="tabpane-1-1" aria-selected="false" tabindex="-1">Homebrew</button><button id="tab-1-2" class="tab-btn" role="tab" data-tab="2" aria-controls="tabpane-1-2" aria-selected="false" tabindex="-1">Claude Code plugin</button></div><div id="tabpane-1-0" class="tabpane active" role="tabpanel" data-tab="0" aria-labelledby="tab-1-0" tabindex="0"><pre><code class="language-bash">npm update -g @zigrivers/scaffold
1419
+ </code></pre></div><div id="tabpane-1-1" class="tabpane" role="tabpanel" data-tab="1" aria-labelledby="tab-1-1" tabindex="0"><pre><code class="language-bash">brew update &#x26;&#x26; brew upgrade scaffold
1420
+ </code></pre></div><div id="tabpane-1-2" class="tabpane" role="tabpanel" data-tab="2" aria-labelledby="tab-1-2" tabindex="0"><pre><code class="language-text">/plugin marketplace update zigrivers-scaffold
1421
+ </code></pre></div></div>
1422
+ <div class="callout callout-danger"><p><strong>Homebrew: run <code>brew update</code> <em>before</em> <code>brew upgrade</code>.</strong> <code>brew outdated</code> and
1423
+ <code>brew upgrade</code> both read from the local tap cache. Without a preceding
1424
+ <code>brew update</code>, the cache stays stale and a freshly-published release reports
1425
+ "already installed" / nothing-outdated even though a newer formula is live on
1426
+ GitHub. Always upgrade with <code>brew update &#x26;&#x26; brew upgrade scaffold</code>
1427
+ <span class="cite-advisory" data-path="docs/architecture/operations-runbook.md:326">docs/architecture/operations-runbook.md:326</span>.</p></div>
1428
+ <p>After upgrading the CLI, run <code>scaffold status</code> inside any existing project. The
1429
+ state manager auto-migrates old step keys, drops retired steps, and normalizes
1430
+ artifact paths — no manual editing of <code>.scaffold/state.json</code> is needed.</p>
1431
+ <h2 id="starting-fresh-vs-adopting">Starting fresh vs. adopting</h2>
1432
+ <p>Both <code>scaffold init</code> and <code>scaffold adopt</code> operate on the current project
1433
+ directory. Use <strong><code>init</code></strong> to set up a project from scratch and <strong><code>adopt</code></strong> to
1434
+ bootstrap pipeline state from documentation you already have.</p>
1435
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 477.609px; background-color: transparent;" viewBox="0 0 477.609375 579.2156372070312" role="graphics-document document">#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#666;stroke:#666;}#my-svg .marker.cross{stroke:#666;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#eee;stroke:#999;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#666;stroke-width:1px;}#my-svg .flowchart-link{stroke:#666;fill:none;}#my-svg .edgeLabel{background-color:white;text-align:center;}#my-svg .edgeLabel p{background-color:white;}#my-svg .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:white;text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:white;padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:white;fill:white;}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:#999;}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:#999;filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}<g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M247.137,57L247.137,61.167C247.137,65.333,247.137,73.667,247.137,81.333C247.137,89,247.137,96,247.137,99.5L247.137,103" id="my-svg-L_A_B_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M195.088,280.567L182.446,295.325C169.804,310.083,144.519,339.599,131.877,359.774C119.234,379.949,119.234,390.782,119.234,396.199L119.234,401.616" id="my-svg-L_B_C_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M299.185,280.567L311.828,295.325C324.47,310.083,349.754,339.599,362.397,359.774C375.039,379.949,375.039,390.782,375.039,396.199L375.039,401.616" id="my-svg-L_B_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M119.234,472.216L119.234,476.382C119.234,480.549,119.234,488.882,129.379,496.975C139.523,505.068,159.812,512.92,169.957,516.846L180.101,520.772" id="my-svg-L_C_E_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M375.039,472.216L375.039,476.382C375.039,480.549,375.039,488.882,364.895,496.975C354.75,505.068,334.461,512.92,324.317,516.846L314.172,520.772" id="my-svg-L_D_E_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel" transform="translate(119.234375, 369.1156311035156)"><g class="label" transform="translate(0, -10.5)"><g><rect class="background" style="" x="-58.1171875" y="-1" width="116.234375" height="23"></rect><text y="-10.1" text-anchor="middle" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"><tspan class="text-inner-tspan">No</tspan><tspan class="text-inner-tspan"> /</tspan><tspan class="text-inner-tspan"> brand</tspan><tspan class="text-inner-tspan"> new</tspan></tspan></text></g></g></g><g class="edgeLabel" transform="translate(375.0390625, 369.1156311035156)"><g class="label" transform="translate(0, -10.5)"><g><rect class="background" style="" x="-74.15625" y="-1" width="148.3125" height="23"></rect><text y="-10.1" text-anchor="middle" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"><tspan class="text-inner-tspan">Yes,</tspan><tspan class="text-inner-tspan"> already</tspan><tspan class="text-inner-tspan"> written</tspan></tspan></text></g></g></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-A-0" transform="translate(247.13671875, 32.5)"><rect class="basic label-container" style="" x="-114.078125" y="-24.5" width="228.15625" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">New</tspan><tspan class="text-inner-tspan"> or</tspan><tspan class="text-inner-tspan"> existing</tspan><tspan class="text-inner-tspan"> project</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-B-1" transform="translate(247.13671875, 219.8078155517578)"><polygon points="112.80781173706055,0 225.6156234741211,-112.80781173706055 112.80781173706055,-225.6156234741211 0,-112.80781173706055" class="label-container" transform="translate(-112.30781173706055, 112.80781173706055)"></polygon><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">Have</tspan><tspan class="text-inner-tspan"> matching</tspan><tspan class="text-inner-tspan"> docs</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">PRD,</tspan><tspan class="text-inner-tspan"> tech-stack,</tspan><tspan class="text-inner-tspan"> etc.?</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-C-3" transform="translate(119.234375, 438.9156303405762)"><rect class="basic label-container" style="" x="-111.234375" y="-33.29999923706055" width="222.46875" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">scaffold</tspan><tspan class="text-inner-tspan"> init</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">wizard</tspan><tspan class="text-inner-tspan"> +</tspan><tspan class="text-inner-tspan"> config</tspan><tspan class="text-inner-tspan"> +</tspan><tspan class="text-inner-tspan"> skills</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-D-5" transform="translate(375.0390625, 438.9156303405762)"><rect class="basic label-container" style="" x="-94.5703125" y="-33.29999923706055" width="189.140625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">scaffold</tspan><tspan class="text-inner-tspan"> init,</tspan><tspan class="text-inner-tspan"> then</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">scaffold</tspan><tspan class="text-inner-tspan"> adopt</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-E-7" transform="translate(247.13671875, 546.7156295776367)"><rect class="basic label-container" style="" x="-88.8359375" y="-24.5" width="177.671875" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">Run</tspan><tspan class="text-inner-tspan"> the</tspan><tspan class="text-inner-tspan"> pipeline</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
1436
+ <h3 id="scaffold-init"><code>scaffold init</code></h3>
1437
+ <p><code>scaffold init</code> initializes Scaffold for the current project
1438
+ (<span class="fp" data-path="src/cli/commands/init.ts:152">src/cli/commands/init.ts:152</span>): it runs a wizard, writes <code>.scaffold/</code>
1439
+ config and state, and installs the skills. The wizard detects whether the
1440
+ directory is brand new or already has code and suggests a methodology preset
1441
+ (<code>mvp</code>, <code>deep</code>, or <code>custom</code>) accordingly. To re-initialize a project that
1442
+ already has a <code>.scaffold/</code> directory, pass <code>--force</code> — it backs up the existing
1443
+ state before reinitializing (<span class="fp" data-path="src/cli/commands/init.ts:157">src/cli/commands/init.ts:157</span>).</p>
1444
+ <h3 id="scaffold-adopt"><code>scaffold adopt</code></h3>
1445
+ <p><code>scaffold adopt</code> adopts an existing project into Scaffold
1446
+ (<span class="fp" data-path="src/cli/commands/adopt.ts:168">src/cli/commands/adopt.ts:168</span>). It classifies the project as
1447
+ <code>greenfield</code>, <code>brownfield</code>, or <code>v1-migration</code>
1448
+ (<span class="fp" data-path="src/project/adopt.ts:72">src/project/adopt.ts:72</span>), discovers which pipeline outputs (PRD,
1449
+ tech-stack, coding-standards, etc.) you already have on disk, and marks the
1450
+ corresponding steps complete so you don't re-run work you've already done.
1451
+ Preview the changes first with <code>--dry-run</code>, which reports what it would do
1452
+ without writing anything (<span class="fp" data-path="src/cli/commands/adopt.ts:173">src/cli/commands/adopt.ts:173</span>).</p>
1453
+ <p>The usual sequence for an existing codebase: <code>scaffold init</code> (create the config
1454
+ and state), then <code>scaffold adopt</code> (backfill state from existing docs), then pick
1455
+ up the pipeline at the first incomplete step.</p>
1456
+ <h2 id="troubleshooting">Troubleshooting</h2>
1457
+ <div class="callout callout-tip"><p><strong><code>scaffold: command not found</code> after npm install.</strong> Your global npm <code>bin</code>
1458
+ directory isn't on <code>PATH</code>. Find it with <code>echo "$(npm prefix -g)/bin"</code> (the
1459
+ binaries live in the <code>bin</code> subdirectory of <code>npm prefix -g</code>) and add that
1460
+ directory to your shell <code>PATH</code>, or reinstall with a prefix that's already on
1461
+ <code>PATH</code>.</p></div>
1462
+ <ul>
1463
+ <li><strong>Homebrew says "already installed" after a release.</strong> You skipped
1464
+ <code>brew update</code>. Run <code>brew update &#x26;&#x26; brew upgrade scaffold</code> (see the danger
1465
+ callout above).</li>
1466
+ <li><strong>Wrong version reported.</strong> Run <code>scaffold version</code> to see the installed
1467
+ version, Node version, platform, and the latest registry version side by
1468
+ side. If two installs collide (e.g. both npm-global and Homebrew), remove one.</li>
1469
+ <li><strong>Node too old.</strong> The CLI needs Node 18.17+ (<span class="fp" data-path="package.json:34">package.json:34</span>). Upgrade
1470
+ Node, or use the Homebrew install, which installs and manages Node as a
1471
+ dependency.</li>
1472
+ <li><strong><code>scaffold update</code> only prints a command.</strong> That's intentional — it never
1473
+ runs installs for you. Copy the printed upgrade command for your channel and
1474
+ run it yourself.</li>
1475
+ <li><strong>Existing project behaves oddly after upgrade.</strong> Run <code>scaffold status</code> to
1476
+ trigger automatic state migration before anything else.</li>
1477
+ </ul>
1478
+ <h2 id="see-also">See also</h2>
1479
+ <ul>
1480
+ <li><a href="../pipeline/index.md">Pipeline reference</a>{mode=advisory} — what the steps are
1481
+ and the order they run in.</li>
1482
+ <li><a href="../cli/index.md">CLI reference</a>{mode=advisory} — every <code>scaffold</code> subcommand.</li>
1483
+ <li><a href="../multi-agent/index.md">Multi-agent workflow</a>{mode=advisory} — running the
1484
+ pipeline across parallel worktrees.</li>
1485
+ </ul></main>
1486
+ <div class="rail-backdrop" data-action="nav" aria-hidden="true"></div>
1487
+ </div>
1488
+ <script>(function(){
1489
+ var LS_KEY = 'guide-theme';
1490
+ function applyTheme(t) {
1491
+ document.documentElement.setAttribute('data-theme', t);
1492
+ }
1493
+
1494
+ document.addEventListener('DOMContentLoaded', function() {
1495
+ // ─── Theme toggle ────────────────────────────────────────────────────────
1496
+ document.querySelectorAll('[data-action="theme"]').forEach(function(btn) {
1497
+ btn.addEventListener('click', function() {
1498
+ var current = document.documentElement.getAttribute('data-theme');
1499
+ var next = current === 'dark' ? 'light' : 'dark';
1500
+ applyTheme(next);
1501
+ try { localStorage.setItem(LS_KEY, next); } catch(e) {}
1502
+ });
1503
+ });
1504
+
1505
+ // ─── Mobile nav (drawer + backdrop; aria-expanded + Escape-to-close) ──────
1506
+ function setNav(open) {
1507
+ var rail = document.querySelector('.rail');
1508
+ if (rail) rail.classList.toggle('open', open);
1509
+ var toggle = document.querySelector('.nav-toggle');
1510
+ if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1511
+ // Modal-drawer focus containment: while open, make the page content inert
1512
+ // (out of tab order + a11y tree) and move focus into the drawer; on close,
1513
+ // restore content and return focus to the toggle.
1514
+ var main = document.querySelector('.content');
1515
+ if (main) main.inert = open;
1516
+ if (open) {
1517
+ var first = rail && rail.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
1518
+ if (first) first.focus();
1519
+ else if (rail) { rail.setAttribute('tabindex', '-1'); rail.focus(); }
1520
+ } else if (toggle) {
1521
+ toggle.focus();
1522
+ }
1523
+ }
1524
+ // If the viewport grows past the mobile breakpoint while the drawer is open,
1525
+ // the rail becomes the desktop sidebar and the toggle hides — clear the open
1526
+ // state so .content doesn't stay inert with no way to close it.
1527
+ if (window.matchMedia) {
1528
+ var mq = window.matchMedia('(max-width: 860px)');
1529
+ var onMq = function() {
1530
+ if (mq.matches) return;
1531
+ var rail = document.querySelector('.rail');
1532
+ if (rail) rail.classList.remove('open');
1533
+ var toggle = document.querySelector('.nav-toggle');
1534
+ if (toggle) toggle.setAttribute('aria-expanded', 'false');
1535
+ var main = document.querySelector('.content');
1536
+ if (main) main.inert = false;
1537
+ };
1538
+ if (mq.addEventListener) mq.addEventListener('change', onMq);
1539
+ else if (mq.addListener) mq.addListener(onMq);
1540
+ }
1541
+ document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
1542
+ btn.addEventListener('click', function() {
1543
+ var rail = document.querySelector('.rail');
1544
+ setNav(!(rail && rail.classList.contains('open')));
1545
+ });
1546
+ });
1547
+ // Selecting a TOC link closes the drawer (so the now-active content isn't
1548
+ // left inert behind the panel) before the anchor navigation scrolls.
1549
+ var drawerRail = document.querySelector('.rail');
1550
+ if (drawerRail) {
1551
+ drawerRail.querySelectorAll('a').forEach(function(a) {
1552
+ a.addEventListener('click', function() {
1553
+ if (drawerRail.classList.contains('open')) setNav(false);
1554
+ });
1555
+ });
1556
+ }
1557
+ document.addEventListener('keydown', function(e) {
1558
+ var rail = document.querySelector('.rail');
1559
+ if (!rail || !rail.classList.contains('open')) return;
1560
+ if (e.key === 'Escape') { setNav(false); return; } // setNav restores focus to the toggle
1561
+ // Trap Tab within the open drawer (modal pattern).
1562
+ if (e.key !== 'Tab') return;
1563
+ var f = rail.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
1564
+ if (!f.length) return;
1565
+ var first = f[0], last = f[f.length - 1];
1566
+ if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
1567
+ else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
1568
+ });
1569
+
1570
+ // ─── Copy buttons ─────────────────────────────────────────────────────────
1571
+ document.querySelectorAll('pre').forEach(function(pre) {
1572
+ if (!pre.parentNode) return;
1573
+ var wrapper = document.createElement('div');
1574
+ wrapper.className = 'code';
1575
+ pre.parentNode.insertBefore(wrapper, pre);
1576
+ wrapper.appendChild(pre);
1577
+ var btn = document.createElement('button');
1578
+ btn.className = 'copy-btn';
1579
+ btn.textContent = 'Copy';
1580
+ btn.addEventListener('click', function() {
1581
+ var text = pre.textContent || '';
1582
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1583
+ navigator.clipboard.writeText(text).then(function() {
1584
+ btn.textContent = 'Copied';
1585
+ setTimeout(function() { btn.textContent = 'Copy'; }, 1200);
1586
+ }, function() {
1587
+ btn.textContent = 'Copy';
1588
+ });
1589
+ }
1590
+ });
1591
+ wrapper.insertBefore(btn, pre);
1592
+ });
1593
+
1594
+ // ─── Tabs (ARIA pattern: aria-selected + roving tabindex + arrow keys) ────
1595
+ function activateTab(group, btn, focus) {
1596
+ var idx = btn.getAttribute('data-tab');
1597
+ group.querySelectorAll('.tab-btn').forEach(function(b) {
1598
+ var on = b === btn;
1599
+ b.classList.toggle('active', on);
1600
+ b.setAttribute('aria-selected', on ? 'true' : 'false');
1601
+ b.setAttribute('tabindex', on ? '0' : '-1');
1602
+ });
1603
+ group.querySelectorAll('.tabpane').forEach(function(pane) {
1604
+ pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
1605
+ });
1606
+ if (focus) btn.focus();
1607
+ }
1608
+ document.querySelectorAll('.tabs').forEach(function(group) {
1609
+ var btns = [].slice.call(group.querySelectorAll('.tab-btn'));
1610
+ btns.forEach(function(btn, i) {
1611
+ btn.addEventListener('click', function() { activateTab(group, btn, false); });
1612
+ btn.addEventListener('keydown', function(e) {
1613
+ var ni = -1;
1614
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') ni = (i + 1) % btns.length;
1615
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') ni = (i - 1 + btns.length) % btns.length;
1616
+ else if (e.key === 'Home') ni = 0;
1617
+ else if (e.key === 'End') ni = btns.length - 1;
1618
+ if (ni >= 0) { e.preventDefault(); activateTab(group, btns[ni], true); }
1619
+ });
1620
+ });
1621
+ });
1622
+
1623
+ // ─── Filter tables ────────────────────────────────────────────────────────
1624
+ document.querySelectorAll('.filter-input').forEach(function(input) {
1625
+ input.addEventListener('input', function() {
1626
+ var q = input.value.toLowerCase();
1627
+ var container = input.closest('.filter-table');
1628
+ if (!container) return;
1629
+ container.querySelectorAll('tbody tr').forEach(function(row) {
1630
+ var text = (row.textContent || '').toLowerCase();
1631
+ row.style.display = text.includes(q) ? '' : 'none';
1632
+ });
1633
+ });
1634
+ });
1635
+
1636
+ // ─── Scrollspy ────────────────────────────────────────────────────────────
1637
+ if (typeof IntersectionObserver === 'undefined') return;
1638
+ var headings = document.querySelectorAll('h2[id],h3[id]');
1639
+ if (!headings.length) return;
1640
+ var observer = new IntersectionObserver(function(entries) {
1641
+ entries.forEach(function(entry) {
1642
+ if (!entry.isIntersecting) return;
1643
+ var id = entry.target.getAttribute('id');
1644
+ document.querySelectorAll('.toc a').forEach(function(a) {
1645
+ a.classList.toggle('active', a.getAttribute('href') === '#' + id);
1646
+ });
1647
+ });
1648
+ }, { rootMargin: '0px 0px -70% 0px', threshold: 0 });
1649
+ headings.forEach(function(h) { observer.observe(h); });
1650
+ });
1651
+ })();</script>
1652
+ </body>
1653
+ </html>