ruvnet-brain 3.9.134-dev → 4.0.2

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 (218) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/README.md +5 -5
  3. package/bin/install.mjs +382 -36
  4. package/console/CONTRACT.md +172 -0
  5. package/console/activity.js +753 -0
  6. package/console/app.js +4189 -0
  7. package/console/architecture.html +1221 -0
  8. package/console/assets/depth-1.webp +0 -0
  9. package/console/assets/depth-2.webp +0 -0
  10. package/console/assets/depth-3.webp +0 -0
  11. package/console/assets/harness-vs-plain.svg +259 -0
  12. package/console/assets/hero.webp +0 -0
  13. package/console/assets/memory.webp +0 -0
  14. package/console/assets/metaharness.svg +247 -0
  15. package/console/index.html +777 -0
  16. package/console/install-architecture.html +162 -0
  17. package/console/install-mockup.html +543 -0
  18. package/console/style.css +2144 -0
  19. package/console/tips.css +926 -0
  20. package/console/tips.html +858 -0
  21. package/console/tips.js +128 -0
  22. package/docs/RELEASE-NOTES-4.0.md +88 -0
  23. package/kb/model-requirements.mjs +37 -6
  24. package/kb/zip-extract.mjs +53 -14
  25. package/keys/ruvnet-brain-signing.pub.pem +3 -0
  26. package/package.json +14 -22
  27. package/plugin/.claude-plugin/marketplace.json +14 -0
  28. package/plugin/.claude-plugin/plugin.json +22 -0
  29. package/plugin/.codex-plugin/plugin.json +21 -0
  30. package/plugin/.mcp.json +8 -0
  31. package/plugin/commands/brain-console.md +16 -0
  32. package/plugin/commands/configure.md +33 -0
  33. package/plugin/commands/rvbc.md +79 -0
  34. package/plugin/commands/rvcb.md +16 -0
  35. package/plugin/commands/whats-new.md +57 -0
  36. package/plugin/hooks/codex-hooks.json +160 -0
  37. package/plugin/hooks/hook-contracts.json +77 -0
  38. package/plugin/hooks/hooks.json +202 -0
  39. package/plugin/mcp/managed-cli-interface.mjs +47 -4
  40. package/plugin/mcp/server.mjs +56 -6
  41. package/plugin/scripts/anticipate.sh +534 -0
  42. package/plugin/scripts/codex-hook-adapter.mjs +96 -0
  43. package/plugin/scripts/continuation-gate.mjs +267 -0
  44. package/plugin/scripts/design-wall.sh +137 -0
  45. package/plugin/scripts/detach.mjs +182 -0
  46. package/plugin/scripts/first-session-worker.mjs +38 -0
  47. package/plugin/scripts/gate-receipt.sh +35 -0
  48. package/plugin/scripts/ground-before-write.sh +199 -0
  49. package/plugin/scripts/ground-ruvnet.sh +517 -0
  50. package/plugin/scripts/grounding-stamp.sh +113 -0
  51. package/plugin/scripts/grounding-substance.mjs +595 -0
  52. package/plugin/scripts/hijack-ruvnet.sh +81 -0
  53. package/plugin/scripts/hook-input.mjs +558 -0
  54. package/plugin/scripts/hook-shim-bash.mjs +55 -0
  55. package/plugin/scripts/hook-shim.mjs +303 -0
  56. package/plugin/scripts/host-update.mjs +58 -0
  57. package/plugin/scripts/kling-preflight.sh +146 -0
  58. package/plugin/scripts/learn-capture.sh +173 -0
  59. package/plugin/scripts/learn-flush.mjs +155 -0
  60. package/plugin/scripts/lesson-hooks.sh +213 -0
  61. package/plugin/scripts/md-stamp.mjs +219 -0
  62. package/plugin/scripts/protect-brain-state.sh +84 -0
  63. package/plugin/scripts/route-dispatch.sh +147 -0
  64. package/plugin/scripts/routing-outcome-capture.mjs +89 -0
  65. package/plugin/scripts/runtime-preferences.mjs +269 -0
  66. package/plugin/scripts/session-start-core.mjs +477 -0
  67. package/plugin/scripts/session-start.sh +13 -0
  68. package/plugin/scripts/signal-watch.mjs +193 -0
  69. package/plugin/scripts/unprompted-runtime.mjs +377 -0
  70. package/plugin/scripts/update-apply.mjs +419 -0
  71. package/plugin/scripts/verify-interface.sh +53 -0
  72. package/plugin/scripts/version-bump-gate.sh +112 -0
  73. package/plugin/skills/brain-build/SKILL.md +123 -0
  74. package/plugin/skills/brain-console/SKILL.md +22 -0
  75. package/plugin/skills/brain-prompt/SKILL.md +83 -0
  76. package/plugin/skills/brain-score/SKILL.md +101 -0
  77. package/plugin/skills/release-proof/SKILL.md +81 -0
  78. package/plugin/skills/release-proof/agents/openai.yaml +4 -0
  79. package/plugin/skills/release-proof/references/receipt-contract.md +38 -0
  80. package/plugin/skills/release-proof/scripts/release-proof.mjs +210 -0
  81. package/plugin/skills/ruvnet-brain/PLAYBOOK.md +121 -0
  82. package/plugin/skills/ruvnet-brain/SKILL.md +234 -0
  83. package/plugin/skills/rvbc/SKILL.md +23 -0
  84. package/plugin/skills/savings/SKILL.md +46 -0
  85. package/plugin/skills/whats-new/SKILL.md +22 -0
  86. package/scripts/adr-backfill.mjs +107 -0
  87. package/scripts/advocacy-outcomes.mjs +808 -0
  88. package/scripts/agentdb-context.mjs +216 -0
  89. package/scripts/agentdb-fleet-doctor.mjs +101 -0
  90. package/scripts/ascii-drift.mjs +236 -0
  91. package/scripts/behavioral-l1-l4.mjs +210 -0
  92. package/scripts/brain-capability-check.mjs +72 -0
  93. package/scripts/brain-grade-groundtruth.mjs +100 -0
  94. package/scripts/brain-latency-50.mjs +227 -0
  95. package/scripts/brain-novice-50.mjs +189 -0
  96. package/scripts/brain-stamp.mjs +94 -0
  97. package/scripts/brain-state.mjs +212 -0
  98. package/scripts/build-bundle.mjs +522 -0
  99. package/scripts/build-concepts.mjs +132 -0
  100. package/scripts/build-l2.mjs +71 -0
  101. package/scripts/build-primer.mjs +73 -0
  102. package/scripts/build-symbols.mjs +68 -0
  103. package/scripts/calibrate-router.mjs +97 -0
  104. package/scripts/capability-audit.mjs +321 -0
  105. package/scripts/capability-registry.mjs +876 -0
  106. package/scripts/check-indexation.mjs +108 -0
  107. package/scripts/check-legibility.mjs +189 -0
  108. package/scripts/ci/build-fixture-kb.mjs +67 -0
  109. package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
  110. package/scripts/ci/learning-replay-recorder.mjs +59 -0
  111. package/scripts/ci/mutate-hook-timeout.mjs +70 -0
  112. package/scripts/ci/stranger-fixture-stage.mjs +17 -0
  113. package/scripts/ci/stranger-scenario.mjs +228 -0
  114. package/scripts/ci/stranger-timeout.mjs +25 -0
  115. package/scripts/ci-verdict.mjs +29 -0
  116. package/scripts/claims-verify.mjs +710 -0
  117. package/scripts/clear-claude-tmp.sh +31 -0
  118. package/scripts/console-engine.mjs +434 -0
  119. package/scripts/console-engine.test.mjs +125 -0
  120. package/scripts/corpus-qa.mjs +250 -0
  121. package/scripts/correction-detect-embed.mjs +346 -0
  122. package/scripts/correction-detect-measure.mjs +270 -0
  123. package/scripts/correction-detect.mjs +686 -0
  124. package/scripts/count-chunks.mjs +54 -0
  125. package/scripts/described-questions.json +30 -0
  126. package/scripts/design-grade.mjs +58 -0
  127. package/scripts/dev-plugin-link.sh +105 -0
  128. package/scripts/distill-project.mjs +200 -0
  129. package/scripts/doc-currency.mjs +801 -0
  130. package/scripts/eval-brain.mjs +244 -0
  131. package/scripts/fix-metaharness-memretrieve.mjs +121 -0
  132. package/scripts/full-hints.mjs +87 -0
  133. package/scripts/gate.sh +39 -0
  134. package/scripts/gates.mjs +146 -0
  135. package/scripts/gen-console-images.mjs +54 -0
  136. package/scripts/gen-images.mjs +47 -0
  137. package/scripts/git-clone-refresh.mjs +52 -0
  138. package/scripts/git-hooks/pre-push +126 -0
  139. package/scripts/goal-match.mjs +398 -0
  140. package/scripts/goldie-research.mjs +223 -0
  141. package/scripts/goldie-weekly.sh +67 -0
  142. package/scripts/health-repair.mjs +250 -0
  143. package/scripts/helix-scenario-questions.json +10 -0
  144. package/scripts/ingest-gists.mjs +230 -0
  145. package/scripts/ingest-meeting.mjs +115 -0
  146. package/scripts/ingest-repo.mjs +79 -0
  147. package/scripts/install-npx-witness.sh +49 -0
  148. package/scripts/issue-fix.mjs +639 -0
  149. package/scripts/issue-watch.mjs +276 -0
  150. package/scripts/issue4-close-note.md +31 -0
  151. package/scripts/key-canary.mjs +91 -0
  152. package/scripts/latency-to-surface.mjs +233 -0
  153. package/scripts/learning-enable.mjs +380 -0
  154. package/scripts/learning-replay.mjs +1570 -0
  155. package/scripts/learnings.mjs +62 -0
  156. package/scripts/lesson-gate.mjs +680 -0
  157. package/scripts/lesson-lifecycle.mjs +449 -0
  158. package/scripts/lesson-promote.mjs +262 -0
  159. package/scripts/lesson-ratify.mjs +98 -0
  160. package/scripts/lesson-seed.mjs +252 -0
  161. package/scripts/lesson-store.mjs +447 -0
  162. package/scripts/loop-checkpoint.mjs +86 -0
  163. package/scripts/memdb-health.sh +14 -0
  164. package/scripts/memory-doctor.mjs +271 -0
  165. package/scripts/model-catalog.mjs +79 -0
  166. package/scripts/nightly-controller.mjs +66 -0
  167. package/scripts/nightly-gists.sh +72 -0
  168. package/scripts/nightly-wrapper.sh +180 -0
  169. package/scripts/notify.sh +12 -0
  170. package/scripts/npx-witness.sh +56 -0
  171. package/scripts/onboarding-console.mjs +2749 -0
  172. package/scripts/private-fence.mjs +69 -0
  173. package/scripts/proactivity-metrics.mjs +118 -0
  174. package/scripts/proof-questions.json +56 -0
  175. package/scripts/prove.mjs +95 -0
  176. package/scripts/proxy/claude-proxied.sh +57 -0
  177. package/scripts/proxy/proxy-revert.sh +59 -0
  178. package/scripts/proxy/proxy-up.sh +60 -0
  179. package/scripts/proxy/proxy-verify.mjs +142 -0
  180. package/scripts/published-surface-probe.mjs +241 -0
  181. package/scripts/qe/card-lane-gate.mjs +162 -0
  182. package/scripts/qe/session-start-gate.mjs +229 -0
  183. package/scripts/qe/ux-suite.mjs +323 -0
  184. package/scripts/reconcile-project.mjs +0 -0
  185. package/scripts/record-lesson.mjs +113 -0
  186. package/scripts/refresh-model-catalog.mjs +99 -0
  187. package/scripts/release-proof.mjs +9 -0
  188. package/scripts/release-vector.mjs +281 -0
  189. package/scripts/release.mjs +395 -0
  190. package/scripts/remedy-registry.mjs +247 -0
  191. package/scripts/rerank-cap-eval.mjs +265 -0
  192. package/scripts/rerank-cap-warm-ab.mjs +129 -0
  193. package/scripts/route-cheap.mjs +20 -15
  194. package/scripts/router-utilization.mjs +182 -0
  195. package/scripts/routing-flywheel.mjs +596 -0
  196. package/scripts/rvf-generation.mjs +104 -0
  197. package/scripts/rvf-index-audit.mjs +138 -0
  198. package/scripts/self-update.mjs +508 -0
  199. package/scripts/selfcheck.mjs +7 -1
  200. package/scripts/sign-bundle.mjs +69 -0
  201. package/scripts/signal-watch.mjs +171 -0
  202. package/scripts/stack-sync.mjs +469 -0
  203. package/scripts/stamp-existing-rvf-generations.mjs +53 -0
  204. package/scripts/stamp-sweep.mjs +144 -0
  205. package/scripts/status-honesty.mjs +102 -0
  206. package/scripts/sync-version.mjs +217 -0
  207. package/scripts/token-report.mjs +102 -0
  208. package/scripts/top100-benchmark.mjs +479 -0
  209. package/scripts/top100-corpus.mjs +112 -0
  210. package/scripts/top100-semantic-assertions.mjs +449 -0
  211. package/scripts/update-apply.mjs +9 -0
  212. package/scripts/upgrade-notice.mjs +14 -0
  213. package/scripts/verify-bundle.mjs +51 -0
  214. package/scripts/verify-channels.mjs +184 -0
  215. package/scripts/verify-model-catalog.mjs +104 -0
  216. package/scripts/verify-nightly-close-issue4.sh +31 -0
  217. package/scripts/version.mjs +40 -0
  218. package/scripts/wired-check.mjs +864 -0
@@ -0,0 +1,926 @@
1
+ /* ============================================================================
2
+ RUVNET BRAIN — HOW TO USE IT (page two of the console)
3
+ ----------------------------------------------------------------------------
4
+ Self-contained on purpose: the tokens below are COPIED from console/style.css
5
+ (Amber Substrate family) so this page never depends on a file another
6
+ session may be mid-edit in. Same semantics, worn the same way:
7
+ cyan = always on (observed — depth 1)
8
+ amber = when you ask (acting — depth 2)
9
+ green = when you name it (grounded, precise — depth 3)
10
+ Fully offline: system font stacks only. Light + dark, both first-class.
11
+ ============================================================================ */
12
+
13
+ /* ============================== 1 · TOKENS (copied) ============================== */
14
+
15
+ :root {
16
+ color-scheme: dark;
17
+
18
+ --bg: #0a0c10;
19
+ --bg-2: #0e1116;
20
+ --surface: #13171e;
21
+ --surface-2: #181d26;
22
+ --ridge: #2a3140;
23
+
24
+ --ink: #ece8dc;
25
+ --ink-2: #c3c2b6;
26
+ --muted: #8f94a1;
27
+ --faint: #5a5f6e;
28
+ --on-accent: #0a0c10;
29
+
30
+ --amber: #f0a830; --amber-text: #f0b04a;
31
+ --cyan: #5ad6ff; --cyan-text: #5ad6ff;
32
+ --green: #5fd38a; --green-text: #5fd38a;
33
+ --red: #ff6b5e; --red-text: #ff8a7e;
34
+
35
+ --spectrum: linear-gradient(96deg, #f0a830 0%, #ffce6a 24%, #5ad6ff 58%, #5fd38a 100%);
36
+
37
+ --shadow: 0 18px 42px -24px rgba(0, 0, 0, 0.65);
38
+ --shadow-deep: 0 30px 70px -34px rgba(0, 0, 0, 0.8);
39
+
40
+ --display: "Iowan Old Style", "Palatino Nova", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
41
+ --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
42
+ --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;
43
+
44
+ --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
45
+ --radius: 16px;
46
+ --radius-s: 10px;
47
+ --maxw: 1080px;
48
+ --gutter: clamp(1.1rem, 4vw, 2.6rem);
49
+
50
+ --fs--2: clamp(0.685rem, 0.66rem + 0.12vw, 0.76rem);
51
+ --fs--1: clamp(0.815rem, 0.79rem + 0.14vw, 0.885rem);
52
+ --fs-0: clamp(0.94rem, 0.91rem + 0.15vw, 1rem);
53
+ --fs-1: clamp(1.06rem, 1.0rem + 0.3vw, 1.19rem);
54
+ --fs-2: clamp(1.24rem, 1.12rem + 0.6vw, 1.5rem);
55
+ --fs-3: clamp(1.85rem, 1.4rem + 2.3vw, 2.95rem);
56
+ }
57
+
58
+ :root[data-theme="light"] {
59
+ color-scheme: light;
60
+
61
+ --bg: #f1efe8;
62
+ --bg-2: #e9e6dd;
63
+ --surface: #fbfaf6;
64
+ --surface-2: #f2efe8;
65
+ --ridge: #d9d5c9;
66
+
67
+ --ink: #20242e;
68
+ --ink-2: #3d4353;
69
+ --muted: #575d6e;
70
+ --faint: #878c99;
71
+ --on-accent: #241a06;
72
+
73
+ --amber: #e09614; --amber-text: #8f5c05;
74
+ --cyan: #2ba6cf; --cyan-text: #0a6d8f;
75
+ --green: #3aa768; --green-text: #1e7a45;
76
+ --red: #e05b4b; --red-text: #b03225;
77
+
78
+ --spectrum: linear-gradient(96deg, #c47d08 0%, #d9a12e 24%, #0e87ad 58%, #20824d 100%);
79
+
80
+ --shadow: 0 16px 36px -24px rgba(60, 50, 20, 0.35);
81
+ --shadow-deep: 0 26px 60px -30px rgba(60, 50, 20, 0.45);
82
+ }
83
+
84
+ /* derived — both themes */
85
+ :root {
86
+ --hair: color-mix(in srgb, var(--ridge) 78%, transparent);
87
+ --hair-strong: color-mix(in srgb, var(--ridge) 100%, transparent);
88
+ --line: color-mix(in srgb, var(--amber) 22%, transparent);
89
+ --fill: color-mix(in srgb, var(--amber) 9%, transparent);
90
+ --fill-cyan: color-mix(in srgb, var(--cyan) 9%, transparent);
91
+ --fill-green: color-mix(in srgb, var(--green) 9%, transparent);
92
+
93
+ --illo-stroke: color-mix(in srgb, var(--ink) 52%, transparent);
94
+ --illo-faint: color-mix(in srgb, var(--ink) 24%, transparent);
95
+ --illo-plate: color-mix(in srgb, var(--ink) 5%, transparent);
96
+ }
97
+
98
+ /* ============================== 2 · BASE (copied) ============================== */
99
+
100
+ *, *::before, *::after { box-sizing: border-box; }
101
+
102
+ html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
103
+
104
+ body {
105
+ margin: 0;
106
+ background: var(--bg);
107
+ color: var(--ink);
108
+ font-family: var(--sans);
109
+ font-size: var(--fs-0);
110
+ line-height: 1.62;
111
+ -webkit-font-smoothing: antialiased;
112
+ text-rendering: optimizeLegibility;
113
+ background-image:
114
+ radial-gradient(color-mix(in srgb, var(--ink) 4.5%, transparent) 1px, transparent 1.5px),
115
+ radial-gradient(1000px 560px at 82% -6%, color-mix(in srgb, var(--amber) 9%, transparent), transparent 62%),
116
+ radial-gradient(820px 640px at -8% 4%, color-mix(in srgb, var(--cyan) 7%, transparent), transparent 56%),
117
+ linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
118
+ background-size: 26px 26px, auto, auto, auto;
119
+ background-attachment: fixed;
120
+ }
121
+
122
+ img, svg, video { max-width: 100%; height: auto; }
123
+
124
+ a { color: var(--cyan-text); text-decoration: none; }
125
+ a:hover { color: var(--amber-text); }
126
+
127
+ code {
128
+ font-family: var(--mono); font-size: 0.86em;
129
+ color: var(--cyan-text); background: var(--fill-cyan);
130
+ border: 1px solid color-mix(in srgb, var(--cyan) 26%, transparent);
131
+ border-radius: 6px; padding: 0.08em 0.42em;
132
+ }
133
+
134
+ .mono { font-family: var(--mono); }
135
+
136
+ :focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; border-radius: 6px; }
137
+ ::selection { background: color-mix(in srgb, var(--amber) 30%, transparent); color: var(--ink); }
138
+
139
+ .skip-link {
140
+ position: absolute; left: 0; top: 0; z-index: 100;
141
+ transform: translateY(-130%);
142
+ background: var(--amber); color: var(--on-accent);
143
+ font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
144
+ padding: 10px 16px; border-radius: 0 0 var(--radius-s) 0;
145
+ transition: transform 0.18s var(--ease);
146
+ }
147
+ .skip-link:focus { transform: translateY(0); }
148
+
149
+ .wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
150
+
151
+ h1, h2, h3 {
152
+ font-family: var(--display);
153
+ font-weight: 600;
154
+ letter-spacing: -0.012em;
155
+ line-height: 1.08;
156
+ color: var(--ink);
157
+ margin: 0 0 0.4em;
158
+ text-wrap: balance;
159
+ }
160
+
161
+ p { margin: 0 0 0.9rem; color: var(--ink-2); max-width: 70ch; text-wrap: pretty; }
162
+ p:last-child { margin-bottom: 0; }
163
+
164
+ .eyebrow {
165
+ font-family: var(--mono); font-size: var(--fs--2);
166
+ letter-spacing: 0.26em; text-transform: uppercase;
167
+ color: var(--cyan-text); margin: 0 0 14px;
168
+ }
169
+
170
+ /* ============================== 3 · HEADER (copied) ============================== */
171
+
172
+ .head {
173
+ position: sticky; top: 0; z-index: 50;
174
+ background: color-mix(in srgb, var(--bg) 82%, transparent);
175
+ backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
176
+ }
177
+ .head-inner {
178
+ display: flex; align-items: center; justify-content: space-between;
179
+ height: 58px; gap: 16px;
180
+ }
181
+ .spectrum-edge { height: 2px; background: var(--spectrum); opacity: 0.9; }
182
+
183
+ .brand { display: inline-flex; align-items: center; gap: 11px; min-width: 0; }
184
+ .brand .mark { width: 28px; height: 28px; flex: none; }
185
+ .brand .mark .mk-plate { fill: #0b0d12; stroke: #2c3850; stroke-width: 1.5; }
186
+ .brand .mark .mk-brain { fill: none; stroke: #f0a830; stroke-width: 3.4; stroke-linejoin: round; }
187
+ .brand .mark .mk-branch { fill: none; stroke: #f0a830; stroke-width: 2.4; stroke-linecap: round; opacity: 0.9; }
188
+ .brand .mark .n1 { fill: #f4b13a; }
189
+ .brand .mark .n2 { fill: #5ad6ff; }
190
+ .brand .mark .n3 { fill: #5fd38a; }
191
+ .brand-text {
192
+ font-family: var(--display); font-weight: 600; font-size: 1.02rem;
193
+ letter-spacing: -0.01em; color: var(--ink); white-space: nowrap;
194
+ overflow: hidden; text-overflow: ellipsis;
195
+ }
196
+ .brand-sub {
197
+ font-family: var(--mono); font-size: 11px; font-weight: 500;
198
+ letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted);
199
+ margin-left: 8px;
200
+ }
201
+
202
+ .head-right { display: inline-flex; align-items: center; gap: 10px; }
203
+
204
+ .back-chip {
205
+ font-size: 12.5px; color: var(--ink-2);
206
+ border: 1px solid var(--hair); border-radius: 100px; padding: 4px 13px;
207
+ white-space: nowrap; letter-spacing: 0.06em;
208
+ transition: border-color 0.18s var(--ease), color 0.18s var(--ease), background 0.18s var(--ease);
209
+ }
210
+ .back-chip:hover { border-color: var(--amber); color: var(--amber-text); background: var(--fill); }
211
+
212
+ .theme-toggle {
213
+ width: 36px; height: 36px; border-radius: 10px;
214
+ border: 1px solid var(--hair-strong); background: transparent;
215
+ display: grid; place-items: center; cursor: pointer;
216
+ color: var(--ink-2);
217
+ transition: border-color 0.18s var(--ease), background 0.18s var(--ease), transform 0.15s var(--ease);
218
+ }
219
+ .theme-toggle:hover { border-color: var(--amber); color: var(--amber-text); background: var(--fill); }
220
+ .theme-toggle:active { transform: scale(0.94); }
221
+ .theme-toggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
222
+ :root[data-theme="dark"] .ic-moon { display: none; }
223
+ :root[data-theme="light"] .ic-sun { display: none; }
224
+
225
+ /* ============================== 4 · HERO ============================== */
226
+
227
+ .intro { padding: clamp(2.2rem, 5vw, 4rem) 0 clamp(1rem, 2.5vw, 1.6rem); animation: rise 0.55s var(--ease) both; }
228
+
229
+ .intro-grid { display: grid; grid-template-columns: 1fr; gap: clamp(1.6rem, 4vw, 3rem); align-items: center; }
230
+ @media (min-width: 62em) { .intro-grid { grid-template-columns: 1.04fr 0.96fr; } }
231
+
232
+ h1 { font-size: var(--fs-3); }
233
+ h1 em {
234
+ font-style: italic;
235
+ background: var(--spectrum);
236
+ -webkit-background-clip: text; background-clip: text;
237
+ color: transparent;
238
+ }
239
+
240
+ .lede { font-size: var(--fs-1); line-height: 1.55; color: var(--ink-2); max-width: 56ch; }
241
+ .lede b { color: var(--ink); font-weight: 650; }
242
+
243
+ /* rUv, in his own words — the page's warrant */
244
+ .ruv-quote {
245
+ margin: 1.4rem 0 0; padding: 16px 20px;
246
+ border-left: 3px solid var(--amber);
247
+ border-radius: 0 var(--radius-s) var(--radius-s) 0;
248
+ background: var(--fill);
249
+ }
250
+ .ruv-quote p {
251
+ font-family: var(--display); font-style: italic;
252
+ font-size: var(--fs-0); line-height: 1.55; color: var(--ink-2);
253
+ margin-bottom: 0.6rem;
254
+ }
255
+ .ruv-quote footer { font-size: 11.5px; letter-spacing: 0.05em; color: var(--muted); }
256
+
257
+ /* ── hero art — the three-depths diagram (REBUILT 2026-07-17, twice) ────────
258
+ Root cause of the old unreadable hero: a 640-unit viewBox rendered into a
259
+ ~453px grid column, so every 11.5-unit label collapsed to ~8px effective.
260
+ The viewBox is now 460×300 — user units ≈ CSS px at desktop widths (scale
261
+ 0.94–1.0) — and label sizes below are chosen so every line is ≥12px
262
+ EFFECTIVE at a 1024 viewport (431px render → ×0.937). Measured, not eyeballed.
263
+ Second lesson (same day): labeling each ring on its own top arc crowded all
264
+ the text into the rings' converging crowns and the paint-order halos punched
265
+ gaps in the strokes. So the layout is the original composition — rings left,
266
+ label column right, leader lines — which cannot collide by construction
267
+ (ring 3 peaks at x=226; the column starts at x=235), and needs no halos, so
268
+ the rings always render continuous.
269
+ Fully token-driven so the light theme works: label fills ride the *-text
270
+ ramps (light mode darkens them for contrast); the plate is --surface with
271
+ color-mix washes instead of a hardcoded dark fill.
272
+ All motion sits under prefers-reduced-motion: no-preference. At rest the
273
+ pulse is stroke-opacity 0 (invisible), the halo is a static soft glow, and
274
+ the caret is a steady block — a complete, fully-labeled diagram. */
275
+ .hero-art {
276
+ margin: 0 auto; position: relative; overflow: hidden;
277
+ width: 100%; max-width: 560px; /* keeps the plate sane when the grid collapses to one column */
278
+ border-radius: var(--radius); border: 1px solid var(--ridge);
279
+ box-shadow: var(--shadow-deep);
280
+ aspect-ratio: 460 / 300; /* matches the viewBox exactly — the SVG fills edge to edge */
281
+ background:
282
+ radial-gradient(120% 85% at 12% 6%, color-mix(in srgb, var(--amber) 9%, transparent), transparent 60%),
283
+ radial-gradient(110% 85% at 90% 18%, color-mix(in srgb, var(--cyan) 8%, transparent), transparent 58%),
284
+ radial-gradient(130% 90% at 45% 108%, color-mix(in srgb, var(--green) 7%, transparent), transparent 60%),
285
+ var(--surface);
286
+ }
287
+ .hero-art .hero-scene { position: absolute; inset: 0; width: 100%; height: 100%; }
288
+ .hero-cap {
289
+ position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
290
+ margin: 0; padding: 7px 14px;
291
+ font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
292
+ color: var(--muted);
293
+ background: color-mix(in srgb, var(--surface) 78%, transparent);
294
+ backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
295
+ border-top: 1px solid color-mix(in srgb, var(--cyan) 16%, transparent);
296
+ }
297
+
298
+ /* strata — the top edge carries each depth's stroke style: solid = always on;
299
+ dashed/dotted = only there when you reach for them. Band fills are a whisper of the
300
+ same hue so the strata read as material, not as chart bars. */
301
+ .hero-scene .hs-band { stroke: none; }
302
+ .hero-scene .hs-band.b1 { fill: color-mix(in srgb, var(--cyan) 8%, transparent); }
303
+ .hero-scene .hs-band.b2 { fill: color-mix(in srgb, var(--amber) 6%, transparent); }
304
+ .hero-scene .hs-band.b3 { fill: color-mix(in srgb, var(--green) 6%, transparent); }
305
+ .hero-scene .hs-edge { fill: none; stroke-linecap: round; }
306
+ .hero-scene .hs-edge.e1 { stroke: var(--cyan); stroke-width: 2.4; }
307
+ .hero-scene .hs-edge.e2 { stroke: var(--amber); stroke-width: 2; stroke-dasharray: 7 8; opacity: 0.85; }
308
+ .hero-scene .hs-edge.e3 { stroke: var(--green); stroke-width: 2; stroke-dasharray: 2 10; opacity: 0.8; }
309
+
310
+ /* the plumb line — the path a prompt takes down through the depths */
311
+ .hero-scene .hs-plumb { stroke: var(--ridge); stroke-width: 1.4; stroke-dasharray: 1 6;
312
+ stroke-linecap: round; }
313
+ .hero-scene .hs-pulse { fill: var(--cyan); opacity: 0; }
314
+ .hero-scene .hs-b1-halo { /* depth 1's edge doubles as the catch halo (animated below) */ }
315
+
316
+ /* labels — inside their own band's lane, clear of the plumb column (x=112) */
317
+ .hero-scene .hs-main, .hero-scene .hs-sub { font-family: var(--mono); text-anchor: start; }
318
+ .hero-scene .hs-main { font-size: 14.5px; letter-spacing: 1.5px; }
319
+ .hero-scene .hs-sub { font-size: 13.5px; fill: var(--muted); }
320
+ .hero-scene .hs-l1 { fill: var(--cyan-text); }
321
+ .hero-scene .hs-l2 { fill: var(--amber-text); }
322
+ .hero-scene .hs-l3 { fill: var(--green-text); }
323
+ .hero-scene .hs-punch { font-family: var(--display); font-style: italic; font-size: 14.5px;
324
+ fill: var(--cyan-text); text-anchor: end; }
325
+ .hero-scene .hs-note { text-anchor: end; font-style: italic; }
326
+
327
+ /* the surface prompt */
328
+ .hero-scene .hs-box { fill: var(--surface-2); stroke: var(--ridge); stroke-width: 1.5; }
329
+ .hero-scene .hs-prompt { font-family: var(--mono); font-size: 13.5px; text-anchor: middle;
330
+ fill: var(--ink-2); }
331
+ .hero-scene .hs-pmark, .hero-scene .hs-caret { fill: var(--amber-text); }
332
+
333
+ /* Small phones: the hero shrinks to ~320px wide, where the 13.5px sub-labels render ~9px —
334
+ present but unreadable (the exact bug this project keeps relearning: see
335
+ scripts/check-legibility.mjs). The strata composition keeps its SHAPE on phones — full-width
336
+ bands need no re-centering — so only the subs and the punchline hide, and the mains grow to
337
+ stay legible. The stage-legend below the figure repeats every word at full size; the
338
+ aria-label still carries the whole description to assistive tech. */
339
+ @media (max-width: 29em) {
340
+ .hero-scene .hs-sub, .hero-scene .hs-punch { display: none; }
341
+ .hero-scene .hs-main { font-size: 19px; }
342
+ .hero-scene .hs-prompt { font-size: 17.5px; }
343
+ .hero-scene .hs-box { width: 138px; }
344
+ .hero-cap { font-size: 10px; letter-spacing: 0.08em; } /* single line — a wrap eats the diagram */
345
+ /* with the sub-labels gone on phones, the legend IS the key — give it more presence */
346
+ .stage-legend { font-size: var(--fs--1); }
347
+ }
348
+
349
+ /* motion — only when the user allows it. The pulse is a prompt dropping down the plumb line;
350
+ depth 1's edge brightens as it lands (same 3.2s clock, so they stay in phase); strata 2 and 3
351
+ never stir. That IS the lesson: depth 1 works without you, the deeper strata only wake when
352
+ reached for. */
353
+ @media (prefers-reduced-motion: no-preference) {
354
+ .hero-scene .hs-caret { animation: hs-caret 1.1s steps(1) infinite; }
355
+ .hero-scene .hs-pulse { animation: hs-drop 3.2s cubic-bezier(0.3, 0, 0.4, 1) infinite; }
356
+ .hero-scene .hs-b1-halo { animation: hs-catch 3.2s linear infinite; }
357
+ }
358
+ @keyframes hs-caret { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }
359
+ @keyframes hs-drop {
360
+ 0% { transform: translateY(0); opacity: 0; }
361
+ 8% { transform: translateY(2px); opacity: 0.8; }
362
+ 45% { transform: translateY(44px); opacity: 0.55; } /* lands inside depth 1 (cy 50 → ~94) */
363
+ 60% { transform: translateY(44px); opacity: 0; }
364
+ 100% { transform: translateY(44px); opacity: 0; }
365
+ }
366
+ @keyframes hs-catch {
367
+ 0%, 40% { stroke-opacity: 0.85; }
368
+ 52% { stroke-opacity: 1; stroke-width: 3.4; }
369
+ 74%, 100% { stroke-opacity: 0.85; }
370
+ }
371
+
372
+ /* ── Collapsible sections (owner, 2026-07-24: "collapsible sections... otherwise it gets too
373
+ overwhelming as a wall of text"). Progressive enhancement: tips.js wraps each .depth-body
374
+ and wires the header; with JS off nothing collapses and the whole page reads as before.
375
+ The grid-rows 0fr/1fr trick animates height without measuring it. */
376
+ .depth.dc-ready > .depth-head { cursor: pointer; }
377
+ .depth.dc-ready > .depth-head:hover h2 { text-decoration: underline;
378
+ text-decoration-thickness: 1px; text-underline-offset: 4px; }
379
+ .depth.dc-ready > .depth-head:focus-visible { outline: 2px solid var(--cyan); outline-offset: 4px; border-radius: 6px; }
380
+ /* Some sections crown themselves with an art <figure> BEFORE the header — the JS wrapper only
381
+ captures children AFTER it, so a collapsed section would still wear its full-bleed art (seen
382
+ live 2026-07-24: depth-3's glass panels floating above its own collapsed header). Hide every
383
+ non-header child on collapse; the DOM stays untouched so the open layout is exactly as authored. */
384
+ .depth.is-collapsed > :not(.depth-head):not(.dc) { display: none; }
385
+ .dc { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.3s ease; }
386
+ .dci { overflow: hidden; min-height: 0; }
387
+ .depth.is-collapsed .dc { grid-template-rows: 0fr; }
388
+ .dc-chev { flex: 0 0 auto; margin-left: auto; width: 22px; height: 22px; align-self: center;
389
+ color: var(--muted); transition: transform 0.3s ease; }
390
+ .depth.is-collapsed .dc-chev { transform: rotate(-90deg); }
391
+ .dc-chev svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2;
392
+ stroke-linecap: round; stroke-linejoin: round; }
393
+ .dc-all { display: flex; justify-content: flex-end; margin: 0 0 10px; }
394
+ .dc-all button { font-family: var(--mono); font-size: 12px; letter-spacing: 0.08em;
395
+ color: var(--muted); background: none; border: 1px solid var(--hair); border-radius: 100px;
396
+ padding: 4px 12px; cursor: pointer; }
397
+ .dc-all button:hover { color: var(--ink-2); }
398
+ @media (prefers-reduced-motion: reduce) {
399
+ .dc, .dc-chev { transition: none; }
400
+ }
401
+
402
+ /* the three-depths legend (same idiom as the console's color legend) */
403
+ .stage-legend {
404
+ display: flex; flex-wrap: wrap; align-items: center; gap: 5px 16px;
405
+ margin: clamp(1.2rem, 3vw, 1.8rem) 0 14px;
406
+ font-size: 11px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
407
+ max-width: none;
408
+ }
409
+ .stage-legend .lg-cap { color: var(--faint); letter-spacing: 0.2em; }
410
+ .stage-legend .lg { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
411
+ .stage-legend .lg-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
412
+ .stage-legend .d-cyan { background: var(--cyan); }
413
+ .stage-legend .d-green { background: var(--green); }
414
+ .stage-legend .d-amber { background: var(--amber); }
415
+
416
+ /* ============================== 5 · DEPTH SECTIONS ============================== */
417
+
418
+ main { padding-bottom: clamp(2rem, 5vw, 4rem); }
419
+
420
+ .card {
421
+ position: relative;
422
+ background: linear-gradient(180deg, var(--surface) 0%, color-mix(in srgb, var(--surface) 55%, var(--bg-2)) 100%);
423
+ border: 1px solid var(--ridge);
424
+ border-radius: var(--radius);
425
+ box-shadow: var(--shadow);
426
+ margin: 0 0 clamp(14px, 2vw, 20px);
427
+ overflow: hidden;
428
+ animation: rise 0.55s var(--ease) both;
429
+ }
430
+ .card:nth-of-type(1) { animation-delay: 0.06s; }
431
+ .card:nth-of-type(2) { animation-delay: 0.14s; }
432
+ .card:nth-of-type(3) { animation-delay: 0.22s; }
433
+ @keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
434
+
435
+ /* stage rail — 3px color spine, same meaning as page one */
436
+ .card::before {
437
+ content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; z-index: 1;
438
+ pointer-events: none;
439
+ }
440
+ .rail-mirror::before { background: color-mix(in srgb, var(--cyan) 64%, transparent); }
441
+ .rail-prove::before { background: color-mix(in srgb, var(--green) 64%, transparent); }
442
+ .rail-act::before { background: color-mix(in srgb, var(--amber) 66%, transparent); }
443
+
444
+ /* stage chips (copied idiom) */
445
+ .stage {
446
+ display: inline-flex; align-items: center; gap: 6px;
447
+ font-family: var(--mono); font-size: 10.5px; font-weight: 600;
448
+ letter-spacing: 0.2em; text-transform: uppercase;
449
+ padding: 2.5px 10px 2.5px 8px; border-radius: 100px; border: 1px solid;
450
+ width: fit-content;
451
+ }
452
+ .stage::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
453
+ .st-mirror { color: var(--cyan-text); border-color: color-mix(in srgb, var(--cyan) 30%, transparent); background: color-mix(in srgb, var(--cyan) 8%, transparent); }
454
+ .st-rec { color: var(--amber-text); border-color: color-mix(in srgb, var(--amber) 32%, transparent); background: color-mix(in srgb, var(--amber) 8%, transparent); }
455
+ .st-prove { color: var(--green-text); border-color: color-mix(in srgb, var(--green) 30%, transparent); background: color-mix(in srgb, var(--green) 8%, transparent); }
456
+
457
+ /* Wide architecture diagrams — the honest answer to a bug measured on 2026-07-17.
458
+ A 900-unit diagram squeezed into a 261px phone column renders its labels at 3px: present,
459
+ unclipped, xmllint-clean, and completely invisible. The tempting fix — shrink the type to
460
+ fit — is what CAUSED the bug (the hero shipped at 8.1px the same way). A dense diagram
461
+ cannot honestly compress; below a certain width it simply stops being readable.
462
+ So we give it a floor and let the narrow viewport scroll to it. Each figure declares its
463
+ own --dmin (the width at which its smallest label clears 12px effective — MEASURE it,
464
+ don't guess: scripts/check-legibility.mjs computes declared x renderedWidth/viewBoxWidth
465
+ against the REAL page, not the asset in isolation). Swiping a legible diagram beats
466
+ squinting at a crushed one. */
467
+ .diagram-scroll {
468
+ margin: 0;
469
+ overflow-x: auto;
470
+ overscroll-behavior-x: contain;
471
+ -webkit-overflow-scrolling: touch;
472
+ border-radius: var(--radius-s);
473
+ }
474
+ .diagram-scroll img {
475
+ display: block;
476
+ width: 100%;
477
+ min-width: var(--dmin, 780px); /* the legibility floor, per diagram */
478
+ height: auto;
479
+ }
480
+ .diagram-scroll figcaption {
481
+ margin: 10px 2px 0;
482
+ font-size: var(--fs--2);
483
+ letter-spacing: 0.1em;
484
+ text-transform: uppercase;
485
+ color: var(--muted);
486
+ position: sticky; left: 0; /* stays put while the diagram scrolls under it */
487
+ }
488
+ /* Below the floor the figure scrolls, so say so — an unannounced scroll region is a trap. */
489
+ @media (max-width: 52em) {
490
+ .diagram-scroll::after {
491
+ content: "swipe the diagram →";
492
+ display: block;
493
+ margin-top: 8px;
494
+ font-family: var(--mono); font-size: var(--fs--2);
495
+ letter-spacing: 0.08em; color: var(--faint);
496
+ position: sticky; left: 0;
497
+ }
498
+ }
499
+
500
+ /* Section art — one still per depth, all three in ONE material language: a dark-glass
501
+ corridor, an amber prompt, cyan/green refraction. Each carries its depth's IDEA rather
502
+ than decorating it — 01 light travelling on its own · 02 light being answered · 03 one
503
+ panel deliberately lit among many. Generated (gemini-3-pro-image) from the console's own
504
+ token palette, which is why they match the page instead of sitting on top of it.
505
+ Full-bleed: the card's own overflow:hidden + radius do the clipping, no negative margins.
506
+ Cropped to a band via aspect-ratio — the source stills are 16:9 with dead sky/floor the
507
+ composition doesn't need. NOT loading="lazy": a lazy image inside this page already
508
+ shipped once that never fetched at all. */
509
+ .depth-art {
510
+ margin: 0;
511
+ aspect-ratio: 1584 / 640;
512
+ background: #0a0c10;
513
+ border-bottom: 1px solid var(--ridge);
514
+ }
515
+ .depth-art img {
516
+ width: 100%; height: 100%;
517
+ object-fit: cover; object-position: center;
518
+ display: block;
519
+ }
520
+ @media (max-width: 40em) {
521
+ .depth-art { aspect-ratio: 1584 / 900; }
522
+ }
523
+
524
+ .depth-head {
525
+ display: flex; align-items: flex-start; gap: clamp(14px, 2.4vw, 22px);
526
+ padding: clamp(18px, 3vw, 28px) clamp(16px, 3vw, 28px) 0;
527
+ }
528
+ .locator { flex: none; width: clamp(56px, 7vw, 76px); height: auto; margin-top: 4px; }
529
+
530
+ .depth-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
531
+ .depth-title h2 { font-size: var(--fs-2); margin: 0; }
532
+ .depth-title .q { font-size: var(--fs--1); color: var(--ink-2); line-height: 1.5; margin: 0; max-width: 58ch; }
533
+
534
+ .depth-num {
535
+ flex: none; align-self: flex-start;
536
+ font-size: clamp(1.6rem, 3vw, 2.2rem); font-weight: 300;
537
+ color: var(--faint); letter-spacing: 0.04em; line-height: 1;
538
+ font-variant-numeric: tabular-nums;
539
+ }
540
+
541
+ .depth-body { padding: clamp(14px, 2.4vw, 22px) clamp(16px, 3vw, 28px) clamp(18px, 3vw, 28px); }
542
+
543
+ /* the shared closing line of each depth — its metaphor, spoken once */
544
+ .metaphor {
545
+ margin: clamp(14px, 2.4vw, 20px) 0 0;
546
+ padding-top: 14px;
547
+ border-top: 1px solid var(--hair);
548
+ font-family: var(--display); font-style: italic;
549
+ font-size: var(--fs-0); color: var(--muted);
550
+ }
551
+
552
+ /* ---- depth 1: the three ambient guarantees ---- */
553
+ .ambient-grid {
554
+ list-style: none; margin: 0; padding: 0;
555
+ display: grid; grid-template-columns: 1fr; gap: 12px;
556
+ }
557
+ @media (min-width: 46em) { .ambient-grid { grid-template-columns: repeat(3, 1fr); } }
558
+ .ambient-grid li {
559
+ border: 1px solid var(--hair); border-radius: var(--radius-s);
560
+ background: var(--illo-plate);
561
+ padding: 16px 16px 14px;
562
+ display: flex; flex-direction: column; gap: 6px;
563
+ }
564
+ .ambient-grid b { color: var(--ink); font-size: var(--fs-0); font-weight: 650; }
565
+ .ambient-grid p { font-size: var(--fs--1); color: var(--ink-2); line-height: 1.5; margin: 0; }
566
+ .amb-ic {
567
+ width: 32px; height: 32px; border-radius: 9px; margin-bottom: 4px;
568
+ display: grid; place-items: center; border: 1px solid;
569
+ }
570
+ .amb-ic svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
571
+ .ic-cyan { color: var(--cyan-text); border-color: color-mix(in srgb, var(--cyan) 22%, transparent); background: color-mix(in srgb, var(--cyan) 8%, transparent); }
572
+
573
+ /* the permission slip between depth 1 and the rest */
574
+ .stop-here {
575
+ display: flex; align-items: center; gap: 16px;
576
+ margin: clamp(18px, 3vw, 28px) 0;
577
+ font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
578
+ color: var(--muted); text-align: center;
579
+ }
580
+ .stop-here::before, .stop-here::after {
581
+ content: ""; flex: 1 1 auto; height: 1px;
582
+ background: linear-gradient(90deg, transparent, var(--hair-strong));
583
+ }
584
+ .stop-here::after { background: linear-gradient(90deg, var(--hair-strong), transparent); }
585
+
586
+ /* ---- depth 2: spoken phrases as speech bubbles ---- */
587
+ .ask-rows { display: flex; flex-direction: column; gap: 14px; }
588
+ .ask-row { display: grid; grid-template-columns: 1fr; gap: 6px 18px; align-items: baseline; }
589
+ @media (min-width: 46em) { .ask-row { grid-template-columns: 148px 1fr; } }
590
+ .ask-cap {
591
+ font-size: 10.5px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase;
592
+ color: var(--muted); white-space: nowrap;
593
+ }
594
+ .say-wrap { display: flex; flex-wrap: wrap; gap: 8px 10px; }
595
+ .say {
596
+ position: relative;
597
+ font-family: var(--mono); font-size: 13px; line-height: 1.3;
598
+ color: var(--amber-text);
599
+ background: var(--fill);
600
+ border: 1px solid color-mix(in srgb, var(--amber) 28%, transparent);
601
+ border-radius: 13px 13px 13px 4px;
602
+ padding: 7px 13px;
603
+ white-space: nowrap;
604
+ }
605
+ @media (max-width: 30em) { .say { white-space: normal; } }
606
+
607
+ /* ---- depth 3: the pegboard ---- */
608
+ .peg-groups { display: grid; grid-template-columns: 1fr; gap: 12px; }
609
+ @media (min-width: 56em) { .peg-groups { grid-template-columns: repeat(3, 1fr); } }
610
+ .peg {
611
+ border: 1px solid var(--hair); border-radius: var(--radius-s);
612
+ padding: 16px 16px 14px;
613
+ /* the pegboard: a quiet grid of holes behind the tools */
614
+ background-color: var(--illo-plate);
615
+ background-image: radial-gradient(color-mix(in srgb, var(--ink) 9%, transparent) 1px, transparent 1.4px);
616
+ background-size: 18px 18px;
617
+ }
618
+ .peg-cap {
619
+ margin: 0 0 12px; font-size: 10.5px; font-weight: 600;
620
+ letter-spacing: 0.2em; text-transform: uppercase; color: var(--green-text);
621
+ }
622
+ .peg-list { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
623
+ .peg-list li { display: flex; flex-direction: column; gap: 1px; align-items: flex-start; }
624
+ .peg-list code { font-size: 13px; padding: 0.18em 0.55em; }
625
+ .peg-gloss { font-size: var(--fs--2); color: var(--muted); padding-left: 2px; }
626
+ .peg-note { font-size: var(--fs--1); color: var(--muted); line-height: 1.5; margin: 0; }
627
+
628
+ /* ============================== 6 · CLOSER + FOOTER ============================== */
629
+
630
+ .closer {
631
+ margin-top: clamp(1.6rem, 4vw, 2.6rem);
632
+ text-align: center;
633
+ display: flex; flex-direction: column; align-items: center; gap: 16px;
634
+ }
635
+ .closer-line { font-size: var(--fs-1); color: var(--ink-2); max-width: 46ch; margin: 0; }
636
+ .closer-line b { color: var(--ink); font-weight: 650; }
637
+
638
+ .btn-console {
639
+ display: inline-flex; align-items: center; gap: 9px;
640
+ font-family: var(--mono); font-size: 13.5px; font-weight: 600;
641
+ letter-spacing: 0.06em;
642
+ color: var(--on-accent); background: var(--amber);
643
+ border: 1px solid color-mix(in srgb, var(--amber) 70%, black);
644
+ border-radius: 11px; padding: 11px 20px;
645
+ transition: transform 0.15s var(--ease), box-shadow 0.2s var(--ease), background 0.18s var(--ease);
646
+ box-shadow: 0 10px 26px -14px color-mix(in srgb, var(--amber) 65%, transparent);
647
+ }
648
+ .btn-console:hover {
649
+ color: var(--on-accent);
650
+ background: color-mix(in srgb, var(--amber) 88%, white);
651
+ transform: translateY(-1px);
652
+ box-shadow: 0 14px 30px -14px color-mix(in srgb, var(--amber) 80%, transparent);
653
+ }
654
+ .btn-console:active { transform: none; }
655
+
656
+ /* RESTING MOTION. Owner, on the earlier version: "it just looks like a window, not a button."
657
+ Making it a filled amber button fixed the shape; it still sat perfectly still until touched, so
658
+ nothing on a long page said "this one is clickable." Everything here is deliberately understated —
659
+ a slow breath on the shadow and a periodic nudge of the arrow, both idling most of the cycle. The
660
+ goal is peripheral-vision pickup, not a demand for attention: a CTA that jitters continuously
661
+ reads as an advert, which on a page whose whole argument is "we tell you the truth quietly" would
662
+ cost more trust than the click is worth.
663
+ Pauses on hover, where the existing lift takes over and a second motion would just be noise. */
664
+ @keyframes btn-console-breathe {
665
+ 0%, 100% { box-shadow: 0 10px 26px -14px color-mix(in srgb, var(--amber) 65%, transparent); }
666
+ 50% { box-shadow: 0 12px 30px -13px color-mix(in srgb, var(--amber) 85%, transparent); }
667
+ }
668
+ @keyframes btn-console-nudge {
669
+ 0%, 78%, 100% { transform: translateX(0); }
670
+ 86% { transform: translateX(3px); }
671
+ 93% { transform: translateX(0); }
672
+ }
673
+ .btn-console { animation: btn-console-breathe 3.2s var(--ease) infinite; }
674
+ .btn-console:hover { animation: none; }
675
+ .btn-console > span[aria-hidden] {
676
+ display: inline-block; /* transform needs a block box; inline ignores translateX */
677
+ animation: btn-console-nudge 3.2s var(--ease) infinite;
678
+ }
679
+ .btn-console:hover > span[aria-hidden] { animation: none; transform: translateX(3px); }
680
+
681
+ .foot { border-top: 1px solid var(--hair); margin-top: clamp(1.5rem, 4vw, 3rem); }
682
+ .foot-inner { padding-block: 22px; }
683
+ .foot-line { font-size: var(--fs--1); color: var(--muted); margin: 0; }
684
+
685
+ /* ============================== 7 · MOTION SAFETY ============================== */
686
+
687
+ @media (prefers-reduced-motion: reduce) {
688
+ html { scroll-behavior: auto; }
689
+ .intro, .card { animation: none; }
690
+ .ring-halo, .caret { animation: none; }
691
+ .btn-console, .theme-toggle, .back-chip { transition: none; }
692
+ /* The resting breathe/nudge is an attention cue, which is exactly the class of motion this query
693
+ exists to switch off. Killing it on the arrow too — a looping transform is the part most likely
694
+ to bother someone who asked for less movement. The button keeps its colour and hover states, so
695
+ nothing about its affordance depends on the animation being present. */
696
+ .btn-console, .btn-console > span[aria-hidden] { animation: none; }
697
+ .btn-console:hover > span[aria-hidden] { transform: none; }
698
+ }
699
+
700
+ /* ── So what IS MetaHarness? ──────────────────────────────────────────────────────────────────────
701
+ The console asks you to switch on a word you've never seen. rUv flagged this exact risk in ADR-076
702
+ ("'Meta-harness' is a newer term… must define it in the first screen so it doesn't read as jargon")
703
+ and we shipped the jargon without the mitigation. The diagram IS the mitigation, so it gets room to
704
+ breathe rather than being squeezed beside prose. */
705
+ .mh-figure {
706
+ margin: 4px 0 14px; padding: 14px; border: 1px solid var(--line); border-radius: 12px;
707
+ background: var(--fill); overflow-x: auto;
708
+ }
709
+ .mh-figure img { display: block; width: 100%; height: auto; max-width: 900px; margin-inline: auto; }
710
+ .mh-cite { font-size: var(--step--2); color: var(--faint); margin-top: 10px; line-height: 1.6; }
711
+ .mh-cite code { color: var(--muted); }
712
+
713
+ /* ============================== 8 · CAPSTONE — the payoff ============================== */
714
+ /* The crown of the page: value reframe + does/doesn't + momentum. Reuses .card / .depth-head
715
+ so it reads as the same page; earns a SPECTRUM rail because this is where all three depths
716
+ pay off at once. 100% token-driven (both themes); every animation gated behind
717
+ prefers-reduced-motion; all text is HTML DOM (the glyphs carry no <text>, so nothing here
718
+ trips the SVG-scaling legibility trap). */
719
+
720
+ .rail-pay::before { background: var(--spectrum); opacity: 0.95; }
721
+
722
+ /* the payoff stage chip — a spectrum dot signals "all three depths, cashed in" */
723
+ .st-pay {
724
+ color: var(--amber-text);
725
+ border-color: color-mix(in srgb, var(--amber) 34%, transparent);
726
+ background: color-mix(in srgb, var(--amber) 9%, transparent);
727
+ }
728
+ .st-pay::before { background: var(--spectrum); }
729
+
730
+ .capstone .depth-sub {
731
+ font-size: var(--fs--1); color: var(--ink-2); line-height: 1.5; margin: 0; max-width: 60ch;
732
+ }
733
+
734
+ /* ---- Part 2 · the trade (effort spared │ power gained) ---- */
735
+ .trade {
736
+ display: grid; grid-template-columns: 1fr; gap: 14px;
737
+ margin-top: clamp(4px, 1.4vw, 10px);
738
+ }
739
+ @media (min-width: 52em) {
740
+ .trade { grid-template-columns: 1fr auto 1fr; gap: clamp(14px, 2.2vw, 28px); align-items: stretch; }
741
+ }
742
+
743
+ .trade-col {
744
+ border: 1px solid var(--hair); border-radius: var(--radius-s);
745
+ padding: 16px 16px 15px;
746
+ display: flex; flex-direction: column; gap: 13px;
747
+ }
748
+ .trade-spared { background: color-mix(in srgb, var(--red) 4%, var(--illo-plate)); }
749
+ .trade-gain { background: color-mix(in srgb, var(--green) 6%, var(--illo-plate)); }
750
+
751
+ .trade-cap {
752
+ display: flex; align-items: center; gap: 9px; margin: 0;
753
+ font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
754
+ }
755
+ .trade-spared .trade-cap { color: var(--muted); }
756
+ .trade-gain .trade-cap { color: var(--green-text); }
757
+ .trade-cap-ic { flex: none; width: 20px; height: 20px; display: grid; place-items: center; }
758
+ .trade-cap-ic svg { width: 20px; height: 20px; }
759
+ .trade-spared .trade-cap-ic { color: var(--red-text); }
760
+ .trade-gain .trade-cap-ic { color: var(--amber-text); }
761
+
762
+ .trade-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
763
+ .trade-list li { display: grid; grid-template-columns: 30px 1fr; gap: 11px; align-items: start; }
764
+ .trade-txt { font-size: var(--fs--1); line-height: 1.45; color: var(--ink-2); }
765
+ .trade-gain .trade-txt { color: var(--ink); }
766
+ .trade-note { display: block; margin-top: 3px; font-size: var(--fs--2); color: var(--muted); line-height: 1.4; }
767
+ .trade-note code { font-size: 0.92em; white-space: nowrap; }
768
+
769
+ /* per-row glyph chips */
770
+ .trade-ic { flex: none; width: 30px; height: 30px; border-radius: 8px; margin-top: 1px;
771
+ display: grid; place-items: center; border: 1px solid; }
772
+ .trade-ic svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
773
+ stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
774
+ /* left = restraint: one quiet red-toned chip for every "spared" row */
775
+ .trade-spared .trade-ic {
776
+ color: var(--muted);
777
+ border-color: color-mix(in srgb, var(--red) 24%, transparent);
778
+ background: color-mix(in srgb, var(--red) 6%, transparent);
779
+ }
780
+ /* right = gain: chips ride the three depth colors (cyan grounding · amber routing · green gates) */
781
+ .ic-cy { color: var(--cyan-text); border-color: color-mix(in srgb, var(--cyan) 26%, transparent); background: color-mix(in srgb, var(--cyan) 9%, transparent); }
782
+ .ic-am { color: var(--amber-text); border-color: color-mix(in srgb, var(--amber) 28%, transparent); background: color-mix(in srgb, var(--amber) 9%, transparent); }
783
+ .ic-gr { color: var(--green-text); border-color: color-mix(in srgb, var(--green) 26%, transparent); background: color-mix(in srgb, var(--green) 9%, transparent); }
784
+
785
+ /* the spine + transform arrow between the two columns */
786
+ .trade-spine { display: flex; align-items: center; justify-content: center; position: relative; }
787
+ .trade-arrow {
788
+ flex: none; width: 36px; height: 36px; border-radius: 50%;
789
+ display: grid; place-items: center; color: var(--on-accent);
790
+ background: var(--spectrum);
791
+ box-shadow: 0 8px 20px -10px color-mix(in srgb, var(--amber) 55%, transparent);
792
+ }
793
+ .trade-arrow svg { width: 19px; height: 19px; fill: none; stroke: currentColor;
794
+ stroke-width: 2.3; stroke-linecap: round; stroke-linejoin: round; }
795
+ /* desktop: vertical spine, arrow points right */
796
+ @media (min-width: 52em) {
797
+ .trade-spine { flex-direction: column; padding: 4px 0; }
798
+ .trade-spine::before, .trade-spine::after {
799
+ content: ""; flex: 1 1 auto; width: 2px;
800
+ background: linear-gradient(var(--hair-strong), transparent);
801
+ }
802
+ .trade-spine::after { background: linear-gradient(transparent, var(--hair-strong)); }
803
+ .trade-spine::before { margin-bottom: 12px; }
804
+ .trade-spine::after { margin-top: 12px; }
805
+ }
806
+ /* mobile: horizontal divider, arrow rotates to point down */
807
+ @media (max-width: 51.99em) {
808
+ .trade-spine { gap: 12px; }
809
+ .trade-spine::before, .trade-spine::after {
810
+ content: ""; flex: 1 1 auto; height: 1px;
811
+ background: linear-gradient(90deg, transparent, var(--hair-strong));
812
+ }
813
+ .trade-spine::after { background: linear-gradient(90deg, var(--hair-strong), transparent); }
814
+ .trade-arrow svg { transform: rotate(90deg); }
815
+ }
816
+
817
+ /* ---- Part 3 · the momentum proof ---- */
818
+ .momentum {
819
+ margin-top: clamp(18px, 3vw, 26px);
820
+ padding-top: clamp(16px, 3vw, 22px);
821
+ border-top: 1px solid var(--hair);
822
+ }
823
+ .mo-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; margin-bottom: 12px; }
824
+ .mo-hero { display: flex; align-items: baseline; gap: 11px; flex-wrap: wrap; }
825
+ .mo-hero-num {
826
+ font-family: var(--display); font-weight: 600; letter-spacing: -0.02em; line-height: 1;
827
+ font-size: clamp(2.1rem, 1.35rem + 3.5vw, 3.4rem);
828
+ background: var(--spectrum); -webkit-background-clip: text; background-clip: text; color: transparent;
829
+ }
830
+ .mo-hero-unit {
831
+ font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.08em;
832
+ text-transform: uppercase; color: var(--muted);
833
+ }
834
+ .mo-climb {
835
+ display: inline-flex; align-items: center; gap: 7px;
836
+ font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
837
+ letter-spacing: 0.06em; text-transform: uppercase; color: var(--green-text);
838
+ border: 1px solid color-mix(in srgb, var(--green) 28%, transparent);
839
+ background: color-mix(in srgb, var(--green) 9%, transparent);
840
+ border-radius: 100px; padding: 4px 12px;
841
+ }
842
+ .mo-climb svg { width: 17px; height: 13px; fill: none; stroke: currentColor;
843
+ stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
844
+ .mo-cap { font-size: var(--fs--1); color: var(--ink-2); margin: 0 0 clamp(13px, 2.2vw, 18px); max-width: 64ch; }
845
+
846
+ .mo-bars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
847
+ .mo-bar { display: grid; grid-template-columns: 118px 1fr 54px; align-items: center; gap: 13px; }
848
+ .mo-bar-name {
849
+ font-family: var(--mono); font-size: var(--fs--1); color: var(--ink-2);
850
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
851
+ }
852
+ .mo-bar-track {
853
+ height: 15px; border-radius: 100px; overflow: hidden;
854
+ background: color-mix(in srgb, var(--ink) 8%, transparent);
855
+ border: 1px solid var(--hair);
856
+ }
857
+ .mo-bar-fill {
858
+ display: block; height: 100%; width: var(--w); min-width: 7px;
859
+ border-radius: 100px; background: var(--spectrum);
860
+ transform-origin: left center;
861
+ }
862
+ .mo-bar-val {
863
+ font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
864
+ color: var(--ink); text-align: right; white-space: nowrap;
865
+ font-variant-numeric: tabular-nums;
866
+ }
867
+ @media (max-width: 34em) {
868
+ .mo-bar { grid-template-columns: 1fr auto; grid-template-areas: "name val" "track track"; row-gap: 6px; column-gap: 10px; }
869
+ .mo-bar-name { grid-area: name; }
870
+ .mo-bar-val { grid-area: val; }
871
+ .mo-bar-track { grid-area: track; }
872
+ }
873
+
874
+ .mo-source {
875
+ margin: clamp(14px, 2.4vw, 18px) 0 0;
876
+ font-size: var(--fs--2); letter-spacing: 0.07em; color: var(--faint);
877
+ }
878
+
879
+ /* the single closing line — hands off to the existing "Open the console →" CTA below */
880
+ .capstone-close {
881
+ margin: clamp(20px, 3.4vw, 30px) auto 0;
882
+ padding-top: clamp(16px, 2.6vw, 20px);
883
+ border-top: 1px solid var(--hair);
884
+ font-family: var(--display); font-size: var(--fs-1); line-height: 1.5;
885
+ color: var(--ink-2); text-align: center; max-width: 58ch;
886
+ }
887
+ .capstone-close b { color: var(--ink); font-weight: 650; }
888
+
889
+ /* motion — gated. Bars draw in from the left; at rest (or reduced-motion) they are already at
890
+ full width — a complete, legible chart, never a blank frame. */
891
+ @media (prefers-reduced-motion: no-preference) {
892
+ .mo-bar-fill { transform: scaleX(0); animation: mo-grow 1s var(--ease) forwards; }
893
+ .mo-bar:nth-child(1) .mo-bar-fill { animation-delay: 0.05s; }
894
+ .mo-bar:nth-child(2) .mo-bar-fill { animation-delay: 0.13s; }
895
+ .mo-bar:nth-child(3) .mo-bar-fill { animation-delay: 0.21s; }
896
+ .mo-bar:nth-child(4) .mo-bar-fill { animation-delay: 0.29s; }
897
+ .mo-bar:nth-child(5) .mo-bar-fill { animation-delay: 0.37s; }
898
+ }
899
+ @keyframes mo-grow { to { transform: scaleX(1); } }
900
+
901
+ /* ── THE PARTS DRAWER ────────────────────────────────────────────────────────────────────────────
902
+ A reference list, styled as one: dense, scannable, two columns on desktop, no illustration. It is
903
+ deliberately the plainest block on the page — everything above it is argument, this is inventory,
904
+ and dressing inventory up as argument is how a catalogue starts to read as a sales pitch. */
905
+ .parts-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(18px, 3vw, 38px); padding: 0 clamp(16px, 3vw, 34px) 4px; }
906
+ @media (max-width: 860px) { .parts-grid { grid-template-columns: 1fr; } }
907
+ .parts-col h3 {
908
+ font-size: .82rem; text-transform: uppercase; letter-spacing: .09em;
909
+ color: var(--muted); margin: 1.1rem 0 .5rem; font-weight: 650;
910
+ }
911
+ .parts-col h3:first-child { margin-top: 0; }
912
+ .parts-col dl { margin: 0; }
913
+ .parts-col dt { font-weight: 650; font-size: .93rem; margin-top: .7rem; }
914
+ .parts-col dd {
915
+ margin: .15rem 0 0; font-size: .89rem; line-height: 1.5; color: var(--muted-strong, var(--ink));
916
+ opacity: .92;
917
+ }
918
+ .parts-col dd code {
919
+ display: inline-block; margin-top: .18rem; font-size: .78rem;
920
+ color: var(--muted); background: color-mix(in srgb, var(--ink) 6%, transparent);
921
+ padding: .06em .38em; border-radius: 4px; word-break: break-word;
922
+ }
923
+ .parts-foot {
924
+ margin: 1.3rem clamp(16px, 3vw, 34px) 0; padding-top: .9rem;
925
+ border-top: 1px solid var(--hair); font-size: .87rem; color: var(--muted); max-width: 74ch;
926
+ }