@zerwiz/ymir 0.1.16 → 0.1.18

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 (171) hide show
  1. package/.agents/config/app-repos.yaml +38 -0
  2. package/.claude/settings.json +31 -0
  3. package/.codex/hooks.json +43 -0
  4. package/.cursor/hooks.json +33 -0
  5. package/.pi/extensions/README.md +39 -0
  6. package/.pi/extensions/gna-pi-watch.ts +15 -0
  7. package/.pi/extensions/lib/ro-assistant-layout.ts +96 -0
  8. package/.pi/extensions/lib/ro-operational-user-layout.ts +145 -0
  9. package/.pi/extensions/lib/ro-visibility.ts +99 -0
  10. package/.pi/extensions/lib/ro-working-ship.ts +243 -0
  11. package/.pi/extensions/lib/rodd-operational-input.ts +62 -0
  12. package/.pi/extensions/lib/skuld-branch-dispatch.ts +252 -0
  13. package/.pi/extensions/lib/skuld-branch-model-picker.ts +77 -0
  14. package/.pi/extensions/lib/vordr-sessionstart-supervisor.mjs +48 -0
  15. package/.pi/extensions/ro.ts +11 -0
  16. package/.pi/extensions/skuld-branch-supervision.ts +11 -0
  17. package/.pi/extensions/syn-turnend-guard.ts +11 -0
  18. package/.pi/mcp.json +16 -0
  19. package/.pi/mcp.json.example +11 -0
  20. package/.pi/npm/.gitignore +2 -0
  21. package/.pi/settings.json +8 -0
  22. package/.pi/shared/extensions/gna-pi-watch.ts +750 -0
  23. package/.pi/shared/extensions/herdr-agent-state.ts +257 -0
  24. package/.pi/shared/extensions/open-editor.ts +509 -0
  25. package/.pi/shared/extensions/ro.ts +504 -0
  26. package/.pi/shared/extensions/skuld-branch-supervision.ts +1535 -0
  27. package/.pi/shared/extensions/syn-turnend-guard.ts +683 -0
  28. package/.pi/shared/extensions/todo.ts +133 -0
  29. package/.pi/shared/extensions/ymir-subagents.ts +287 -0
  30. package/.pi/shared/extensions/ymir-well.ts +130 -0
  31. package/CHANGELOG.md +23 -0
  32. package/assets/Ymir.md +4654 -0
  33. package/assets/healer.html +717 -0
  34. package/assets/icon-family/family-sheet.png +0 -0
  35. package/assets/icon-family/hlidskjalf.svg +1 -0
  36. package/assets/icon-family/sessrumnir.svg +1 -0
  37. package/assets/icon-family/smidja.svg +1 -0
  38. package/assets/icon-family/ymir.svg +1 -0
  39. package/assets/mimirswell.html +880 -0
  40. package/assets/mock/README.md +16 -0
  41. package/assets/mock/feeds.ts +175 -0
  42. package/assets/mock/mock.ts +408 -0
  43. package/assets/reference/docs/agent-control.md +122 -0
  44. package/assets/reference/docs/apodex/Appdex_as_Orchestrator_Planner_Reviewer_Assessment.md +176 -0
  45. package/assets/reference/docs/apodex/Final Report(1).md +198 -0
  46. package/assets/reference/docs/apodex/Final Report(2).md +54 -0
  47. package/assets/reference/docs/apodex/Final Report(3).md +40 -0
  48. package/assets/reference/docs/apodex/Final Report(4).md +234 -0
  49. package/assets/reference/docs/apodex/Final Report(5).md +13 -0
  50. package/assets/reference/docs/apodex/Final Report.md +82 -0
  51. package/assets/reference/docs/architecture.md +392 -0
  52. package/assets/reference/docs/arm-pretool-check.md +247 -0
  53. package/assets/reference/docs/calm-mode-feasibility.md +541 -0
  54. package/assets/reference/docs/calm.md +59 -0
  55. package/assets/reference/docs/captain-hold-lifecycle.md +96 -0
  56. package/assets/reference/docs/cd-guard.md +165 -0
  57. package/assets/reference/docs/cmux-backend.md +133 -0
  58. package/assets/reference/docs/codex-app-backend.md +57 -0
  59. package/assets/reference/docs/configuration.md +924 -0
  60. package/assets/reference/docs/documentation-audiences.json +468 -0
  61. package/assets/reference/docs/documentation-audiences.md +28 -0
  62. package/assets/reference/docs/examples/crew-dispatch.json +26 -0
  63. package/assets/reference/docs/examples/process-event-extension/file-signal.mjs +96 -0
  64. package/assets/reference/docs/examples/process-event-extension/firstmate-extension.json +15 -0
  65. package/assets/reference/docs/examples/watched-tools.json +24 -0
  66. package/assets/reference/docs/examples/wedge-alarm +22 -0
  67. package/assets/reference/docs/extension-bindings.md +237 -0
  68. package/assets/reference/docs/fm-test-isolation-proof.json +37 -0
  69. package/assets/reference/docs/fm-test-isolation-proof.md +149 -0
  70. package/assets/reference/docs/fm-test-portable-shards.md +118 -0
  71. package/assets/reference/docs/gitlab-merge-watch.md +275 -0
  72. package/assets/reference/docs/herdr-backend.md +346 -0
  73. package/assets/reference/docs/orca-backend.md +84 -0
  74. package/assets/reference/docs/pi-supervision-branch-poster.svg +125 -0
  75. package/assets/reference/docs/pi-supervision-branch.md +96 -0
  76. package/assets/reference/docs/plans/agentic-engineering-workflow.md +395 -0
  77. package/assets/reference/docs/plans/agentic-factory-implementation-plan.md +615 -0
  78. package/assets/reference/docs/plans/command-factory-enhancement-plan.md +420 -0
  79. package/assets/reference/docs/plans/phase-1-layer3-program-design.md +134 -0
  80. package/assets/reference/docs/plans/phase-2-vertical-slices.md +100 -0
  81. package/assets/reference/docs/plans/phase-3-measurable-goals.md +106 -0
  82. package/assets/reference/docs/plans/phase-4-context-budget.md +106 -0
  83. package/assets/reference/docs/plans/phase-5-pre-mortem-confidence.md +100 -0
  84. package/assets/reference/docs/plans/phase-6-incident-to-agent.md +109 -0
  85. package/assets/reference/docs/remote-secondmates.md +268 -0
  86. package/assets/reference/docs/scripts.md +146 -0
  87. package/assets/reference/docs/sessionstart-nudge.md +115 -0
  88. package/assets/reference/docs/subagent-guard.md +383 -0
  89. package/assets/reference/docs/supervision-protocols/claude.md +27 -0
  90. package/assets/reference/docs/supervision-protocols/codex.md +16 -0
  91. package/assets/reference/docs/supervision-protocols/cursor.md +31 -0
  92. package/assets/reference/docs/supervision-protocols/grok.md +39 -0
  93. package/assets/reference/docs/supervision-protocols/opencode.md +17 -0
  94. package/assets/reference/docs/supervision-protocols/pi.md +33 -0
  95. package/assets/reference/docs/supervision-protocols/unknown.md +13 -0
  96. package/assets/reference/docs/tmux-backend.md +115 -0
  97. package/assets/reference/docs/trace-context.md +117 -0
  98. package/assets/reference/docs/turnend-guard.md +170 -0
  99. package/assets/reference/docs/verification/dispatch-auth.md +204 -0
  100. package/assets/reference/docs/verification/muse.md +221 -0
  101. package/assets/reference/docs/verification/process-event-sources.md +208 -0
  102. package/assets/reference/docs/verification/public-followup.md +119 -0
  103. package/assets/reference/docs/verification/runtime-backends.md +1022 -0
  104. package/assets/reference/docs/verification/stow-memory.md +53 -0
  105. package/assets/reference/docs/verification/supervision.md +534 -0
  106. package/assets/reference/docs/verification/trace-context.md +31 -0
  107. package/assets/reference/docs/voice-relay.md +295 -0
  108. package/assets/reference/docs/watcher-continuity.md +119 -0
  109. package/assets/reference/docs/wedge-alarm.md +39 -0
  110. package/assets/reference/docs/zellij-backend.md +114 -0
  111. package/assets/reference/index.html +1172 -0
  112. package/assets/reference/skills/afk/SKILL.md +241 -0
  113. package/assets/reference/skills/ahoy/SKILL.md +56 -0
  114. package/assets/reference/skills/ask-user-authority/SKILL.md +56 -0
  115. package/assets/reference/skills/bearings/SKILL.md +161 -0
  116. package/assets/reference/skills/bearings/assets/board-template.html +731 -0
  117. package/assets/reference/skills/bootstrap-diagnostics/SKILL.md +68 -0
  118. package/assets/reference/skills/captain-hold-lifecycle/SKILL.md +54 -0
  119. package/assets/reference/skills/decision-hold-lifecycle/SKILL.md +15 -0
  120. package/assets/reference/skills/diagnostic-reasoning/SKILL.md +53 -0
  121. package/assets/reference/skills/firstmate-codexapp/SKILL.md +110 -0
  122. package/assets/reference/skills/firstmate-codexapp/agents/openai.yaml +4 -0
  123. package/assets/reference/skills/firstmate-coding-guidelines/SKILL.md +127 -0
  124. package/assets/reference/skills/firstmate-orca/SKILL.md +92 -0
  125. package/assets/reference/skills/fmx-respond/SKILL.md +283 -0
  126. package/assets/reference/skills/harness-adapters/SKILL.md +95 -0
  127. package/assets/reference/skills/harness-adapters/references/common/control-and-recovery.md +37 -0
  128. package/assets/reference/skills/harness-adapters/references/common/dispatch.md +32 -0
  129. package/assets/reference/skills/harness-adapters/references/common/model-and-effort.md +42 -0
  130. package/assets/reference/skills/harness-adapters/references/common/primary-hooks.md +40 -0
  131. package/assets/reference/skills/harness-adapters/references/harness/claude.md +55 -0
  132. package/assets/reference/skills/harness-adapters/references/harness/codex.md +43 -0
  133. package/assets/reference/skills/harness-adapters/references/harness/cursor.md +75 -0
  134. package/assets/reference/skills/harness-adapters/references/harness/grok.md +69 -0
  135. package/assets/reference/skills/harness-adapters/references/harness/kimi.md +51 -0
  136. package/assets/reference/skills/harness-adapters/references/harness/muse.md +70 -0
  137. package/assets/reference/skills/harness-adapters/references/harness/opencode.md +42 -0
  138. package/assets/reference/skills/harness-adapters/references/harness/pi.md +56 -0
  139. package/assets/reference/skills/process-event-sources/SKILL.md +137 -0
  140. package/assets/reference/skills/project-management/SKILL.md +92 -0
  141. package/assets/reference/skills/quota-array-dispatch/SKILL.md +131 -0
  142. package/assets/reference/skills/secondmate-provisioning/SKILL.md +254 -0
  143. package/assets/reference/skills/stow/SKILL.md +309 -0
  144. package/assets/reference/skills/stuck-crewmate-recovery/SKILL.md +54 -0
  145. package/assets/reference/skills/updatefirstmate/SKILL.md +64 -0
  146. package/assets/skills/README.md +5 -0
  147. package/assets/yggdrasil.html +649 -0
  148. package/assets/ymir-banner-01.png +0 -0
  149. package/assets/ymir-banner-02.png +0 -0
  150. package/assets/ymir-banner-03.png +0 -0
  151. package/assets/ymir-banner-04.png +0 -0
  152. package/assets/ymir-banner-05.png +0 -0
  153. package/assets/ymir-banner-06.png +0 -0
  154. package/assets/ymir-emblem-darksteel.svg +63 -0
  155. package/assets/ymir-emblem-forged.svg +55 -0
  156. package/assets/ymir-emblem-slate.svg +55 -0
  157. package/assets/ymir-mark-algiz-anvil.svg +9 -0
  158. package/assets/ymir-stave.svg +53 -0
  159. package/assets/ymir.html +563 -0
  160. package/bin/ymir-install.sh +6 -1
  161. package/data/learnings.md +33 -0
  162. package/data/local-models.md +34 -0
  163. package/data/operator.md +15 -0
  164. package/data/projects.md +28 -0
  165. package/data/realm.md +1 -0
  166. package/deploy/Containerfile +27 -0
  167. package/deploy/README.md +104 -0
  168. package/deploy/compose/compose.yaml +45 -0
  169. package/deploy/env.example +30 -0
  170. package/deploy/quadlet/ymir.container +57 -0
  171. package/package.json +10 -8
@@ -0,0 +1,649 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Yggdrasil — The World Tree</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Cormorant:wght@500;600&family=Newsreader:ital,opsz,wght@0,6..72,300..500;1,6..72,300..500&family=IBM+Plex+Mono:wght@300;400;500&family=Noto+Sans+unic&display=swap" rel="stylesheet">
10
+ <style>
11
+ /* ============ tokens — obsidian, bone, bronze, blood ============ */
12
+ :root{
13
+ --bg:#0e0c09; --panel:#151209; --panel3:#14100b;
14
+ --line:#2b241a; --line-soft:#221d14;
15
+ --bone:#cfc3a9; --bone-dim:#9a8f75; --bone-faint:#6b6250;
16
+ --steel:#96a0a8; --steel-dim:#5f686e;
17
+ --bronze:#c9973f; --bronze-deep:#7d5f2a;
18
+ --blood:#7c3a30; --blood-lit:#c2584a;
19
+ --disp:'Cormorant',serif; --serif:'Newsreader',Georgia,serif;
20
+ --mono:'IBM Plex Mono',monospace; --run:'Noto Sans Runic','Segoe UI Historic',serif;
21
+ }
22
+ *{box-sizing:border-box;margin:0;padding:0}
23
+ body{background:var(--bg);color:var(--bone);font-family:var(--serif);
24
+ -webkit-font-smoothing:antialiased;overflow-x:hidden}
25
+ ::selection{background:#57411a;color:#f0e6cd}
26
+ :focus-visible{outline:1px solid var(--bronze);outline-offset:3px}
27
+
28
+ /* stone grain + vignette */
29
+ body::before{content:'';position:fixed;inset:0;z-index:60;pointer-events:none;opacity:.055;
30
+ background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
31
+ body::after{content:'';position:fixed;inset:0;z-index:59;pointer-events:none;
32
+ box-shadow:inset 0 0 170px 30px rgba(0,0,0,.5)}
33
+ #gap{position:fixed;inset:0;width:100%;height:100%;z-index:0}
34
+ main,footer{position:relative;z-index:2}
35
+
36
+ /* ============ topbar ============ */
37
+ .topbar{position:fixed;inset:0 0 auto 0;height:46px;display:flex;justify-content:space-between;align-items:center;
38
+ padding:0 clamp(1rem,3vw,2rem);z-index:50;background:rgba(13,11,8,.78);backdrop-filter:blur(10px);
39
+ border-bottom:1px solid var(--line-soft);font-family:var(--mono);font-size:.58rem;
40
+ letter-spacing:.3em;color:var(--bone-dim);text-transform:uppercase}
41
+ .topbar .rn{color:var(--bronze);margin-right:.6em;font-size:.85rem}
42
+ main{padding-top:46px}
43
+
44
+ /* ============ stage ============ */
45
+ .stage{min-height:calc(100svh - 46px);display:flex;align-items:center;justify-content:center;
46
+ position:relative;padding:74px 1rem 2rem;animation:fadeUp 1s .05s both}
47
+ .scenewrap{width:min(94vw, calc(76svh * .691), 620px);aspect-ratio:760/1100}
48
+ #scene{width:100%;height:100%;display:block}
49
+ @keyframes fadeUp{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
50
+
51
+ .anno{position:absolute;top:96px;max-width:250px;line-height:1.95;font-size:.92rem;z-index:3}
52
+ .anno-l{left:clamp(1rem,4vw,3.2rem);font-style:italic;color:var(--bone-faint)}
53
+ .anno-r{right:clamp(1rem,4vw,3.2rem);text-align:right;font-family:var(--mono);font-size:.56rem;
54
+ letter-spacing:.26em;color:var(--steel-dim);text-transform:uppercase;line-height:2.5}
55
+ @media(max-width:900px){.anno{display:none}}
56
+
57
+ /* ============ SVG — the carving ============ */
58
+ #scene text{font-family:var(--mono);user-select:none}
59
+ g[data-part]{cursor:pointer;outline:none}
60
+ .wgroup{transition:opacity .45s}
61
+ #scene.focus g.wgroup:not(.lit){opacity:.45}
62
+
63
+ /* world-glyph defaults (the machine side) */
64
+ .wgroup .gs{stroke:#4a4232;fill:#12100b;stroke-width:2;transition:stroke .4s,fill .4s}
65
+ .wgroup .st{stroke:#4a4232;stroke-width:2.4;fill:none;transition:stroke .4s}
66
+ .wgroup .wk{stroke:#4a4232;stroke-width:1.8;fill:none;stroke-dasharray:4 7;transition:stroke .4s}
67
+ .wgroup .wt{fill:none;stroke:#4a4232;stroke-width:1.4;stroke-dasharray:2 8;transition:stroke .4s}
68
+ .wgroup .wm{stroke:#2e261a;stroke-width:1.6;fill:none;stroke-linecap:round;transition:stroke .4s}
69
+ .wgroup.lit .gs{stroke:var(--bronze);fill:#231b10}
70
+ .wgroup.lit .st{stroke:var(--bronze)}
71
+ .wgroup.lit .wk{stroke:var(--bronze);animation:march 1.2s linear infinite}
72
+ .wgroup.lit .wt{stroke:var(--bronze)}
73
+ .wgroup.lit .wm{stroke:var(--bronze)}
74
+ .wgroup .doorRune{font-family:var(--run);font-size:11px;fill:#6b5b3a;transition:fill .4s}
75
+ .wgroup.lit .doorRune{fill:var(--bronze)}
76
+ .wgroup .subRune{font-family:var(--run);font-size:17px;fill:#6d5426;transition:fill .4s}
77
+ .wgroup.lit .subRune{fill:var(--bronze)}
78
+
79
+ /* tree anatomy defaults (the myth side) */
80
+ .bpart .gs{stroke:#4a3d2a;fill:#161009;stroke-width:2;transition:stroke .4s,fill .4s}
81
+ .bpart .limb-o{fill:none;stroke:#4a3d2a;stroke-linecap:round;transition:stroke .4s}
82
+ .bpart .limb-i{fill:none;stroke:#0e0c09;stroke-linecap:round}
83
+ .bpart .det{fill:none;stroke:#2e261a;stroke-linecap:round;transition:stroke .4s}
84
+ .bpart .rid{fill:none;stroke:#241e14;stroke-width:2.5}
85
+ .bpart .trunkRune{font-family:var(--run);fill:#6d5426;transition:fill .4s;animation:rp 6.8s ease-in-out infinite}
86
+ .bpart .trunkRune2{font-family:var(--run);fill:#4a3d2a;transition:fill .4s}
87
+ .bpart .eyeE{fill:#8f4a33;transition:fill .4s}
88
+ .bpart.lit .gs{stroke:var(--bronze)}
89
+ .bpart.lit .fillable{fill:#241b0e}
90
+ .bpart.lit .limb-o{stroke:var(--bronze)}
91
+ .bpart.lit .det{stroke:var(--bronze)}
92
+ .bpart.lit .trunkRune,.bpart.lit .trunkRune2{fill:var(--bronze)}
93
+ .bpart.lit .eyeE{fill:var(--blood-lit)}
94
+ @keyframes rp{0%,100%{opacity:.45}50%{opacity:.85}}
95
+
96
+ /* parallel worktree marks — carved along the limbs, lit only */
97
+ .wkt{fill:none;stroke:var(--bronze);stroke-width:1.4;stroke-dasharray:3 8;opacity:0;transition:opacity .4s}
98
+ .bpart.lit .wkt{opacity:.75;animation:march 1s linear infinite}
99
+
100
+ /* the messenger's track */
101
+ .track{fill:none;stroke:#2c251a;stroke-width:1.6;stroke-dasharray:4 8;transition:stroke .4s}
102
+ .bpart.lit .track{stroke:var(--bronze);animation:march 1s linear infinite}
103
+ .chip{fill:#6d5426;transition:fill .4s}
104
+ .bpart.lit .chip{fill:var(--bronze)}
105
+
106
+ /* labels */
107
+ .lab{font-size:18px;letter-spacing:4.2px;fill:#6f6650;opacity:.92;paint-order:stroke;
108
+ stroke:#0e0c09;stroke-width:5;stroke-linejoin:round;transition:fill .4s}
109
+ .lab2{font-size:10px;letter-spacing:3px;fill:#5a5240}
110
+ .lab3{font-size:9.5px;letter-spacing:2.6px;fill:#5a5240;transition:fill .4s}
111
+ .wgroup.lit .lab{fill:var(--bone)}
112
+ .wgroup.lit .lab3{fill:var(--bone-dim)}
113
+
114
+ /* bifrost — the burning bridge, faint */
115
+ .bifA{stroke:#7d5f2a;fill:none;stroke-width:2;opacity:.55}
116
+ .bifB{stroke:#7d5f2a;fill:none;stroke-width:1.2;opacity:.35}
117
+ .bifd{stroke:#7d5f2a;fill:none;stroke-width:1;stroke-dasharray:2 9;opacity:.5;animation:march 1.5s linear infinite}
118
+
119
+ /* invisible hit areas */
120
+ .hit{fill:#000;fill-opacity:0;stroke:#000;stroke-opacity:0;pointer-events:all}
121
+
122
+ /* connectors — hidden until the part is lit */
123
+ .lnk{fill:none;stroke:var(--bronze);stroke-width:1.6;stroke-dasharray:3 8;opacity:0;
124
+ transition:opacity .4s;pointer-events:none}
125
+ .lnk.lit{opacity:.75;animation:march 1.1s linear infinite}
126
+ @keyframes march{to{stroke-dashoffset:-22}}
127
+
128
+ /* the well remembers — ripples only while lit */
129
+ .rip{fill:none;stroke:var(--bronze);stroke-width:1.3;opacity:0;transform-box:fill-box;transform-origin:center}
130
+ #wellG.lit .rip{animation:rip 1.2s ease-out infinite}
131
+ #wellG.lit .rip.r2{animation-delay:.6s}
132
+ @keyframes rip{0%{transform:scale(.2);opacity:.9}100%{transform:scale(3.2);opacity:0}}
133
+
134
+ /* ============ caption + plinth ============ */
135
+ .plate{max-width:780px;margin:0 auto;padding:0 1.4rem 3rem;animation:fadeUp 1s .35s both}
136
+ .cap{text-align:center;font-family:var(--mono);font-size:.62rem;letter-spacing:.24em;
137
+ text-transform:uppercase;color:var(--bone-faint);line-height:2.6}
138
+ .cap button{background:none;border:none;cursor:pointer;font:inherit;color:var(--bone-dim);
139
+ letter-spacing:inherit;padding:.2em .1em;border-bottom:1px solid transparent;
140
+ transition:color .25s,border-color .25s}
141
+ .cap button:hover,.cap button.on{color:var(--bronze);border-bottom-color:var(--bronze-deep)}
142
+ .cap i{font-style:normal;color:var(--bone-faint);margin:0 .3em}
143
+
144
+ .carved{background:var(--panel3);border:1px solid var(--line);
145
+ box-shadow:inset 0 1px 0 rgba(255,244,214,.03),inset 0 -1px 0 rgba(0,0,0,.5),0 2px 16px rgba(0,0,0,.35)}
146
+ .plinth{margin-top:1.7rem;padding:1.7rem clamp(1.2rem,4vw,2.1rem) 1.9rem}
147
+ .pl-head{display:flex;align-items:center;gap:1.3rem;padding-bottom:1.2rem;border-bottom:1px solid var(--line)}
148
+ #pRune{font-family:var(--run);font-size:2.6rem;color:var(--bronze);line-height:1}
149
+ .pl-k{font-family:var(--mono);font-size:.56rem;letter-spacing:.34em;color:var(--steel-dim);text-transform:uppercase}
150
+ #pName{font-family:var(--disp);font-weight:600;font-size:1.3rem;letter-spacing:.14em;color:var(--bone);margin-top:.35rem}
151
+ .prow{display:grid;grid-template-columns:5.4rem 1fr;gap:1.2rem;padding:1.05rem 0;
152
+ border-bottom:1px dashed var(--line-soft)}
153
+ .prow:last-of-type{border-bottom:none}
154
+ .ptag{font-family:var(--mono);font-size:.56rem;letter-spacing:.26em;color:var(--bronze);
155
+ text-transform:uppercase;padding-top:.4rem}
156
+ .pmyth{font-family:var(--serif);font-style:italic;font-size:1.07rem;color:var(--bone);line-height:1.75}
157
+ .pymir{font-family:var(--mono);font-size:.72rem;color:var(--steel);line-height:1.85}
158
+ .pl-head,.prow{transition:opacity .18s}
159
+ .plinth.swap .pl-head,.plinth.swap .prow{opacity:.06}
160
+ @media(max-width:560px){.prow{grid-template-columns:1fr;gap:.3rem}.ptag{padding-top:0}}
161
+
162
+ .pl-actions{text-align:center;margin-top:1.9rem}
163
+ .btn-carv{font-family:var(--mono);font-size:.64rem;letter-spacing:.32em;text-indent:.32em;text-transform:uppercase;
164
+ color:var(--bone);background:var(--panel);border:1px solid var(--line);padding:.95rem 1.9rem;cursor:pointer;
165
+ box-shadow:inset 0 1px 0 rgba(255,244,214,.05),inset 0 -1px 0 rgba(0,0,0,.55),0 2px 10px rgba(0,0,0,.4);
166
+ transition:border-color .3s,color .3s,transform .1s}
167
+ .btn-carv .rn{color:var(--bronze);margin-right:.7em;font-size:.85rem;letter-spacing:0;text-indent:0}
168
+ .btn-carv:hover{border-color:var(--bronze-deep);color:#e6dcc2}
169
+ .btn-carv:active{transform:translateY(1px)}
170
+ .btn-carv.busy{opacity:.55;pointer-events:none}
171
+
172
+ /* ============ footer ============ */
173
+ footer{border-top:1px solid var(--line);padding:3.6rem 1.5rem 4rem;text-align:center;animation:fadeUp 1s .55s both}
174
+ .frule{display:flex;align-items:center;gap:1.4rem;max-width:640px;margin:0 auto 2.8rem}
175
+ .frule::before,.frule::after{content:'';flex:1;height:1px;background:var(--line-soft)}
176
+ .frule span{font-family:var(--run);font-size:.7rem;letter-spacing:.42em;color:var(--bone-faint);white-space:nowrap}
177
+ .f-big{font-family:var(--disp);font-weight:600;font-size:clamp(1.15rem,2.6vw,1.65rem);
178
+ letter-spacing:.14em;color:var(--bone);text-shadow:0 -1px 0 rgba(0,0,0,.8)}
179
+ .f-mono{font-family:var(--mono);font-size:.56rem;letter-spacing:.3em;color:var(--steel-dim);
180
+ margin-top:1.7rem;text-transform:uppercase;line-height:2.3}
181
+ .f-repo{font-family:var(--mono);font-size:.58rem;color:var(--bone-faint);letter-spacing:.16em;margin-top:1.2rem}
182
+
183
+ @media (prefers-reduced-motion:reduce){
184
+ *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
185
+ }
186
+ </style>
187
+ </head>
188
+ <body>
189
+
190
+ <div class="topbar">
191
+ <span><span class="rn">ᛇ</span>YGGDRASIL · THE WORLD TREE</span>
192
+ <span>nine realms · one trunk</span>
193
+ </div>
194
+
195
+ <canvas id="gap" aria-hidden="true"></canvas>
196
+
197
+ <main>
198
+ <!-- ============ THE STANDING TREE ============ -->
199
+ <section class="stage">
200
+ <p class="anno anno-l">Yggdrasil — the ash that binds the worlds. They are not separate:
201
+ they hang in one tree, connected, holding each other up. An eagle watches from the crown,
202
+ a dragon gnaws the far root, and a squirrel runs between them without rest.</p>
203
+ <p class="anno anno-r">git worktree isolation<br>zero-collision parallel edits<br>every realm routes<br>through one trunk</p>
204
+
205
+ <div class="scenewrap">
206
+ <svg id="scene" viewBox="0 0 760 1100" role="group" aria-label="The world tree Yggdrasil, and the machine carved around it">
207
+ <defs>
208
+ <radialGradient id="halo" cx="50%" cy="50%" r="50%">
209
+ <stop offset="0" stop-color="#20180e" stop-opacity=".5"/>
210
+ <stop offset="1" stop-color="#20180e" stop-opacity="0"/>
211
+ </radialGradient>
212
+ </defs>
213
+ <ellipse cx="380" cy="560" rx="330" ry="470" fill="url(#halo)" pointer-events="none"/>
214
+
215
+ <!-- Bifrost — the burning bridge, entering from beyond the frame -->
216
+ <path class="bifA" d="M-8 252 C62 200 122 186 172 198"/>
217
+ <path class="bifB" d="M-8 266 C64 214 124 200 172 212"/>
218
+ <path class="bifd" d="M-8 259 C63 207 123 193 172 205"/>
219
+ <text class="lab2" x="30" y="222">BIFROST</text>
220
+
221
+ <!-- ======== THE WORLD FEATURES (the machine, carved around the tree) ======== -->
222
+
223
+ <!-- THE HIGH SEAT -->
224
+ <g id="seatG" class="wgroup" data-part="crown" tabindex="0" role="button"
225
+ aria-label="the high seat — what the crown became">
226
+ <path class="st" d="M616 56 L616 122"/>
227
+ <path class="gs" d="M616 42 L623 51 L616 60 L609 51 Z"/>
228
+ <path class="gs" d="M604 122 L648 122 L648 134 L604 134 Z"/>
229
+ <path class="st" d="M608 134 L608 154 M644 134 L644 154"/>
230
+ <path class="gs" d="M616 96 L602 96 L602 108 L616 108 Z"/>
231
+ <path class="gs" d="M622 84 C628 76 642 76 648 84 C642 92 628 92 622 84 Z"/>
232
+ <path class="gs" d="M635 78 L639 84 L635 90 L631 84 Z"/>
233
+ <text class="lab" x="626" y="176" text-anchor="middle">THE HIGH SEAT</text>
234
+ <text class="lab3" x="626" y="194" text-anchor="middle">hlidskjalf · control plane</text>
235
+ <rect class="hit" x="592" y="36" width="72" height="130"/>
236
+ </g>
237
+
238
+ <!-- THE WORKTREES -->
239
+ <g id="wtrG" class="wgroup" data-part="branches" tabindex="0" role="button"
240
+ aria-label="the worktrees — what the branches became">
241
+ <path class="wk" d="M64 342 C92 326 122 306 148 286"/>
242
+ <path class="wk" d="M64 346 C94 338 124 330 152 322"/>
243
+ <path class="wk" d="M64 350 C92 352 122 358 148 368"/>
244
+ <path class="gs" d="M56 338 L64 346 L56 354 L48 346 Z"/>
245
+ <path class="gs" d="M152 277 L158 284 L152 291 L146 284 Z"/>
246
+ <path class="gs" d="M156 313 L162 320 L156 327 L150 320 Z"/>
247
+ <path class="gs" d="M152 363 L158 370 L152 377 L146 370 Z"/>
248
+ <text class="lab" x="100" y="258" text-anchor="middle">THE WORKTREES</text>
249
+ <text class="lab3" x="100" y="276" text-anchor="middle">.yggdrasil branches</text>
250
+ <rect class="hit" x="40" y="282" width="140" height="100"/>
251
+ </g>
252
+
253
+ <!-- THE EVENT BUS -->
254
+ <g id="busG" class="wgroup" data-part="messenger" tabindex="0" role="button"
255
+ aria-label="the event bus — what the messenger became">
256
+ <path class="wk" d="M602 476 L626 467 M638 464 L662 469 M674 472 L696 481"/>
257
+ <path class="gs" d="M596 471 L602 478 L596 485 L590 478 Z"/>
258
+ <path class="gs" d="M632 457 L638 464 L632 471 L626 464 Z"/>
259
+ <path class="gs" d="M668 465 L674 472 L668 479 L662 472 Z"/>
260
+ <path class="gs" d="M702 479 L708 486 L702 493 L696 486 Z"/>
261
+ <text class="lab" x="654" y="522" text-anchor="middle">THE EVENT BUS</text>
262
+ <text class="lab3" x="654" y="540" text-anchor="middle">ratatoskr · a2a 1.0</text>
263
+ <rect class="hit" x="582" y="450" width="138" height="100"/>
264
+ </g>
265
+
266
+ <!-- THE SUBSTRATE -->
267
+ <g id="subG" class="wgroup" data-part="trunk" tabindex="0" role="button"
268
+ aria-label="the substrate — what the trunk became">
269
+ <path class="gs" d="M636 712 L636 622 C636 596 648 580 664 580 C680 580 692 596 692 622 L692 712 Z"/>
270
+ <path class="st" d="M644 622 h40 M644 646 h40 M644 670 h40"/>
271
+ <text class="subRune" x="664" y="612" text-anchor="middle">ᚢ</text>
272
+ <text class="lab" x="664" y="738" text-anchor="middle">THE SUBSTRATE</text>
273
+ <text class="lab3" x="664" y="756" text-anchor="middle">one repo · one machine</text>
274
+ <rect class="hit" x="628" y="574" width="72" height="146"/>
275
+ </g>
276
+
277
+ <!-- THE HALLS BELOW -->
278
+ <g id="hallsG" class="wgroup" data-part="roots" tabindex="0" role="button"
279
+ aria-label="the halls below — what the roots became">
280
+ <path class="gs" d="M36 1000 C62 922 132 876 198 890 C242 900 264 938 270 1000 Z"/>
281
+ <path class="gs" style="fill:#0d0a07" d="M92 1000 L92 954 C92 942 108 942 108 954 L108 1000 Z"/>
282
+ <path class="gs" style="fill:#0d0a07" d="M136 1000 L136 942 C136 928 156 928 156 942 L156 1000 Z"/>
283
+ <path class="gs" style="fill:#0d0a07" d="M184 1000 L184 952 C184 940 200 940 200 952 L200 1000 Z"/>
284
+ <text class="doorRune" x="100" y="938" text-anchor="middle">ᚲ</text>
285
+ <text class="doorRune" x="146" y="924" text-anchor="middle">ᚦ</text>
286
+ <text class="doorRune" x="192" y="936" text-anchor="middle">ᛒ</text>
287
+ <text class="lab" x="152" y="1024" text-anchor="middle">THE HALLS BELOW</text>
288
+ <text class="lab3" x="152" y="1042" text-anchor="middle">svartalfaheim · tenants</text>
289
+ </g>
290
+
291
+ <!-- ======== THE TREE ======== -->
292
+
293
+ <!-- roots : what runs beneath -->
294
+ <g id="rootsG" class="bpart" data-part="roots" tabindex="0" role="button"
295
+ aria-label="the roots — they reach the halls below">
296
+ <path class="rid" d="M60 814 C160 806 260 820 380 814 C500 808 600 820 704 812"/>
297
+ <path id="ridgeHatch" class="det" d="" stroke-width="1.2"/>
298
+ <path class="limb-o" style="stroke-width:17" d="M320 792 C258 812 192 842 142 890 C112 918 94 952 86 992"/>
299
+ <path class="limb-i" style="stroke-width:10" d="M320 792 C258 812 192 842 142 890 C112 918 94 952 86 992"/>
300
+ <path class="limb-o" style="stroke-width:14" d="M352 798 C332 842 314 890 304 936 C298 964 296 986 296 1006"/>
301
+ <path class="limb-i" style="stroke-width:8" d="M352 798 C332 842 314 890 304 936 C298 964 296 986 296 1006"/>
302
+ <path class="limb-o" style="stroke-width:13" d="M410 798 C428 842 444 886 456 924 C462 944 464 960 462 972"/>
303
+ <path class="limb-i" style="stroke-width:7.5" d="M410 798 C428 842 444 886 456 924 C462 944 464 960 462 972"/>
304
+ <path class="limb-o" style="stroke-width:9" d="M420 800 C452 826 478 854 494 882 C502 896 498 908 486 908"/>
305
+ <path class="limb-i" style="stroke-width:4.5" d="M420 800 C452 826 478 854 494 882 C502 896 498 908 486 908"/>
306
+ <path class="limb-o" style="stroke-width:17" d="M446 792 C508 812 572 842 620 884 C650 910 668 938 678 966"/>
307
+ <path class="limb-i" style="stroke-width:10" d="M446 792 C508 812 572 842 620 884 C650 910 668 938 678 966"/>
308
+ <path class="gs" d="M80 985 L87 993 L80 1001 L73 993 Z"/>
309
+ <path class="gs" d="M290 999 L297 1007 L290 1015 L283 1007 Z"/>
310
+ <path class="gs" d="M672 959 L679 967 L672 975 L665 967 Z"/>
311
+ <!-- Níðhöggr, coiled at the far root -->
312
+ <path class="limb-o" style="stroke-width:10" d="M600 888 C626 896 618 916 642 928 C666 940 654 960 676 984"/>
313
+ <path class="limb-i" style="stroke-width:5" d="M600 888 C626 896 618 916 642 928 C666 940 654 960 676 984"/>
314
+ <path class="gs" d="M604 884 C598 873 582 870 575 879 L564 883 L574 890 C583 897 596 893 604 884 Z"/>
315
+ <circle class="eyeE" cx="585" cy="882" r="1.5"/>
316
+ <path class="hit" d="M300 772 L452 772 L688 952 L640 1002 L120 1002 L58 908 Z"/>
317
+ </g>
318
+
319
+ <!-- trunk : one repo, one machine -->
320
+ <g id="trunkG" class="bpart" data-part="trunk" tabindex="0" role="button"
321
+ aria-label="the trunk — the substrate everything routes through">
322
+ <path class="gs fillable" d="M342 336 C334 388 338 438 332 492 C327 544 336 596 331 648
323
+ C328 688 334 726 328 760 C326 776 318 786 304 796 L456 796 C442 786 434 776 432 760
324
+ C426 726 432 688 429 648 C424 596 433 544 428 492 C422 438 426 388 418 336
325
+ C394 326 366 326 342 336 Z"/>
326
+ <path class="det" d="M366 360 C360 470 368 570 362 690 C360 736 364 768 362 788" stroke-width="2"/>
327
+ <path class="det" d="M398 356 C404 480 394 580 400 700 C402 742 398 770 400 788" stroke-width="2"/>
328
+ <circle class="det" cx="352" cy="560" r="9" stroke-width="2"/>
329
+ <circle class="det" cx="352" cy="560" r="4" stroke-width="1.6"/>
330
+ <text class="trunkRune" x="380" y="532" text-anchor="middle" font-size="48">ᛇ</text>
331
+ <text class="trunkRune2" x="380" y="616" text-anchor="middle" font-size="26">ᚢ</text>
332
+ </g>
333
+
334
+ <!-- branches : parallel effort -->
335
+ <g id="branchesG" class="bpart" data-part="branches" tabindex="0" role="button"
336
+ aria-label="the branches — the worktrees holding parallel effort">
337
+ <path class="limb-o" style="stroke-width:15" d="M338 376 C286 360 216 356 152 390 C120 407 96 432 80 460"/>
338
+ <path class="limb-i" style="stroke-width:9" d="M338 376 C286 360 216 356 152 390 C120 407 96 432 80 460"/>
339
+ <path class="limb-o" style="stroke-width:9" d="M252 372 C236 352 224 340 212 330"/>
340
+ <path class="limb-i" style="stroke-width:4.5" d="M252 372 C236 352 224 340 212 330"/>
341
+ <path class="limb-o" style="stroke-width:8" d="M166 388 C150 374 140 364 132 352"/>
342
+ <path class="limb-i" style="stroke-width:4" d="M166 388 C150 374 140 364 132 352"/>
343
+ <path class="limb-o" style="stroke-width:15" d="M422 376 C474 358 540 352 596 380 C622 392 638 408 648 426"/>
344
+ <path class="limb-i" style="stroke-width:9" d="M422 376 C474 358 540 352 596 380 C622 392 638 408 648 426"/>
345
+ <path class="limb-o" style="stroke-width:9" d="M508 370 C524 350 536 338 548 328"/>
346
+ <path class="limb-i" style="stroke-width:4.5" d="M508 370 C524 350 536 338 548 328"/>
347
+ <path class="limb-o" style="stroke-width:8" d="M594 390 C610 376 620 366 628 354"/>
348
+ <path class="limb-i" style="stroke-width:4" d="M594 390 C610 376 620 366 628 354"/>
349
+ <path class="gs" d="M78 453 L84 461 L78 469 L72 461 Z"/>
350
+ <path class="gs" d="M126 345 L132 353 L126 361 L120 353 Z"/>
351
+ <path class="gs" d="M206 323 L212 331 L206 339 L200 331 Z"/>
352
+ <path class="gs" d="M644 421 L650 429 L644 437 L638 429 Z"/>
353
+ <path class="gs" d="M624 347 L630 355 L624 363 L618 355 Z"/>
354
+ <path class="gs" d="M544 321 L550 329 L544 337 L538 329 Z"/>
355
+ <path class="wkt" d="M150 396 C210 372 280 372 336 388"/>
356
+ <path class="wkt" d="M426 390 C484 368 544 368 596 390"/>
357
+ <path class="hit" style="fill:none;stroke-width:44" d="M338 376 C286 360 216 356 152 390 C120 407 96 432 80 460"/>
358
+ <path class="hit" style="fill:none;stroke-width:44" d="M422 376 C474 358 540 352 596 380 C622 392 638 408 648 426"/>
359
+ </g>
360
+
361
+ <!-- crown : the eagle's seat -->
362
+ <g id="crownG" class="bpart" data-part="crown" tabindex="0" role="button"
363
+ aria-label="the crown — where the eagle sits and watches every realm">
364
+ <path class="gs fillable" d="M146 308 C112 272 124 226 162 216 C146 178 170 144 208 152
365
+ C218 112 260 98 290 124 C304 76 356 64 380 98 C404 62 456 72 462 112 C500 94 538 118 534 154
366
+ C574 158 594 198 570 230 C604 248 600 290 562 304 C580 332 546 352 514 336 C502 360 458 368 440 344
367
+ C416 368 368 368 352 342 C326 360 286 352 276 324 C240 342 202 332 196 304 C170 318 148 314 146 308 Z"/>
368
+ <path class="gs" style="fill:#100c07" d="M214 322 C200 294 218 264 252 264 C256 234 296 226 316 248
369
+ C334 218 380 220 392 250 C418 228 456 242 456 272 C490 274 504 306 482 328 C490 352 464 368 440 358
370
+ C424 376 384 380 370 360 C346 374 310 368 302 346 C274 354 244 346 238 324 C226 332 212 330 214 322 Z"/>
371
+ <path class="det" d="M244 236 C264 220 288 226 290 244 C290 258 274 262 268 252" stroke-width="2"/>
372
+ <path class="det" d="M470 186 C490 170 514 176 516 194 C516 208 500 212 494 202" stroke-width="2"/>
373
+ <path class="det" d="M336 138 C354 124 376 130 378 146 C378 160 362 164 356 152" stroke-width="2"/>
374
+ <g id="leafDust"></g>
375
+ <!-- the eagle -->
376
+ <path class="gs" d="M372 96 L358 116 L378 108 Z"/>
377
+ <path class="gs" d="M368 94 C360 78 368 58 386 54 C402 50 414 58 416 72 C418 84 410 94 400 98 C390 104 374 102 368 94 Z"/>
378
+ <path class="det" d="M376 86 C388 78 402 80 408 90 M374 96 C386 90 400 92 406 100 M378 106 C388 102 396 104 400 110" stroke-width="2"/>
379
+ <path class="gs" d="M408 60 C410 50 422 46 428 52 L437 56 L429 62 C424 68 414 68 410 64 Z"/>
380
+ <circle class="eyeE" cx="419" cy="57" r="1.6"/>
381
+ <path class="det" d="M384 102 l-2 7 M397 102 l1 7" stroke-width="2.2"/>
382
+ <path class="gs" style="fill:none;stroke-width:3" d="M356 112 C374 120 402 120 418 111"/>
383
+ <ellipse class="hit" cx="380" cy="215" rx="250" ry="155"/>
384
+ </g>
385
+
386
+ <!-- THE WELL — Mimirsbrunn, at the root of the tree -->
387
+ <g id="wellG" class="wgroup" data-part="well" tabindex="0" role="button"
388
+ aria-label="the well — Mimirsbrunn, the memory engine at the root">
389
+ <circle class="gs" cx="505" cy="945" r="44"/>
390
+ <circle class="wt" cx="505" cy="945" r="31"/>
391
+ <circle class="gs" style="fill:#080706;stroke:#241d14" cx="505" cy="945" r="18"/>
392
+ <path class="wm" d="M492 945 h9 M506 938 h8 M502 952 h7"/>
393
+ <circle class="rip" cx="505" cy="945" r="10"/>
394
+ <circle class="rip r2" cx="505" cy="945" r="10"/>
395
+ <text class="lab" x="505" y="1020" text-anchor="middle">THE WELL</text>
396
+ <text class="lab3" x="505" y="1038" text-anchor="middle">mimirsbrunn · engram</text>
397
+ <circle class="hit" cx="505" cy="945" r="50"/>
398
+ </g>
399
+
400
+ <!-- ======== THE MESSENGER — Ratatoskr, forever running ======== -->
401
+ <g id="messG" class="bpart" data-part="messenger" tabindex="0" role="button"
402
+ aria-label="the messenger — Ratatoskr, who runs the length of the tree">
403
+ <path id="sqTrack" class="track"
404
+ d="M300 700 C350 672 410 672 460 648 C410 624 350 624 300 600 C350 576 410 576 460 552
405
+ C410 528 350 528 300 504 C350 480 410 480 448 452"/>
406
+ <path class="hit" style="fill:none;stroke-width:26"
407
+ d="M300 700 C350 672 410 672 460 648 C410 624 350 624 300 600 C350 576 410 576 460 552
408
+ C410 528 350 528 300 504 C350 480 410 480 448 452"/>
409
+ <g id="squirrel" pointer-events="none">
410
+ <path class="gs" d="M-4 2 C-22 6 -34 -6 -28 -20 C-23 -31 -10 -32 -6 -23 C-3 -16 -7 -4 -4 2 Z"/>
411
+ <ellipse class="gs" cx="2" cy="2" rx="11" ry="8"/>
412
+ <circle class="gs" cx="14" cy="-2" r="6"/>
413
+ <path class="gs" d="M11 -7 L8 -14 L16 -9 Z"/>
414
+ <circle cx="16" cy="-3" r="1.3" fill="#7d5f2a"/>
415
+ <path d="M-2 9 l-2 5 M8 9 l1 5" stroke="#4a3d2a" stroke-width="2" stroke-linecap="round" fill="none"/>
416
+ <path class="chip" d="M24 -4 L28 0 L24 4 L20 0 Z"/>
417
+ </g>
418
+ </g>
419
+
420
+ <!-- connectors : part of the tree → feature of the machine -->
421
+ <g id="links">
422
+ <path class="lnk" data-part="crown" d="M592 248 C606 218 614 186 622 158"/>
423
+ <path class="lnk" data-part="branches" d="M204 380 C186 364 170 350 158 336"/>
424
+ <path class="lnk" data-part="trunk" d="M436 648 C502 656 566 658 630 654"/>
425
+ <path class="lnk" data-part="roots" d="M300 806 C262 828 234 858 206 888"/>
426
+ <path class="lnk" data-part="well" d="M448 806 C474 834 488 866 496 900"/>
427
+ <path class="lnk" data-part="messenger" d="M462 550 C512 546 550 522 588 500"/>
428
+ </g>
429
+ </svg>
430
+ </div>
431
+ </section>
432
+
433
+ <!-- ============ caption + the plinth ============ -->
434
+ <section class="plate">
435
+ <p class="cap">hover or touch the carving —
436
+ <button data-p="crown">crown</button><i>·</i>
437
+ <button data-p="branches">branches</button><i>·</i>
438
+ <button data-p="trunk">trunk</button><i>·</i>
439
+ <button data-p="roots">roots</button><i>·</i>
440
+ <button data-p="well">well</button><i>·</i>
441
+ <button data-p="messenger">messenger</button>
442
+ </p>
443
+
444
+ <div class="plinth carved" id="plinth" aria-live="polite">
445
+ <div class="pl-head">
446
+ <span id="pRune" class="rn">ᛇ</span>
447
+ <div>
448
+ <p class="pl-k">The carving speaks</p>
449
+ <p id="pName">the world tree</p>
450
+ </div>
451
+ </div>
452
+ <div class="prow">
453
+ <span class="ptag">The myth</span>
454
+ <p class="pmyth" id="pMyth">Run your hand along the carving — nine worlds hang in one ash, connected, holding each other up.</p>
455
+ </div>
456
+ <div class="prow">
457
+ <span class="ptag">In Ymir</span>
458
+ <p class="pymir" id="pYmir">The same tree holds the work: branches for parallel effort, one trunk for every realm, memory at the root.</p>
459
+ </div>
460
+ </div>
461
+
462
+ <div class="pl-actions">
463
+ <button class="btn-carv" id="tourBtn"><span class="rn">ᚱ</span><span id="tourLabel">climb the tree</span></button>
464
+ </div>
465
+ </section>
466
+ </main>
467
+
468
+ <footer>
469
+ <div class="frule"><span>ᚠ ᚢ ᚦ ᚨ ᚱ ᚲ ᚷ ᚹ ᚺ ᚾ ᛁ ᛇ ᛈ ᛉ ᛊ ᛏ ᛒ ᛖ ᛗ ᛚ ᛜ ᛞ ᛟ</span></div>
470
+ <p class="f-big">The worlds are not separate — they hang in one tree.</p>
471
+ <p class="f-mono">one tree · one trunk · one well · one messenger</p>
472
+ <p class="f-repo">zerwiz/ymir · docs/design.md · the lore v2 · §III</p>
473
+ </footer>
474
+
475
+ <script>
476
+ (function(){
477
+ 'use strict';
478
+ const $ = s => document.querySelector(s);
479
+ const $$ = s => Array.from(document.querySelectorAll(s));
480
+ const NS = 'http://www.w3.org/2000/svg';
481
+ const RM = matchMedia('(prefers-reduced-motion: reduce)').matches;
482
+
483
+ /* ---------- generated texture: leaf dust + earth hatch ---------- */
484
+ (function(){
485
+ let dd = '', db = '';
486
+ for(let i = 0; i < 26; i++){
487
+ const x = (175 + Math.random()*415).toFixed(1), y = (135 + Math.random()*195).toFixed(1);
488
+ const s = (1.6 + Math.random()*2.6).toFixed(1);
489
+ const m = `M${x} ${y}l${s} -${s}l${s} ${s}l-${s} ${s}z`;
490
+ if(i % 3 === 0) db += m; else dd += m;
491
+ }
492
+ [['#2b2214', dd], ['#3f3219', db]].forEach(([f, d]) => {
493
+ const p = document.createElementNS(NS, 'path');
494
+ p.setAttribute('d', d); p.setAttribute('fill', f);
495
+ $('#leafDust').appendChild(p);
496
+ });
497
+ let rh = '';
498
+ for(let x = 70; x <= 690; x += 24) rh += `M${x} 818 l-8 9 `;
499
+ $('#ridgeHatch').setAttribute('d', rh);
500
+ })();
501
+
502
+ /* ---------- the carving speaks ---------- */
503
+ const PARTS = {
504
+ crown:{ rune:'ᛉ', name:'the crown',
505
+ myth:'At the crown of the tree sits the eagle — his eye takes in every realm, every branch, every root.',
506
+ ymir:'the high seat — Hlidskjalf, the control plane: the view of every realm, the sovereign\u2019s eye over the whole fleet.'},
507
+ branches:{ rune:'ᛇ', name:'the branches',
508
+ myth:'The branches spread wide, each holding its own weather — parallel worlds grown from one wood, never colliding.',
509
+ ymir:'the worktrees — .yggdrasil branches hold parallel effort without collision. The tree is the worktree manager.'},
510
+ trunk:{ rune:'ᚢ', name:'the trunk',
511
+ myth:'One trunk carries the whole tree — every branch, every realm, draws its life through the same wood.',
512
+ ymir:'the substrate — one repo, one machine; every realm routes through the same trunk. The trunk is the giant himself.'},
513
+ roots:{ rune:'ᛜ', name:'the roots',
514
+ myth:'The roots run deep into the earth — down into the dwarf-halls, where the smiths work beneath the world.',
515
+ ymir:'Svartalfaheim — the tenant realms: isolated dwarf-halls, the shop floors. Nothing bleeds between halls.'},
516
+ well:{ rune:'ᛚ', name:'the well',
517
+ myth:'At the root lies the well — Mimir guards it; whoever drinks receives the memory of all things. Even Odin paid an eye.',
518
+ ymir:'Mimirsbrunn — the engram memory engine: every action observed, every plan recalls what the well already knows.'},
519
+ messenger:{ rune:'ᚱ', name:'the messenger',
520
+ myth:'Ratatoskr runs the length of the tree, carrying words between the eagle at the crown and the dragon at the roots.',
521
+ ymir:'the A2A 1.0 backbone — the event bus: every word between gates is announced, tracked, observed, and carved into Runes.'}
522
+ };
523
+
524
+ const plinth = $('#plinth');
525
+ let swapT = null, pinned = null, touring = false;
526
+ let sqBoost = 0; /* the squirrel is watching the plinth too */
527
+
528
+ function light(p){
529
+ $$('g[data-part], .lnk').forEach(el =>
530
+ el.classList.toggle('lit', !!p && el.dataset.part === p));
531
+ $('#scene').classList.toggle('focus', !!p);
532
+ $$('.cap button').forEach(b => b.classList.toggle('on', !!p && b.dataset.p === p));
533
+ if(p === 'messenger') sqBoost = 2.6;
534
+ plinth.classList.add('swap');
535
+ clearTimeout(swapT);
536
+ swapT = setTimeout(() => {
537
+ $('#pRune').textContent = p ? PARTS[p].rune : 'ᛇ';
538
+ $('#pName').textContent = p ? PARTS[p].name : 'the world tree';
539
+ $('#pMyth').textContent = p ? PARTS[p].myth :
540
+ 'Run your hand along the carving — nine worlds hang in one ash, connected, holding each other up.';
541
+ $('#pYmir').textContent = p ? PARTS[p].ymir :
542
+ 'The same tree holds the work: branches for parallel effort, one trunk for every realm, memory at the root.';
543
+ plinth.classList.remove('swap');
544
+ }, 170);
545
+ }
546
+
547
+ function hook(el, p){
548
+ el.addEventListener('mouseenter', () => { if(!touring) light(p); });
549
+ el.addEventListener('mouseleave', () => { if(!touring) light(pinned); });
550
+ const act = () => { pinned = (pinned === p) ? null : p; light(pinned); };
551
+ el.addEventListener('click', act);
552
+ el.addEventListener('keydown', e => {
553
+ if(e.key === 'Enter' || e.key === ' '){ e.preventDefault(); act(); }
554
+ });
555
+ el.addEventListener('focus', () => { if(!touring) light(p); });
556
+ el.addEventListener('blur', () => { if(!touring) light(pinned); });
557
+ }
558
+ $$('#scene g[data-part]').forEach(g => hook(g, g.dataset.part));
559
+ $$('.cap button').forEach(b => hook(b, b.dataset.p));
560
+
561
+ /* ---------- Ratatoskr runs the tree — pause, turn, run again ---------- */
562
+ (function(){
563
+ const track = $('#sqTrack'), sq = $('#squirrel');
564
+ const L = track.getTotalLength();
565
+ if(RM){
566
+ const pt = track.getPointAtLength(L * 0.5);
567
+ sq.setAttribute('transform', `translate(${pt.x},${pt.y})`);
568
+ return;
569
+ }
570
+ let dist = 0, dir = 1, pause = 0;
571
+ (function tick(){
572
+ if(pause > 0){ pause--; }
573
+ else{
574
+ dist += dir * (2.5 + sqBoost);
575
+ sqBoost *= 0.992;
576
+ if(dist >= L){ dist = L; dir = -1; pause = 42; }
577
+ else if(dist <= 0){ dist = 0; dir = 1; pause = 42; }
578
+ }
579
+ const pt = track.getPointAtLength(dist);
580
+ sq.setAttribute('transform', `translate(${pt.x.toFixed(1)},${pt.y.toFixed(1)}) scale(${dir},1)`);
581
+ requestAnimationFrame(tick);
582
+ })();
583
+ })();
584
+
585
+ /* ---------- the climb: crown to root ---------- */
586
+ $('#tourBtn').addEventListener('click', () => {
587
+ if(touring) return;
588
+ touring = true; pinned = null;
589
+ const btn = $('#tourBtn');
590
+ btn.classList.add('busy');
591
+ $('#tourLabel').textContent = 'crown to root';
592
+ const order = ['crown','branches','trunk','roots','well','messenger'];
593
+ let i = 0;
594
+ (function step(){
595
+ if(i < order.length){ light(order[i++]); setTimeout(step, 2150); }
596
+ else{
597
+ light(null); touring = false;
598
+ btn.classList.remove('busy');
599
+ $('#tourLabel').textContent = 'climb the tree';
600
+ }
601
+ })();
602
+ });
603
+
604
+ /* ---------- ambience: cold mist meets embers ---------- */
605
+ (function(){
606
+ const cv = $('#gap'), ctx = cv.getContext('2d');
607
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
608
+ let W = 0, H = 0, mist = [], embers = [], t = 0;
609
+ function size(){
610
+ W = innerWidth; H = innerHeight;
611
+ cv.width = W*dpr; cv.height = H*dpr;
612
+ ctx.setTransform(dpr,0,0,dpr,0,0);
613
+ }
614
+ function init(){
615
+ mist = []; embers = [];
616
+ for(let i=0;i<6;i++) mist.push({ x:Math.random()*W, y:H*.2+Math.random()*H*.7,
617
+ r:160+Math.random()*220, v:(Math.random()-.5)*.15, cold:Math.random()<.65 });
618
+ for(let i=0;i<12;i++) embers.push({ x:Math.random()*W, y:Math.random()*H,
619
+ r:.6+Math.random()*1.1, v:.1+Math.random*.25, ph:Math.random()*6.28, sp:.5+Math.random() });
620
+ }
621
+ function frame(){
622
+ t += .016; ctx.clearRect(0,0,W,H);
623
+ for(const m of mist){
624
+ m.x += m.v;
625
+ if(m.x < -m.r) m.x = W+m.r;
626
+ if(m.x > W+m.r) m.x = -m.r;
627
+ const a = m.cold ? .045 : .028;
628
+ const g = ctx.createRadialGradient(m.x,m.y,0,m.x,m.y,m.r);
629
+ g.addColorStop(0, m.cold?`rgba(96,110,128,${a})`:`rgba(214,138,64,${a})`);
630
+ g.addColorStop(1,'rgba(0,0,0,0)');
631
+ ctx.fillStyle = g;
632
+ ctx.beginPath(); ctx.arc(m.x,m.y,m.r,0,6.283); ctx.fill();
633
+ }
634
+ for(const e of embers){
635
+ e.y -= e.v; e.x += Math.sin(t*e.sp+e.ph)*.15;
636
+ if(e.y < -6){ e.y = H+6; e.x = Math.random()*W; }
637
+ const a = .12 + .3*(.5+.5*Math.sin(t*e.sp*2+e.ph));
638
+ ctx.fillStyle = `rgba(236,166,84,${a.toFixed(3)})`;
639
+ ctx.beginPath(); ctx.arc(e.x,e.y,e.r,0,6.283); ctx.fill();
640
+ }
641
+ }
642
+ addEventListener('resize', () => { size(); init(); if(RM) frame(); });
643
+ size(); init();
644
+ if(RM) frame(); else (function loop(){ frame(); requestAnimationFrame(loop); })();
645
+ })();
646
+ })();
647
+ </script>
648
+ </body>
649
+ </html>
Binary file