@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,1970 @@
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>Concepts &amp; Glossary</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>Concepts &amp; Glossary</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="#how-to-read-this-guide">How to read this guide</a></li><li class="toc-2"><a href="#pipeline-concepts">Pipeline concepts</a></li><li class="toc-3"><a href="#phase">Phase</a></li><li class="toc-3"><a href="#planning-vs-build-regime">Planning vs. build regime</a></li><li class="toc-3"><a href="#dependencies-hard-gate">dependencies (hard gate)</a></li><li class="toc-3"><a href="#reads-soft-reference">reads (soft reference)</a></li><li class="toc-3"><a href="#conditional-if-needed">Conditional / if-needed</a></li><li class="toc-3"><a href="#stateless-step">Stateless step</a></li><li class="toc-3"><a href="#create-vs-update-mode">CREATE vs. UPDATE mode</a></li><li class="toc-3"><a href="#methodology-preset">Methodology preset</a></li><li class="toc-3"><a href="#depth-15">Depth (1–5)</a></li><li class="toc-3"><a href="#overlay">Overlay</a></li><li class="toc-2"><a href="#observability-concepts">Observability concepts</a></li><li class="toc-3"><a href="#ledger">Ledger</a></li><li class="toc-3"><a href="#event">Event</a></li><li class="toc-3"><a href="#adapter">Adapter</a></li><li class="toc-3"><a href="#lens-ai">Lens (A–I)</a></li><li class="toc-3"><a href="#finding">Finding</a></li><li class="toc-3"><a href="#audit-verdict">Audit verdict</a></li><li class="toc-3"><a href="#fix-threshold">fix_threshold</a></li><li class="toc-3"><a href="#--fix-flow">--fix flow</a></li><li class="toc-3"><a href="#stall-signal">Stall signal</a></li><li class="toc-3"><a href="#phase-boundary-audit">Phase-boundary audit</a></li><li class="toc-3"><a href="#doc-conformance-channel">doc-conformance channel</a></li><li class="toc-2"><a href="#knowledge-concepts">Knowledge concepts</a></li><li class="toc-3"><a href="#knowledge-entry">Knowledge entry</a></li><li class="toc-3"><a href="#volatility-tier">Volatility tier</a></li><li class="toc-3"><a href="#knowledge-gap-signal">Knowledge-gap signal</a></li><li class="toc-2"><a href="#review-concepts">Review concepts</a></li><li class="toc-3"><a href="#channel">Channel</a></li><li class="toc-3"><a href="#compensating-pass">Compensating pass</a></li><li class="toc-3"><a href="#reconcile">Reconcile</a></li><li class="toc-3"><a href="#finding-key">finding_key</a></li><li class="toc-3"><a href="#mmr-verdict">MMR verdict</a></li><li class="toc-2"><a href="#multi-agent-concepts">Multi-agent concepts</a></li><li class="toc-3"><a href="#worktree">Worktree</a></li><li class="toc-3"><a href="#worktree-identity">Worktree identity</a></li><li class="toc-3"><a href="#ledger-harvest">Ledger harvest</a></li><li class="toc-3"><a href="#teardown">Teardown</a></li><li class="toc-2"><a href="#see-also">See also</a></li></ul></nav>
1372
+ </aside>
1373
+ <main class="content"><h2 id="how-to-read-this-guide">How to read this guide</h2>
1374
+ <p>Scaffold's other guides each go deep on one system; this one is the <strong>map of the
1375
+ vocabulary</strong> they share. Every term below gets a short definition and a link to
1376
+ the guide that owns the full story. When two guides use the same word slightly
1377
+ differently (a "verdict" in MMR vs. in the audit engine; a "lens" that lives in
1378
+ the audit but reasons about the knowledge base), this guide is where the seam is
1379
+ named.</p>
1380
+ <p>Terms cluster into four families:</p>
1381
+ <ul>
1382
+ <li><strong>Pipeline</strong> — how an idea becomes a build-ready spec.</li>
1383
+ <li><strong>Observability</strong> — the durable record of what the build actually did.</li>
1384
+ <li><strong>Review</strong> — how independent models gate changes.</li>
1385
+ <li><strong>Multi-agent</strong> — how parallel worktrees coordinate without stepping on each other.</li>
1386
+ </ul>
1387
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
1388
+
1389
+
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+
1406
+
1407
+
1408
+
1409
+
1410
+
1411
+
1412
+
1413
+
1414
+
1415
+
1416
+
1417
+
1418
+
1419
+
1420
+
1421
+
1422
+
1423
+
1424
+
1425
+
1426
+
1427
+
1428
+
1429
+
1430
+
1431
+
1432
+
1433
+
1434
+
1435
+
1436
+
1437
+
1438
+
1439
+
1440
+
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+
1449
+
1450
+
1451
+
1452
+
1453
+
1454
+
1455
+
1456
+
1457
+
1458
+
1459
+
1460
+
1461
+
1462
+
1463
+
1464
+
1465
+
1466
+
1467
+
1468
+
1469
+
1470
+
1471
+
1472
+
1473
+
1474
+
1475
+
1476
+
1477
+
1478
+
1479
+
1480
+
1481
+
1482
+
1483
+
1484
+
1485
+
1486
+
1487
+
1488
+
1489
+
1490
+
1491
+
1492
+
1493
+
1494
+
1495
+
1496
+
1497
+
1498
+
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+
1508
+
1509
+
1510
+
1511
+
1512
+
1513
+
1514
+
1515
+
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+
1526
+
1527
+
1528
+
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1535
+
1536
+
1537
+
1538
+
1539
+
1540
+
1541
+
1542
+
1543
+
1544
+
1545
+
1546
+
1547
+
1548
+
1549
+
1550
+
1551
+
1552
+
1553
+
1554
+
1555
+
1556
+
1557
+
1558
+
1559
+
1560
+
1561
+
1562
+ <table><thead><tr><th>Term</th><th>Cluster</th><th>See also</th></tr></thead><tbody><tr><td>Phase</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Planning vs. build regime</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td><code>dependencies</code> (hard gate)</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td><code>reads</code> (soft reference)</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Conditional / <code>if-needed</code></td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Stateless step</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>CREATE vs. UPDATE mode</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Methodology preset</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Depth (1–5)</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Overlay</td><td>Pipeline</td><td><a href="../pipeline/index.md">pipeline</a></td></tr><tr><td>Ledger</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Event</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Adapter</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Lens (A–I)</td><td>Observability</td><td><a href="../observability/index.md">observability</a> · <a href="../knowledge-freshness/index.md">knowledge-freshness</a></td></tr><tr><td>Finding</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Audit verdict</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td><code>fix_threshold</code></td><td>Observability · Review</td><td><a href="../observability/index.md">observability</a> · <a href="../mmr/index.md">mmr</a></td></tr><tr><td><code>--fix</code> flow</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Stall signal</td><td>Observability</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Phase-boundary audit</td><td>Observability · Pipeline</td><td><a href="../observability/index.md">observability</a> · <a href="../pipeline/index.md">pipeline</a></td></tr><tr><td><code>doc-conformance</code> channel</td><td>Observability · Review</td><td><a href="../observability/index.md">observability</a> · <a href="../mmr/index.md">mmr</a></td></tr><tr><td>Knowledge entry</td><td>Knowledge</td><td><a href="../knowledge-freshness/index.md">knowledge-freshness</a> · <a href="../knowledge/index.md">knowledge</a></td></tr><tr><td>Volatility tier</td><td>Knowledge</td><td><a href="../knowledge-freshness/index.md">knowledge-freshness</a></td></tr><tr><td>Knowledge-gap signal</td><td>Knowledge</td><td><a href="../knowledge-freshness/index.md">knowledge-freshness</a></td></tr><tr><td>Channel</td><td>Review</td><td><a href="../mmr/index.md">mmr</a> · <a href="../review-workflow/index.md">review-workflow</a></td></tr><tr><td>Compensating pass</td><td>Review</td><td><a href="../mmr/index.md">mmr</a></td></tr><tr><td>Reconcile</td><td>Review</td><td><a href="../mmr/index.md">mmr</a></td></tr><tr><td><code>finding_key</code></td><td>Review</td><td><a href="../mmr/index.md">mmr</a></td></tr><tr><td>MMR verdict</td><td>Review</td><td><a href="../mmr/index.md">mmr</a></td></tr><tr><td>Worktree</td><td>Multi-agent</td><td><a href="../multi-agent/index.md">multi-agent</a> · <a href="../observability/index.md">observability</a></td></tr><tr><td>Worktree identity</td><td>Multi-agent</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Ledger harvest</td><td>Multi-agent</td><td><a href="../observability/index.md">observability</a></td></tr><tr><td>Teardown</td><td>Multi-agent</td><td><a href="../observability/index.md">observability</a></td></tr></tbody></table></div>
1563
+ <h2 id="pipeline-concepts">Pipeline concepts</h2>
1564
+ <p>These describe the meta-prompt pipeline that turns an idea into a frozen,
1565
+ build-ready spec. Full treatment: <a href="../pipeline/index.md">the pipeline guide</a>.</p>
1566
+ <h3 id="phase">Phase</h3>
1567
+ <p>One of the <strong>16 ordered stages</strong> the pipeline divides into, numbered 0 (vision)
1568
+ through 15 (build). The phase list, slugs, numbers, and display names are defined
1569
+ exactly once, in the <code>PHASES</code> constant
1570
+ <span class="fp" data-path="src/types/frontmatter.ts:6">src/types/frontmatter.ts:6</span>; every doc, skill, and command resolves
1571
+ against it. Steps within a phase run in <code>order</code> sequence (phase N occupies the
1572
+ N00–N99 band). See <a href="../pipeline/index.md#the-16-phases-at-a-glance">the pipeline guide</a>.</p>
1573
+ <h3 id="planning-vs-build-regime">Planning vs. build regime</h3>
1574
+ <p>The pipeline splits into two regimes. <strong>Planning</strong> (phases 0–14) is <em>stateful</em>
1575
+ and <em>sequential</em> — each step produces a durable artifact and is run roughly once,
1576
+ in dependency order, working toward a frozen spec. <strong>Build</strong> (phase 15) is
1577
+ <em>stateless</em> and <em>on-demand</em> — the execution loops you run repeatedly while
1578
+ actually writing code. See <a href="../pipeline/index.md#the-mental-model">the pipeline guide</a>.</p>
1579
+ <h3 id="dependencies-hard-gate"><code>dependencies</code> (hard gate)</h3>
1580
+ <p>A step's frontmatter <code>dependencies</code> <span class="fp" data-path="src/types/frontmatter.ts:114">src/types/frontmatter.ts:114</span> are
1581
+ <strong>hard gates</strong>: <code>scaffold run</code> refuses a step until each dependency is
1582
+ <code>completed</code>, <code>skipped</code>, or disabled by the resolved pipeline/overlay (a disabled
1583
+ dep counts as satisfied). Contrast <code>reads</code>. See
1584
+ <a href="../pipeline/index.md#why-a-step-is-blocked">Why a step is blocked</a>.</p>
1585
+ <h3 id="reads-soft-reference"><code>reads</code> (soft reference)</h3>
1586
+ <p>A step's <code>reads</code> <span class="fp" data-path="src/types/frontmatter.ts:122">src/types/frontmatter.ts:122</span> are <strong>soft references</strong> — a
1587
+ step uses an upstream artifact if it's present, but a missing read never blocks
1588
+ execution (the assembler silently skips it). The
1589
+ <a href="../pipeline/index.md#why-a-step-is-blocked">pipeline guide</a> explains why
1590
+ <code>reads ≠ dependencies</code> is a common trip-up.</p>
1591
+ <h3 id="conditional-if-needed">Conditional / <code>if-needed</code></h3>
1592
+ <p>A step marked <code>conditional: 'if-needed'</code> <span class="fp" data-path="src/types/frontmatter.ts:118">src/types/frontmatter.ts:118</span> is
1593
+ enabled but only <em>applies</em> to certain project shapes (e.g. <code>database-schema</code>
1594
+ runs only if your project has a database layer). Conditional steps that don't
1595
+ apply count as "satisfied" for dependency purposes. See
1596
+ <a href="../pipeline/index.md#conditional-if-needed-steps">Conditional steps</a>.</p>
1597
+ <h3 id="stateless-step">Stateless step</h3>
1598
+ <p>A phase-15 build step with <code>stateless: true</code> <span class="fp" data-path="src/types/frontmatter.ts:126">src/types/frontmatter.ts:126</span>
1599
+ — it carries no completion state and can be run over and over (the agent loops,
1600
+ resume commands, <code>quick-task</code>, <code>new-enhancement</code>). The pipeline never tracks or
1601
+ gates these. See <a href="../pipeline/index.md#the-mental-model">the pipeline guide</a>.</p>
1602
+ <h3 id="create-vs-update-mode">CREATE vs. UPDATE mode</h3>
1603
+ <p>Every document-creating prompt detects whether its output file already exists. On
1604
+ first run it's <strong>CREATE mode</strong>; on a re-run it's <strong>UPDATE mode</strong>, which preserves
1605
+ human/team customizations and changes only what genuinely needs to change. This
1606
+ is what makes planning phases safe to iterate. See
1607
+ <a href="../pipeline/index.md#create-vs-update-mode">CREATE vs UPDATE mode</a>.</p>
1608
+ <h3 id="methodology-preset">Methodology preset</h3>
1609
+ <p><em>Which</em> steps are enabled. Three presets ship — <code>mvp</code>, <code>custom</code> (balanced), and
1610
+ <code>deep</code> (the schema default and most thorough). Presets are layered with
1611
+ <strong>overlays</strong>. See <a href="../pipeline/index.md#methodology-depth">Methodology &#x26; depth</a>.</p>
1612
+ <h3 id="depth-15">Depth (1–5)</h3>
1613
+ <p><em>How thorough</em> each enabled step's output is, on a 1–5 scale from Minimal to
1614
+ Exhaustive (depth 3 is the recommended default). Orthogonal to the preset:
1615
+ the preset picks the steps, depth dials each one's detail. At depth 4–5 some
1616
+ review/validation steps add external- or multi-model dispatch. See
1617
+ <a href="../pipeline/index.md#depth-15">Depth</a>.</p>
1618
+ <h3 id="overlay">Overlay</h3>
1619
+ <p>A preset layer (<code>content/methodology/*-overlay.yml</code>) applied on top of a
1620
+ preset. Overlays are either <strong>project-type</strong> (keyed by <code>project-type</code>, e.g.
1621
+ web-app, mobile, CLI, library) or <strong>structural</strong> (e.g. <code>multi-service</code>, which
1622
+ has no <code>project-type</code> and is activated by the presence of <code>services[]</code> in
1623
+ config). Most overlays only <strong>inject domain knowledge</strong> into existing steps; a
1624
+ few <strong>enable whole step families</strong> (game, multi-service). See
1625
+ <a href="../pipeline/index.md#project-type-playbooks">Project-type playbooks</a>.</p>
1626
+ <h2 id="observability-concepts">Observability concepts</h2>
1627
+ <p>These describe Build Observability — the durable record of what the build did,
1628
+ and the audit that checks it against the planning docs. Full treatment:
1629
+ <a href="../observability/index.md">the build observability guide</a>.</p>
1630
+ <h3 id="ledger">Ledger</h3>
1631
+ <p>The append-only <code>.scaffold/activity.jsonl</code> file where every durable observation
1632
+ lands as one JSON object per line. Writes are lock-guarded so parallel worktrees
1633
+ never corrupt it, each event is capped at 4 KiB, and secrets and home paths are
1634
+ redacted on the way in and out. See <a href="../observability/index.md#the-ledger">The ledger</a>.</p>
1635
+ <h3 id="event">Event</h3>
1636
+ <p>One typed entry in the ledger, written via <code>scaffold observe event &#x3C;type> …</code>.
1637
+ There are <strong>nine event types</strong> <span class="fp" data-path="src/observability/engine/types.ts:9">src/observability/engine/types.ts:9</span>
1638
+ (<code>task_claimed</code>, <code>task_completed</code>, <code>decision_recorded</code>, <code>blocker_hit</code>,
1639
+ <code>blocker_resolved</code>, <code>pr_opened</code>, <code>progress_heartbeat</code>, <code>finding_acknowledged</code>,
1640
+ <code>knowledge_gap_signal</code>), each with its own payload allow-list. See
1641
+ <a href="../observability/index.md#the-nine-event-types">The nine event types</a>.</p>
1642
+ <h3 id="adapter">Adapter</h3>
1643
+ <p>A component that <em>synthesizes</em> events from the surrounding tools (git, GitHub,
1644
+ MMR jobs, pipeline state, test runs) so the timeline reflects more than what
1645
+ agents chose to record. Eight adapters exist
1646
+ <span class="fp" data-path="src/observability/engine/types.ts:69">src/observability/engine/types.ts:69</span>; five emit replay events, three are
1647
+ availability probes. See <a href="../observability/index.md#adapters">Adapters</a>.</p>
1648
+ <h3 id="lens-ai">Lens (A–I)</h3>
1649
+ <p>An independent audit check function inside <code>scaffold observe audit</code>. The suite
1650
+ runs <strong>nine lenses, A through I</strong> — TDD coverage, AC coverage, coding-standards
1651
+ drift, tech-stack drift, design-system drift, scope, decisions, cross-doc
1652
+ consistency, and knowledge gaps. Lenses A–G run under <code>--scope code</code>, H and I
1653
+ under <code>--scope docs</code>. See
1654
+ <a href="../observability/index.md#the-nine-lens-audit">The nine-lens audit</a>. <strong>Lens I</strong>
1655
+ (<code>I-knowledge-gaps</code>) lives in the audit but reasons about the knowledge base —
1656
+ its full behavior is in <a href="../knowledge-freshness/index.md#lens-i-gap-detection-suppression">the knowledge-freshness guide</a>.</p>
1657
+ <h3 id="finding">Finding</h3>
1658
+ <p>A single issue a lens reports, carrying a severity (<code>P0</code>–<code>P3</code>), a title, a
1659
+ source doc, and an optional fix hint. Findings can be <code>open</code>, <code>acknowledged</code>
1660
+ (silenced via <code>scaffold observe ack</code>), or <code>skipped</code> (a lens whose required
1661
+ adapter was missing). See
1662
+ <a href="../observability/index.md#the-nine-lens-audit">The nine-lens audit</a>.</p>
1663
+ <h3 id="audit-verdict">Audit verdict</h3>
1664
+ <p>The overall result of an audit run. The engine computes exactly <strong>three</strong>
1665
+ verdicts <span class="fp" data-path="src/observability/engine/types.ts:6">src/observability/engine/types.ts:6</span>: <code>pass</code> (no blocking
1666
+ findings, no skipped lenses), <code>degraded-pass</code> (no blocking findings but ≥1 lens
1667
+ skipped), and <code>blocked</code> (≥1 open finding at or above <code>fix_threshold</code>). Note
1668
+ this is <strong>not</strong> the same set as MMR's four verdicts — see <a href="#mmr-verdict">MMR verdict</a>.
1669
+ See <a href="../observability/index.md#verdict-taxonomy">Verdict taxonomy</a>.</p>
1670
+ <h3 id="fix-threshold"><code>fix_threshold</code></h3>
1671
+ <p>The severity cutoff that decides which findings count as <em>blocking</em>. A finding
1672
+ blocks when its status is <code>open</code> and its severity is at or above the threshold
1673
+ (default <strong>P2</strong>). The threshold never hides findings — it only decides which
1674
+ ones drive a <code>blocked</code> verdict. The same name and default govern the MMR gate.
1675
+ See <a href="../observability/index.md#verdict-taxonomy"><code>fix_threshold</code></a> and the
1676
+ <a href="../mmr/index.md#the-gate-the-four-verdicts">MMR gate</a>.</p>
1677
+ <h3 id="--fix-flow"><code>--fix</code> flow</h3>
1678
+ <p><code>scaffold observe audit --fix</code> doesn't just report blocking findings — it
1679
+ dispatches an agent to fix each one, verifies the fix with a single-lens
1680
+ re-audit, and writes a post-fix report, all under abort-safe stashing. See
1681
+ <a href="../observability/index.md#the---fix-flow">The --fix flow</a>.</p>
1682
+ <h3 id="stall-signal">Stall signal</h3>
1683
+ <p>A staleness alert raised on the "Needs Attention" surface when
1684
+ <code>scaffold observe progress</code> runs — e.g. a claimed task with no recent activity,
1685
+ a PR that hasn't merged, an unaddressed blocker. Six signals are defined; five
1686
+ fire today. Thresholds are configurable under <code>stall:</code> in
1687
+ <code>.scaffold/observability.yaml</code>. See
1688
+ <a href="../observability/index.md#stall-detection-the-six-signals">Stall detection</a>.</p>
1689
+ <h3 id="phase-boundary-audit">Phase-boundary audit</h3>
1690
+ <p>A non-gating cross-document audit that fires automatically when a planning
1691
+ document at a phase boundary is marked complete. It runs only the <code>H-cross-doc</code>
1692
+ lens at <code>scope=docs</code>, prints a one-line summary, and never blocks the state
1693
+ transition. The six boundary steps are <code>user-stories</code>, <code>tech-stack</code>,
1694
+ <code>coding-standards</code>, <code>design-system</code>, <code>implementation-plan</code>, and
1695
+ <code>implementation-playbook</code>. See
1696
+ <a href="../observability/index.md#phase-boundary-triggers">Phase-boundary triggers</a> and
1697
+ the <a href="../pipeline/index.md#phase-boundary-audits">pipeline view</a>.</p>
1698
+ <h3 id="doc-conformance-channel"><code>doc-conformance</code> channel</h3>
1699
+ <p>The seam where the audit plugs into multi-model review: a built-in MMR channel
1700
+ that runs <code>scaffold observe audit --output-mode=mmr-findings</code> and emits findings
1701
+ in MMR's <code>Finding</code> shape. Disabled by default; enable with
1702
+ <code>--channels=doc-conformance</code>. See
1703
+ <a href="../observability/index.md#mmr-doc-conformance-channel">MMR doc-conformance channel</a>
1704
+ and the <a href="../mmr/index.md#channel-architecture">MMR channel architecture</a>.</p>
1705
+ <h2 id="knowledge-concepts">Knowledge concepts</h2>
1706
+ <p>These describe the knowledge base and how it stays current. Full treatment:
1707
+ <a href="../knowledge-freshness/index.md">the knowledge-freshness guide</a> and
1708
+ <a href="../knowledge/index.md">the knowledge guide</a>.</p>
1709
+ <h3 id="knowledge-entry">Knowledge entry</h3>
1710
+ <p>A domain-expertise document under <code>content/knowledge/&#x3C;category>/&#x3C;slug>.md</code>,
1711
+ injected into prompts during assembly. Each declares a <code>name</code>, <code>volatility</code>
1712
+ tier, and a list of <code>sources</code>. See
1713
+ <a href="../knowledge/index.md">the knowledge guide</a> and
1714
+ <a href="../knowledge-freshness/index.md#adding-a-new-entry-to-the-kb">Adding a new entry</a>.</p>
1715
+ <h3 id="volatility-tier">Volatility tier</h3>
1716
+ <p>How often an entry is expected to drift, on a three-tier scale — <code>fast-moving</code>,
1717
+ <code>evolving</code> (default), <code>stable</code> — which sets the daily cron's re-audit cadence
1718
+ (14 / 60 / 180 days). See
1719
+ <a href="../knowledge-freshness/index.md#cadence-model">the cadence model</a>.</p>
1720
+ <h3 id="knowledge-gap-signal">Knowledge-gap signal</h3>
1721
+ <p>A <code>knowledge_gap_signal</code> ledger event emitted when an agent hits a topic the KB
1722
+ doesn't cover. <strong>Lens I</strong> aggregates these over a rolling 90-day window into
1723
+ P1/P2 findings, suppressing any topic an entry already covers. This is where the
1724
+ observability and knowledge-freshness systems meet. See
1725
+ <a href="../knowledge-freshness/index.md#how-a-gap-closes">How a gap closes</a>.</p>
1726
+ <h2 id="review-concepts">Review concepts</h2>
1727
+ <p>These describe Multi-Model Review (MMR). Full treatment:
1728
+ <a href="../mmr/index.md">the MMR guide</a> and
1729
+ <a href="../review-workflow/index.md">the review-workflow guide</a>.</p>
1730
+ <h3 id="channel">Channel</h3>
1731
+ <p>One independent AI reviewer in an MMR run — a separate subprocess given the same
1732
+ prompt and run in isolation. The built-in channels are <code>codex</code>, <code>gemini</code>,
1733
+ <code>claude</code>, <code>grok</code>, and the opt-in <code>doc-conformance</code>; the <code>scaffold run</code> wrappers
1734
+ add a Superpowers code-reviewer <em>agent</em> channel. A channel is pure config data,
1735
+ not per-channel code. See <a href="../mmr/index.md#channel-architecture">Channel architecture</a>.</p>
1736
+ <h3 id="compensating-pass">Compensating pass</h3>
1737
+ <p>When a channel is degraded (not installed, auth-failed, timed out), MMR runs a
1738
+ <code>claude -p</code> pass focused on that channel's strength area, labeled e.g.
1739
+ <code>[compensating: Grok-equivalent]</code>. These findings are single-source and
1740
+ low-confidence. See <a href="../mmr/index.md#degraded-mode-compensation-auth">Degraded mode</a>.</p>
1741
+ <h3 id="reconcile">Reconcile</h3>
1742
+ <p>The step that groups every channel's findings by a stable key, de-duplicates
1743
+ them, and scores each group for agreement and confidence — producing the single
1744
+ list and verdict. Agreement <em>between</em> channels raises confidence; disagreement
1745
+ surfaces ambiguity. <code>mmr reconcile</code> also folds an external agent channel's
1746
+ findings into an existing job. See
1747
+ <a href="../mmr/index.md#findings-reconciliation-verdicts">Findings, reconciliation &#x26; verdicts</a>.</p>
1748
+ <h3 id="finding-key"><code>finding_key</code></h3>
1749
+ <p>The stable identity MMR computes for a finding so the same issue can be tracked
1750
+ across rounds and acknowledgments. Line numbers are stripped from the location
1751
+ and severity is excluded, so the same issue at P1 vs. P2 collapses to one key;
1752
+ a character-5-gram shingle backs a fuzzy match for re-worded findings. See
1753
+ <a href="../mmr/index.md#stable-identity-finding-key">Stable identity</a>.</p>
1754
+ <h3 id="mmr-verdict">MMR verdict</h3>
1755
+ <p>The gate result of a review. MMR computes <strong>four</strong> verdicts: <code>pass</code>,
1756
+ <code>degraded-pass</code>, <code>blocked</code>, and <code>needs-user-decision</code> (no channel completed).
1757
+ Proceed only on <code>pass</code> or <code>degraded-pass</code>.</p>
1758
+ <div class="callout callout-warning"><p><strong>Two verdict vocabularies — don't conflate them.</strong> The MMR review gate has
1759
+ <em>four</em> verdicts (the fourth, <code>needs-user-decision</code>, fires when no channel
1760
+ completes). The Build Observability audit engine emits only <em>three</em> —
1761
+ <code>needs-user-decision</code> is <strong>not</strong> an audit-engine verdict. See
1762
+ <a href="../mmr/index.md#the-gate-the-four-verdicts">the MMR gate</a> and
1763
+ <a href="../observability/index.md#verdict-taxonomy">the audit verdict taxonomy</a>.</p></div>
1764
+ <h2 id="multi-agent-concepts">Multi-agent concepts</h2>
1765
+ <p>These describe how parallel agents share a repo without colliding. Full
1766
+ treatment: <a href="../multi-agent/index.md">the multi-agent guide</a>, with the durable
1767
+ record covered in <a href="../observability/index.md">the observability guide</a>.</p>
1768
+ <h3 id="worktree">Worktree</h3>
1769
+ <p>An isolated git working tree used for parallel agent execution, so multiple
1770
+ agents (and humans) can build different parts of a project at once without
1771
+ sharing a checkout. Each worktree has its own ledger. See
1772
+ <a href="../multi-agent/index.md">the multi-agent guide</a>.</p>
1773
+ <h3 id="worktree-identity">Worktree identity</h3>
1774
+ <p>The stable per-worktree identity recorded in <code>.scaffold/identity.json</code> on first
1775
+ write — a <code>worktree_id</code> (UUID), a <code>worktree_label</code>, and <code>created_at</code>. It's what
1776
+ lets the harvester tell one worktree's events from another's, and what stamps
1777
+ every event's <code>worktree_id</code>. See
1778
+ <a href="../observability/index.md#worktree-identity">Worktree identity</a>.</p>
1779
+ <h3 id="ledger-harvest">Ledger harvest</h3>
1780
+ <p>Copying a worktree's local ledger into the primary repo's archive <em>before</em> the
1781
+ worktree is removed, so the build's reasoning survives teardown.
1782
+ <code>harvest --recover</code> separately rotates already-harvested stale entries from
1783
+ <code>.scaffold/activity-archive/active/</code> into the monthly <code>YYYY-MM.jsonl</code> archives
1784
+ when their worktree is no longer live — it does <strong>not</strong> rescue an unharvested
1785
+ ledger; that ledger is lost once the worktree is removed. See
1786
+ <a href="../observability/index.md#harvest-recover-teardown">Harvest, recover &#x26; teardown</a>.</p>
1787
+ <h3 id="teardown">Teardown</h3>
1788
+ <p>Removing a finished worktree the safe way — <code>scripts/teardown-agent-worktree.sh</code>
1789
+ harvests the ledger first, then runs <code>git worktree remove</code>, then deletes the
1790
+ workspace branch. Harvesting before removal is what closes the
1791
+ decisions-die-at-teardown gap. See
1792
+ <a href="../observability/index.md#harvest-recover-teardown">Harvest, recover &#x26; teardown</a>.</p>
1793
+ <h2 id="see-also">See also</h2>
1794
+ <ul>
1795
+ <li><a href="../pipeline/index.md">The Scaffold Pipeline</a> — phases, dependencies, presets, depth.</li>
1796
+ <li><a href="../observability/index.md">Build Observability</a> — ledger, events, the nine lenses, verdicts, <code>--fix</code>.</li>
1797
+ <li><a href="../knowledge-freshness/index.md">Knowledge Freshness</a> — volatility tiers, gap signals, Lens I.</li>
1798
+ <li><a href="../mmr/index.md">MMR Reference</a> — channels, reconciliation, the four verdicts.</li>
1799
+ <li><a href="../multi-agent/index.md">Multi-agent</a> — worktrees and parallel execution.</li>
1800
+ <li><a href="../review-workflow/index.md">Review workflow</a> — running reviews end to end.</li>
1801
+ <li><a href="../cli/index.md">CLI reference</a> — the full command surface.</li>
1802
+ </ul></main>
1803
+ <div class="rail-backdrop" data-action="nav" aria-hidden="true"></div>
1804
+ </div>
1805
+ <script>(function(){
1806
+ var LS_KEY = 'guide-theme';
1807
+ function applyTheme(t) {
1808
+ document.documentElement.setAttribute('data-theme', t);
1809
+ }
1810
+
1811
+ document.addEventListener('DOMContentLoaded', function() {
1812
+ // ─── Theme toggle ────────────────────────────────────────────────────────
1813
+ document.querySelectorAll('[data-action="theme"]').forEach(function(btn) {
1814
+ btn.addEventListener('click', function() {
1815
+ var current = document.documentElement.getAttribute('data-theme');
1816
+ var next = current === 'dark' ? 'light' : 'dark';
1817
+ applyTheme(next);
1818
+ try { localStorage.setItem(LS_KEY, next); } catch(e) {}
1819
+ });
1820
+ });
1821
+
1822
+ // ─── Mobile nav (drawer + backdrop; aria-expanded + Escape-to-close) ──────
1823
+ function setNav(open) {
1824
+ var rail = document.querySelector('.rail');
1825
+ if (rail) rail.classList.toggle('open', open);
1826
+ var toggle = document.querySelector('.nav-toggle');
1827
+ if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1828
+ // Modal-drawer focus containment: while open, make the page content inert
1829
+ // (out of tab order + a11y tree) and move focus into the drawer; on close,
1830
+ // restore content and return focus to the toggle.
1831
+ var main = document.querySelector('.content');
1832
+ if (main) main.inert = open;
1833
+ if (open) {
1834
+ var first = rail && rail.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
1835
+ if (first) first.focus();
1836
+ else if (rail) { rail.setAttribute('tabindex', '-1'); rail.focus(); }
1837
+ } else if (toggle) {
1838
+ toggle.focus();
1839
+ }
1840
+ }
1841
+ // If the viewport grows past the mobile breakpoint while the drawer is open,
1842
+ // the rail becomes the desktop sidebar and the toggle hides — clear the open
1843
+ // state so .content doesn't stay inert with no way to close it.
1844
+ if (window.matchMedia) {
1845
+ var mq = window.matchMedia('(max-width: 860px)');
1846
+ var onMq = function() {
1847
+ if (mq.matches) return;
1848
+ var rail = document.querySelector('.rail');
1849
+ if (rail) rail.classList.remove('open');
1850
+ var toggle = document.querySelector('.nav-toggle');
1851
+ if (toggle) toggle.setAttribute('aria-expanded', 'false');
1852
+ var main = document.querySelector('.content');
1853
+ if (main) main.inert = false;
1854
+ };
1855
+ if (mq.addEventListener) mq.addEventListener('change', onMq);
1856
+ else if (mq.addListener) mq.addListener(onMq);
1857
+ }
1858
+ document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
1859
+ btn.addEventListener('click', function() {
1860
+ var rail = document.querySelector('.rail');
1861
+ setNav(!(rail && rail.classList.contains('open')));
1862
+ });
1863
+ });
1864
+ // Selecting a TOC link closes the drawer (so the now-active content isn't
1865
+ // left inert behind the panel) before the anchor navigation scrolls.
1866
+ var drawerRail = document.querySelector('.rail');
1867
+ if (drawerRail) {
1868
+ drawerRail.querySelectorAll('a').forEach(function(a) {
1869
+ a.addEventListener('click', function() {
1870
+ if (drawerRail.classList.contains('open')) setNav(false);
1871
+ });
1872
+ });
1873
+ }
1874
+ document.addEventListener('keydown', function(e) {
1875
+ var rail = document.querySelector('.rail');
1876
+ if (!rail || !rail.classList.contains('open')) return;
1877
+ if (e.key === 'Escape') { setNav(false); return; } // setNav restores focus to the toggle
1878
+ // Trap Tab within the open drawer (modal pattern).
1879
+ if (e.key !== 'Tab') return;
1880
+ var f = rail.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
1881
+ if (!f.length) return;
1882
+ var first = f[0], last = f[f.length - 1];
1883
+ if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
1884
+ else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
1885
+ });
1886
+
1887
+ // ─── Copy buttons ─────────────────────────────────────────────────────────
1888
+ document.querySelectorAll('pre').forEach(function(pre) {
1889
+ if (!pre.parentNode) return;
1890
+ var wrapper = document.createElement('div');
1891
+ wrapper.className = 'code';
1892
+ pre.parentNode.insertBefore(wrapper, pre);
1893
+ wrapper.appendChild(pre);
1894
+ var btn = document.createElement('button');
1895
+ btn.className = 'copy-btn';
1896
+ btn.textContent = 'Copy';
1897
+ btn.addEventListener('click', function() {
1898
+ var text = pre.textContent || '';
1899
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1900
+ navigator.clipboard.writeText(text).then(function() {
1901
+ btn.textContent = 'Copied';
1902
+ setTimeout(function() { btn.textContent = 'Copy'; }, 1200);
1903
+ }, function() {
1904
+ btn.textContent = 'Copy';
1905
+ });
1906
+ }
1907
+ });
1908
+ wrapper.insertBefore(btn, pre);
1909
+ });
1910
+
1911
+ // ─── Tabs (ARIA pattern: aria-selected + roving tabindex + arrow keys) ────
1912
+ function activateTab(group, btn, focus) {
1913
+ var idx = btn.getAttribute('data-tab');
1914
+ group.querySelectorAll('.tab-btn').forEach(function(b) {
1915
+ var on = b === btn;
1916
+ b.classList.toggle('active', on);
1917
+ b.setAttribute('aria-selected', on ? 'true' : 'false');
1918
+ b.setAttribute('tabindex', on ? '0' : '-1');
1919
+ });
1920
+ group.querySelectorAll('.tabpane').forEach(function(pane) {
1921
+ pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
1922
+ });
1923
+ if (focus) btn.focus();
1924
+ }
1925
+ document.querySelectorAll('.tabs').forEach(function(group) {
1926
+ var btns = [].slice.call(group.querySelectorAll('.tab-btn'));
1927
+ btns.forEach(function(btn, i) {
1928
+ btn.addEventListener('click', function() { activateTab(group, btn, false); });
1929
+ btn.addEventListener('keydown', function(e) {
1930
+ var ni = -1;
1931
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') ni = (i + 1) % btns.length;
1932
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') ni = (i - 1 + btns.length) % btns.length;
1933
+ else if (e.key === 'Home') ni = 0;
1934
+ else if (e.key === 'End') ni = btns.length - 1;
1935
+ if (ni >= 0) { e.preventDefault(); activateTab(group, btns[ni], true); }
1936
+ });
1937
+ });
1938
+ });
1939
+
1940
+ // ─── Filter tables ────────────────────────────────────────────────────────
1941
+ document.querySelectorAll('.filter-input').forEach(function(input) {
1942
+ input.addEventListener('input', function() {
1943
+ var q = input.value.toLowerCase();
1944
+ var container = input.closest('.filter-table');
1945
+ if (!container) return;
1946
+ container.querySelectorAll('tbody tr').forEach(function(row) {
1947
+ var text = (row.textContent || '').toLowerCase();
1948
+ row.style.display = text.includes(q) ? '' : 'none';
1949
+ });
1950
+ });
1951
+ });
1952
+
1953
+ // ─── Scrollspy ────────────────────────────────────────────────────────────
1954
+ if (typeof IntersectionObserver === 'undefined') return;
1955
+ var headings = document.querySelectorAll('h2[id],h3[id]');
1956
+ if (!headings.length) return;
1957
+ var observer = new IntersectionObserver(function(entries) {
1958
+ entries.forEach(function(entry) {
1959
+ if (!entry.isIntersecting) return;
1960
+ var id = entry.target.getAttribute('id');
1961
+ document.querySelectorAll('.toc a').forEach(function(a) {
1962
+ a.classList.toggle('active', a.getAttribute('href') === '#' + id);
1963
+ });
1964
+ });
1965
+ }, { rootMargin: '0px 0px -70% 0px', threshold: 0 });
1966
+ headings.forEach(function(h) { observer.observe(h); });
1967
+ });
1968
+ })();</script>
1969
+ </body>
1970
+ </html>