@zerwiz/ymir 0.1.15 → 0.1.17

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 (169) hide show
  1. package/.claude/settings.json +31 -0
  2. package/.codex/hooks.json +43 -0
  3. package/.cursor/hooks.json +33 -0
  4. package/.pi/extensions/README.md +39 -0
  5. package/.pi/extensions/gna-pi-watch.ts +15 -0
  6. package/.pi/extensions/lib/ro-assistant-layout.ts +96 -0
  7. package/.pi/extensions/lib/ro-operational-user-layout.ts +145 -0
  8. package/.pi/extensions/lib/ro-visibility.ts +99 -0
  9. package/.pi/extensions/lib/ro-working-ship.ts +243 -0
  10. package/.pi/extensions/lib/rodd-operational-input.ts +62 -0
  11. package/.pi/extensions/lib/skuld-branch-dispatch.ts +252 -0
  12. package/.pi/extensions/lib/skuld-branch-model-picker.ts +77 -0
  13. package/.pi/extensions/lib/vordr-sessionstart-supervisor.mjs +48 -0
  14. package/.pi/extensions/ro.ts +11 -0
  15. package/.pi/extensions/skuld-branch-supervision.ts +11 -0
  16. package/.pi/extensions/syn-turnend-guard.ts +11 -0
  17. package/.pi/mcp.json +16 -0
  18. package/.pi/mcp.json.example +11 -0
  19. package/.pi/npm/.gitignore +2 -0
  20. package/.pi/settings.json +8 -0
  21. package/.pi/shared/extensions/gna-pi-watch.ts +750 -0
  22. package/.pi/shared/extensions/herdr-agent-state.ts +257 -0
  23. package/.pi/shared/extensions/open-editor.ts +509 -0
  24. package/.pi/shared/extensions/ro.ts +504 -0
  25. package/.pi/shared/extensions/skuld-branch-supervision.ts +1535 -0
  26. package/.pi/shared/extensions/syn-turnend-guard.ts +683 -0
  27. package/.pi/shared/extensions/todo.ts +133 -0
  28. package/.pi/shared/extensions/ymir-subagents.ts +287 -0
  29. package/.pi/shared/extensions/ymir-well.ts +130 -0
  30. package/CHANGELOG.md +8 -0
  31. package/assets/Ymir.md +4654 -0
  32. package/assets/healer.html +717 -0
  33. package/assets/icon-family/family-sheet.png +0 -0
  34. package/assets/icon-family/hlidskjalf.svg +1 -0
  35. package/assets/icon-family/sessrumnir.svg +1 -0
  36. package/assets/icon-family/smidja.svg +1 -0
  37. package/assets/icon-family/ymir.svg +1 -0
  38. package/assets/mimirswell.html +880 -0
  39. package/assets/mock/README.md +16 -0
  40. package/assets/mock/feeds.ts +175 -0
  41. package/assets/mock/mock.ts +408 -0
  42. package/assets/reference/docs/agent-control.md +122 -0
  43. package/assets/reference/docs/apodex/Appdex_as_Orchestrator_Planner_Reviewer_Assessment.md +176 -0
  44. package/assets/reference/docs/apodex/Final Report(1).md +198 -0
  45. package/assets/reference/docs/apodex/Final Report(2).md +54 -0
  46. package/assets/reference/docs/apodex/Final Report(3).md +40 -0
  47. package/assets/reference/docs/apodex/Final Report(4).md +234 -0
  48. package/assets/reference/docs/apodex/Final Report(5).md +13 -0
  49. package/assets/reference/docs/apodex/Final Report.md +82 -0
  50. package/assets/reference/docs/architecture.md +392 -0
  51. package/assets/reference/docs/arm-pretool-check.md +247 -0
  52. package/assets/reference/docs/calm-mode-feasibility.md +541 -0
  53. package/assets/reference/docs/calm.md +59 -0
  54. package/assets/reference/docs/captain-hold-lifecycle.md +96 -0
  55. package/assets/reference/docs/cd-guard.md +165 -0
  56. package/assets/reference/docs/cmux-backend.md +133 -0
  57. package/assets/reference/docs/codex-app-backend.md +57 -0
  58. package/assets/reference/docs/configuration.md +924 -0
  59. package/assets/reference/docs/documentation-audiences.json +468 -0
  60. package/assets/reference/docs/documentation-audiences.md +28 -0
  61. package/assets/reference/docs/examples/crew-dispatch.json +26 -0
  62. package/assets/reference/docs/examples/process-event-extension/file-signal.mjs +96 -0
  63. package/assets/reference/docs/examples/process-event-extension/firstmate-extension.json +15 -0
  64. package/assets/reference/docs/examples/watched-tools.json +24 -0
  65. package/assets/reference/docs/examples/wedge-alarm +22 -0
  66. package/assets/reference/docs/extension-bindings.md +237 -0
  67. package/assets/reference/docs/fm-test-isolation-proof.json +37 -0
  68. package/assets/reference/docs/fm-test-isolation-proof.md +149 -0
  69. package/assets/reference/docs/fm-test-portable-shards.md +118 -0
  70. package/assets/reference/docs/gitlab-merge-watch.md +275 -0
  71. package/assets/reference/docs/herdr-backend.md +346 -0
  72. package/assets/reference/docs/orca-backend.md +84 -0
  73. package/assets/reference/docs/pi-supervision-branch-poster.svg +125 -0
  74. package/assets/reference/docs/pi-supervision-branch.md +96 -0
  75. package/assets/reference/docs/plans/agentic-engineering-workflow.md +395 -0
  76. package/assets/reference/docs/plans/agentic-factory-implementation-plan.md +615 -0
  77. package/assets/reference/docs/plans/command-factory-enhancement-plan.md +420 -0
  78. package/assets/reference/docs/plans/phase-1-layer3-program-design.md +134 -0
  79. package/assets/reference/docs/plans/phase-2-vertical-slices.md +100 -0
  80. package/assets/reference/docs/plans/phase-3-measurable-goals.md +106 -0
  81. package/assets/reference/docs/plans/phase-4-context-budget.md +106 -0
  82. package/assets/reference/docs/plans/phase-5-pre-mortem-confidence.md +100 -0
  83. package/assets/reference/docs/plans/phase-6-incident-to-agent.md +109 -0
  84. package/assets/reference/docs/remote-secondmates.md +268 -0
  85. package/assets/reference/docs/scripts.md +146 -0
  86. package/assets/reference/docs/sessionstart-nudge.md +115 -0
  87. package/assets/reference/docs/subagent-guard.md +383 -0
  88. package/assets/reference/docs/supervision-protocols/claude.md +27 -0
  89. package/assets/reference/docs/supervision-protocols/codex.md +16 -0
  90. package/assets/reference/docs/supervision-protocols/cursor.md +31 -0
  91. package/assets/reference/docs/supervision-protocols/grok.md +39 -0
  92. package/assets/reference/docs/supervision-protocols/opencode.md +17 -0
  93. package/assets/reference/docs/supervision-protocols/pi.md +33 -0
  94. package/assets/reference/docs/supervision-protocols/unknown.md +13 -0
  95. package/assets/reference/docs/tmux-backend.md +115 -0
  96. package/assets/reference/docs/trace-context.md +117 -0
  97. package/assets/reference/docs/turnend-guard.md +170 -0
  98. package/assets/reference/docs/verification/dispatch-auth.md +204 -0
  99. package/assets/reference/docs/verification/muse.md +221 -0
  100. package/assets/reference/docs/verification/process-event-sources.md +208 -0
  101. package/assets/reference/docs/verification/public-followup.md +119 -0
  102. package/assets/reference/docs/verification/runtime-backends.md +1022 -0
  103. package/assets/reference/docs/verification/stow-memory.md +53 -0
  104. package/assets/reference/docs/verification/supervision.md +534 -0
  105. package/assets/reference/docs/verification/trace-context.md +31 -0
  106. package/assets/reference/docs/voice-relay.md +295 -0
  107. package/assets/reference/docs/watcher-continuity.md +119 -0
  108. package/assets/reference/docs/wedge-alarm.md +39 -0
  109. package/assets/reference/docs/zellij-backend.md +114 -0
  110. package/assets/reference/index.html +1172 -0
  111. package/assets/reference/skills/afk/SKILL.md +241 -0
  112. package/assets/reference/skills/ahoy/SKILL.md +56 -0
  113. package/assets/reference/skills/ask-user-authority/SKILL.md +56 -0
  114. package/assets/reference/skills/bearings/SKILL.md +161 -0
  115. package/assets/reference/skills/bearings/assets/board-template.html +731 -0
  116. package/assets/reference/skills/bootstrap-diagnostics/SKILL.md +68 -0
  117. package/assets/reference/skills/captain-hold-lifecycle/SKILL.md +54 -0
  118. package/assets/reference/skills/decision-hold-lifecycle/SKILL.md +15 -0
  119. package/assets/reference/skills/diagnostic-reasoning/SKILL.md +53 -0
  120. package/assets/reference/skills/firstmate-codexapp/SKILL.md +110 -0
  121. package/assets/reference/skills/firstmate-codexapp/agents/openai.yaml +4 -0
  122. package/assets/reference/skills/firstmate-coding-guidelines/SKILL.md +127 -0
  123. package/assets/reference/skills/firstmate-orca/SKILL.md +92 -0
  124. package/assets/reference/skills/fmx-respond/SKILL.md +283 -0
  125. package/assets/reference/skills/harness-adapters/SKILL.md +95 -0
  126. package/assets/reference/skills/harness-adapters/references/common/control-and-recovery.md +37 -0
  127. package/assets/reference/skills/harness-adapters/references/common/dispatch.md +32 -0
  128. package/assets/reference/skills/harness-adapters/references/common/model-and-effort.md +42 -0
  129. package/assets/reference/skills/harness-adapters/references/common/primary-hooks.md +40 -0
  130. package/assets/reference/skills/harness-adapters/references/harness/claude.md +55 -0
  131. package/assets/reference/skills/harness-adapters/references/harness/codex.md +43 -0
  132. package/assets/reference/skills/harness-adapters/references/harness/cursor.md +75 -0
  133. package/assets/reference/skills/harness-adapters/references/harness/grok.md +69 -0
  134. package/assets/reference/skills/harness-adapters/references/harness/kimi.md +51 -0
  135. package/assets/reference/skills/harness-adapters/references/harness/muse.md +70 -0
  136. package/assets/reference/skills/harness-adapters/references/harness/opencode.md +42 -0
  137. package/assets/reference/skills/harness-adapters/references/harness/pi.md +56 -0
  138. package/assets/reference/skills/process-event-sources/SKILL.md +137 -0
  139. package/assets/reference/skills/project-management/SKILL.md +92 -0
  140. package/assets/reference/skills/quota-array-dispatch/SKILL.md +131 -0
  141. package/assets/reference/skills/secondmate-provisioning/SKILL.md +254 -0
  142. package/assets/reference/skills/stow/SKILL.md +309 -0
  143. package/assets/reference/skills/stuck-crewmate-recovery/SKILL.md +54 -0
  144. package/assets/reference/skills/updatefirstmate/SKILL.md +64 -0
  145. package/assets/skills/README.md +5 -0
  146. package/assets/yggdrasil.html +649 -0
  147. package/assets/ymir-banner-01.png +0 -0
  148. package/assets/ymir-banner-02.png +0 -0
  149. package/assets/ymir-banner-03.png +0 -0
  150. package/assets/ymir-banner-04.png +0 -0
  151. package/assets/ymir-banner-05.png +0 -0
  152. package/assets/ymir-banner-06.png +0 -0
  153. package/assets/ymir-emblem-darksteel.svg +63 -0
  154. package/assets/ymir-emblem-forged.svg +55 -0
  155. package/assets/ymir-emblem-slate.svg +55 -0
  156. package/assets/ymir-mark-algiz-anvil.svg +9 -0
  157. package/assets/ymir-stave.svg +53 -0
  158. package/assets/ymir.html +563 -0
  159. package/data/learnings.md +33 -0
  160. package/data/local-models.md +34 -0
  161. package/data/operator.md +15 -0
  162. package/data/projects.md +28 -0
  163. package/data/realm.md +1 -0
  164. package/deploy/Containerfile +27 -0
  165. package/deploy/README.md +104 -0
  166. package/deploy/compose/compose.yaml +45 -0
  167. package/deploy/env.example +30 -0
  168. package/deploy/quadlet/ymir.container +57 -0
  169. package/package.json +11 -3
@@ -0,0 +1,880 @@
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>Ymir — The Well</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:ital,wght@0,500;600;1,500&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+Runic&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
+ .rn{font-family:var(--run)}
28
+
29
+ /* stone grain + vignette */
30
+ body::before{content:'';position:fixed;inset:0;z-index:60;pointer-events:none;opacity:.055;
31
+ 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")}
32
+ body::after{content:'';position:fixed;inset:0;z-index:59;pointer-events:none;
33
+ box-shadow:inset 0 0 170px 30px rgba(0,0,0,.5)}
34
+ #gap{position:fixed;inset:0;width:100%;height:100%;z-index:0}
35
+ main,footer{position:relative;z-index:2}
36
+
37
+ /* ============ topbar ============ */
38
+ .topbar{position:fixed;inset:0 0 auto 0;height:46px;display:flex;justify-content:space-between;align-items:center;
39
+ padding:0 clamp(1rem,3vw,2rem);z-index:50;background:rgba(13,11,8,.78);backdrop-filter:blur(10px);
40
+ border-bottom:1px solid var(--line-soft);font-family:var(--mono);font-size:.58rem;
41
+ letter-spacing:.3em;color:var(--bone-dim);text-transform:uppercase}
42
+ .topbar .rn{color:var(--bronze);margin-right:.6em;font-size:.85rem}
43
+ main{padding-top:46px}
44
+ @keyframes fadeUp{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
45
+
46
+ /* ============ lore head ============ */
47
+ .lore-head{padding:5.5rem 1.5rem 0;text-align:center}
48
+ .lh-k{font-family:var(--mono);font-size:.58rem;letter-spacing:.44em;text-indent:.44em;color:var(--steel-dim);
49
+ text-transform:uppercase;animation:fadeUp 1s .05s both}
50
+ .lore-head h1{font-family:var(--disp);font-weight:600;font-size:clamp(1.9rem,5.4vw,3.4rem);
51
+ letter-spacing:.16em;text-indent:.16em;color:var(--bone);margin-top:1.3rem;
52
+ text-shadow:0 -1px 0 rgba(0,0,0,.85);animation:fadeUp 1s .18s both}
53
+ .lh-motto{font-family:var(--mono);font-size:.6rem;letter-spacing:.34em;text-indent:.34em;color:var(--bronze);
54
+ text-transform:uppercase;margin-top:1.2rem;animation:fadeUp 1s .32s both}
55
+ .lh-rule{width:150px;height:1px;background:var(--bronze-deep);margin:2.1rem auto 2.3rem;position:relative;
56
+ animation:fadeUp 1s .42s both}
57
+ .lh-rule::after{content:'';position:absolute;left:50%;top:50%;width:5px;height:5px;background:var(--bronze-deep);
58
+ transform:translate(-50%,-50%) rotate(45deg)}
59
+ .lh-body{max-width:62ch;margin:0 auto;font-size:1.04rem;line-height:1.95;color:var(--bone-dim);
60
+ animation:fadeUp 1s .52s both}
61
+ .lh-body+.lh-body{margin-top:1.1rem;animation-delay:.62s}
62
+ .lh-close{font-family:var(--disp);font-style:italic;font-size:clamp(1.05rem,2.2vw,1.3rem);letter-spacing:.06em;
63
+ color:var(--bone);margin:1.8rem auto 0;max-width:52ch;line-height:1.7;animation:fadeUp 1s .72s both}
64
+
65
+ /* ============ stage ============ */
66
+ .stage{position:relative;display:flex;align-items:center;justify-content:center;
67
+ padding:3.6rem 1rem 1.6rem;animation:fadeUp 1.1s .85s both}
68
+ .scenewrap{width:min(94vw, calc(76svh * .691), 600px);aspect-ratio:760/1100}
69
+ #scene{width:100%;height:100%;display:block}
70
+ .anno{position:absolute;top:110px;max-width:250px;line-height:1.95;font-size:.92rem;z-index:3}
71
+ .anno-l{left:clamp(1rem,4vw,3.2rem);font-style:italic;color:var(--bone-faint)}
72
+ .anno-r{right:clamp(1rem,4vw,3.2rem);text-align:right;font-family:var(--mono);font-size:.56rem;
73
+ letter-spacing:.26em;color:var(--steel-dim);text-transform:uppercase;line-height:2.6}
74
+ @media(max-width:900px){.anno{display:none}}
75
+
76
+ /* ============ SVG — the underground carving ============ */
77
+ #scene text{font-family:var(--mono);user-select:none}
78
+ #scene g[data-part]{cursor:pointer;outline:none}
79
+ .bpart{transition:opacity .45s}
80
+ #scene.focus g.bpart:not(.lit){opacity:.55}
81
+ .bpart .gs{stroke:#4a3d2a;fill:#161009;stroke-width:2;transition:stroke .4s,fill .4s}
82
+ .bpart .limb-o{fill:none;stroke:#4a3d2a;stroke-linecap:round;transition:stroke .4s}
83
+ .bpart .limb-i{fill:none;stroke:#0e0c09;stroke-linecap:round}
84
+ .bpart .det{fill:none;stroke:#2e261a;stroke-linecap:round;transition:stroke .4s}
85
+ .bpart.lit .gs{stroke:var(--bronze)}
86
+ .bpart.lit .fillable{fill:#241b0e}
87
+ .bpart.lit .limb-o{stroke:var(--bronze)}
88
+ .bpart.lit .det{stroke:var(--bronze)}
89
+ .lab{font-size:18px;letter-spacing:3px;fill:#6f6650;opacity:.92;paint-order:stroke;
90
+ stroke:#0e0c09;stroke-width:5;stroke-linejoin:round;transition:fill .4s}
91
+ .lab3{font-size:10px;letter-spacing:3px;fill:#5a5240;transition:fill .4s}
92
+ .bpart.lit .lab{fill:var(--bone)}
93
+ .bpart.lit .lab3{fill:var(--bone-dim)}
94
+ .hit{fill:#000;fill-opacity:0;stroke:#000;stroke-opacity:0;pointer-events:all}
95
+
96
+ /* the well structure (non-interactive) */
97
+ .joints{fill:none;stroke:#2e261a;stroke-width:1.5}
98
+ .jv{fill:none;stroke:#241d14;stroke-width:1.4}
99
+ .caprune{font-family:var(--run);font-size:12px;fill:#3a3122}
100
+
101
+ /* water */
102
+ .wv{fill:none;stroke:#4a3d2a;stroke-width:2.5;transition:stroke .4s}
103
+ .wv2{fill:none;stroke:#3b3222;stroke-width:1.6;opacity:.35;animation:wp 5.5s ease-in-out infinite;transition:stroke .4s}
104
+ .wv3{fill:none;stroke:#3b3222;stroke-width:1.2;opacity:.25;transition:stroke .4s}
105
+ @keyframes wp{50%{opacity:.6}}
106
+ #waterG.lit .wv{stroke:var(--bronze)}
107
+ #waterG.lit .wv2{stroke:#8a6a34}
108
+ #waterG.lit .wv3{stroke:#6d5426}
109
+ #waterG.lit .wbody{fill:#0c0907}
110
+ .mem{font-size:11px;letter-spacing:.5px;fill:#9fb3bd;opacity:0;transition:opacity .6s}
111
+ .mem.show{opacity:.95}
112
+
113
+ /* the eye — closed until it is looked for */
114
+ #eyeG .ec{transition:opacity .3s}
115
+ #eyeG .eo{stroke:var(--bronze);opacity:0;transition:opacity .45s}
116
+ #eyeG .arc{fill:none;stroke-width:2.5;stroke-dasharray:54;stroke-dashoffset:54;
117
+ transition:stroke-dashoffset .9s ease,opacity .3s}
118
+ #eyeG .iris{fill:#120e08;stroke-width:2}
119
+ #eyeG.lit .eo,#eyeG.peek .eo{opacity:1}
120
+ #eyeG.lit .arc,#eyeG.peek .arc{stroke-dashoffset:0}
121
+ #eyeG.lit .ec,#eyeG.peek .ec{opacity:0}
122
+ #eyeG .eg{opacity:0;transition:opacity .7s}
123
+ #eyeG.lit .eg,#eyeG.peek .eg{opacity:1}
124
+
125
+ /* the root — observe flows down it when lit */
126
+ .rflow{fill:none;stroke:var(--bronze);stroke-width:2.5;stroke-dasharray:5 13;opacity:0;transition:opacity .4s}
127
+ #rootG.lit .rflow{opacity:.8;animation:march 1s linear infinite}
128
+ @keyframes march{to{stroke-dashoffset:-36}}
129
+
130
+ /* the keeper's glint and key */
131
+ #guardG .keppe{fill:#6b5b3a;transition:fill .4s}
132
+ #guardG .kkey path,#guardG .kkey circle{stroke:#6b5b3a;transition:stroke .4s}
133
+ #guardG.lit .keppe{fill:var(--bronze)}
134
+ #guardG.lit .kkey path,#guardG.lit .kkey circle{stroke:var(--bronze)}
135
+
136
+ /* the runestone ledger */
137
+ #ledgerG .stn{stroke:#4a4232;fill:#14100b;transition:stroke .4s}
138
+ #ledgerG .stn2{fill:none;stroke:#2e261a;stroke-width:1.5;transition:stroke .4s}
139
+ #ledgerG.lit .stn{stroke:var(--bronze)}
140
+ #ledgerG.lit .stn2{stroke:var(--bronze-deep)}
141
+ .rrow .rslot{font-family:var(--run);font-size:15px;fill:#3a3122;transition:fill .4s;
142
+ transform-box:fill-box;transform-origin:center}
143
+ .rrow .rsc{stroke:#241d14;stroke-width:1.2;fill:none;transition:stroke .4s}
144
+ .rrow.carved .rslot{fill:var(--bronze);filter:drop-shadow(0 0 3px rgba(201,151,63,.55));
145
+ animation:carvepop .55s cubic-bezier(.2,.7,.3,1.3) both}
146
+ .rrow.carved .rsc{stroke:var(--bronze-deep)}
147
+ @keyframes carvepop{0%{opacity:0;transform:scale(.4)}60%{transform:scale(1.3)}100%{opacity:1;transform:scale(1)}}
148
+ #ledgerG.flash .stn{stroke:#e0b25f}
149
+
150
+ /* ripples, drops, motes */
151
+ .rip2{fill:none;stroke:var(--bronze);stroke-width:1.6;transform-box:fill-box;transform-origin:center;
152
+ animation:rip2 1.15s ease-out forwards}
153
+ @keyframes rip2{0%{transform:scale(calc(.15*var(--rs,1)),calc(.15*var(--rs,1)));opacity:.9}
154
+ 100%{transform:scale(calc(6*var(--rs,1)),calc(2.4*var(--rs,1)));opacity:0}}
155
+ .ripSlow{fill:none;stroke:#57411a;stroke-width:1.4;transform-box:fill-box;transform-origin:center;
156
+ animation:ripSlow 3.4s ease-out infinite}
157
+ @keyframes ripSlow{0%{transform:scale(.4);opacity:0}40%{opacity:.5}100%{transform:scale(2.6,.9);opacity:0}}
158
+ .dropFall{fill:#c9a35a;opacity:0;animation:dropfall 1.5s cubic-bezier(.45,0,.8,.6) forwards}
159
+ @keyframes dropfall{0%{opacity:0;transform:translateY(0)}12%{opacity:.9}88%{opacity:.9}
160
+ 100%{opacity:0;transform:translateY(690px)}}
161
+ .moteUp{fill:#7a5c2c;opacity:0;animation:moteUp 7.5s ease-in-out infinite}
162
+ @keyframes moteUp{0%{transform:translateY(0);opacity:0}18%{opacity:.65}
163
+ 100%{transform:translateY(-120px);opacity:0}}
164
+
165
+ /* connectors */
166
+ .lnk{fill:none;stroke:var(--bronze);stroke-width:1.6;stroke-dasharray:3 8;opacity:0;
167
+ transition:opacity .4s;pointer-events:none}
168
+ .lnk.lit{opacity:.75;animation:march 1.1s linear infinite}
169
+
170
+ /* ============ caption + plinth ============ */
171
+ .plate{max-width:780px;margin:0 auto;padding:0 1.4rem 3rem}
172
+ .cap-hint{text-align:center;font-family:var(--mono);font-size:.58rem;letter-spacing:.18em;
173
+ color:var(--bone-faint);text-transform:uppercase;line-height:2.3;margin-bottom:.7rem}
174
+ .cap-hint b{color:var(--bronze);font-weight:400}
175
+ .cap{text-align:center;font-family:var(--mono);font-size:.62rem;letter-spacing:.24em;
176
+ text-transform:uppercase;color:var(--bone-faint);line-height:2.6}
177
+ .cap button{background:none;border:none;cursor:pointer;font:inherit;color:var(--bone-dim);
178
+ letter-spacing:inherit;padding:.2em .1em;border-bottom:1px solid transparent;
179
+ transition:color .25s,border-color .25s}
180
+ .cap button:hover,.cap button.on{color:var(--bronze);border-bottom-color:var(--bronze-deep)}
181
+ .cap button .rn{color:var(--bronze);margin-right:.45em}
182
+ .cap i{font-style:normal;color:var(--bone-faint);margin:0 .3em}
183
+
184
+ .carved{background:var(--panel3);border:1px solid var(--line);
185
+ 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)}
186
+ .plinth{margin-top:1.7rem;padding:1.7rem clamp(1.2rem,4vw,2.1rem) 1.9rem}
187
+ .pl-head{display:flex;align-items:center;gap:1.3rem;padding-bottom:1.2rem;border-bottom:1px solid var(--line)}
188
+ #pRune{font-family:var(--run);font-size:2.4rem;color:var(--bronze);line-height:1;letter-spacing:.08em}
189
+ .pl-k{font-family:var(--mono);font-size:.56rem;letter-spacing:.34em;color:var(--steel-dim);text-transform:uppercase}
190
+ #pName{font-family:var(--disp);font-weight:600;font-size:1.3rem;letter-spacing:.14em;color:var(--bone);margin-top:.35rem}
191
+ .prow{display:grid;grid-template-columns:5.4rem 1fr;gap:1.2rem;padding:1.05rem 0;
192
+ border-bottom:1px dashed var(--line-soft)}
193
+ .prow:last-of-type{border-bottom:none}
194
+ .ptag{font-family:var(--mono);font-size:.56rem;letter-spacing:.26em;color:var(--bronze);
195
+ text-transform:uppercase;padding-top:.4rem}
196
+ .pmyth{font-family:var(--serif);font-style:italic;font-size:1.05rem;color:var(--bone);line-height:1.75}
197
+ .pymir{font-family:var(--mono);font-size:.72rem;color:var(--steel);line-height:1.85}
198
+ .pl-head,.prow{transition:opacity .18s}
199
+ .plinth.swap .pl-head,.plinth.swap .prow{opacity:.06}
200
+ @media(max-width:560px){.prow{grid-template-columns:1fr;gap:.3rem}.ptag{padding-top:0}}
201
+
202
+ .pl-actions{text-align:center;margin-top:1.9rem}
203
+ .btn-carv{font-family:var(--mono);font-size:.64rem;letter-spacing:.32em;text-indent:.32em;text-transform:uppercase;
204
+ color:var(--bone);background:var(--panel);border:1px solid var(--line);padding:.95rem 1.9rem;cursor:pointer;
205
+ 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);
206
+ transition:border-color .3s,color .3s,transform .1s}
207
+ .btn-carv .rn{color:var(--bronze);margin-right:.7em;font-size:.85rem;letter-spacing:0;text-indent:0}
208
+ .btn-carv:hover{border-color:var(--bronze-deep);color:#e6dcc2}
209
+ .btn-carv:active{transform:translateY(1px)}
210
+ .btn-carv.busy{opacity:.55;pointer-events:none}
211
+
212
+ /* quote + callout + unfold */
213
+ .quote{border-left:2px solid var(--bronze-deep);padding:.3rem 0 .3rem 1.6rem;margin:2.6rem auto 0;
214
+ font-size:1.12rem;font-style:italic;color:var(--bone);line-height:1.85;max-width:58ch}
215
+ .quote cite{display:block;margin-top:.9rem;font-style:normal;font-family:var(--mono);font-size:.56rem;
216
+ letter-spacing:.32em;color:var(--steel-dim);text-transform:uppercase}
217
+ .callout{display:flex;gap:1rem;align-items:flex-start;padding:1.1rem 1.3rem;margin-top:2.2rem}
218
+ .callout .rn{color:var(--bronze);font-size:1.1rem;line-height:1.5}
219
+ .callout p{font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;color:var(--steel);line-height:1.9}
220
+ .callout b{color:var(--bone);font-weight:500}
221
+ details.unfold{margin-top:2.4rem;border:1px solid var(--line);background:var(--panel3)}
222
+ details.unfold summary{padding:1.05rem 1.3rem;font-family:var(--mono);font-size:.62rem;letter-spacing:.32em;
223
+ color:var(--bone-dim);cursor:pointer;list-style:none;display:flex;align-items:center;gap:1rem;
224
+ text-transform:uppercase;transition:color .3s}
225
+ details.unfold summary::-webkit-details-marker{display:none}
226
+ details.unfold summary:hover{color:var(--bone)}
227
+ details.unfold summary .rn{color:var(--bronze);font-size:.95rem}
228
+ .tend-grid{display:grid;grid-template-columns:1fr 1fr;gap:2.4rem;padding:1.2rem 1.4rem 0}
229
+ @media(max-width:640px){.tend-grid{grid-template-columns:1fr}}
230
+ .tg-k{font-family:var(--mono);font-size:.6rem;letter-spacing:.3em;color:var(--bone-dim);
231
+ text-transform:uppercase;margin-bottom:.9rem}
232
+ .tg-k .rn{color:var(--bronze);margin-right:.5em;font-size:.9rem}
233
+ .tend-grid ul{list-style:none}
234
+ .tend-grid li{font-family:var(--mono);font-size:.62rem;color:var(--steel);line-height:1.7;
235
+ padding:.45rem 0 .45rem 1.1rem;border-bottom:1px dashed var(--line-soft);position:relative}
236
+ .tend-grid li::before{content:'—';position:absolute;left:0;color:var(--bronze-deep)}
237
+ .tg-road{font-family:var(--mono);font-size:.62rem;color:var(--steel-dim);padding:1.3rem 1.4rem 1.6rem;line-height:1.9}
238
+ .tg-road .rn{color:var(--bronze);margin-right:.5em}
239
+
240
+ /* the rune ledger */
241
+ .rune-ledger{margin-top:1.4rem;padding:1.2rem 1.4rem;max-height:190px;overflow:auto}
242
+ .rune-ledger::-webkit-scrollbar{width:6px}
243
+ .rune-ledger::-webkit-scrollbar-thumb{background:#2c241a}
244
+ .rl-k{font-family:var(--mono);font-size:.54rem;letter-spacing:.32em;color:var(--steel-dim);
245
+ text-transform:uppercase;margin-bottom:.5rem}
246
+ .rl-k b{color:var(--bronze);font-weight:400}
247
+ #rlList{list-style:none}
248
+ #rlList li{display:flex;gap:.8rem;font-family:var(--mono);font-size:.62rem;letter-spacing:.04em;
249
+ color:var(--bone-dim);padding:.5rem 0;border-bottom:1px dashed var(--line-soft);animation:carvein .5s both}
250
+ #rlList li .rn{color:var(--bronze);flex:0 0 1.1em}
251
+ @keyframes carvein{from{opacity:0;transform:translateX(-8px)}}
252
+
253
+ .pull{font-family:var(--disp);font-size:clamp(1.05rem,2vw,1.3rem);letter-spacing:.06em;line-height:1.8;
254
+ color:var(--bone);border-left:2px solid var(--bronze-deep);padding:.3rem 0 .3rem 1.6rem;
255
+ margin:3rem auto 0;max-width:56ch}
256
+ .pull small{display:block;font-family:var(--mono);font-size:.56rem;letter-spacing:.3em;
257
+ color:var(--steel-dim);margin-top:.9rem;text-transform:uppercase}
258
+
259
+ /* ============ footer ============ */
260
+ footer{border-top:1px solid var(--line);padding:3.6rem 1.5rem 4rem;text-align:center;animation:fadeUp 1s .5s both}
261
+ .frule{display:flex;align-items:center;gap:1.4rem;max-width:640px;margin:0 auto 2.8rem}
262
+ .frule::before,.frule::after{content:'';flex:1;height:1px;background:var(--line-soft)}
263
+ .frule span{font-family:var(--run);font-size:.7rem;letter-spacing:.42em;color:var(--bone-faint);white-space:nowrap}
264
+ .f-big{font-family:var(--disp);font-weight:600;font-size:clamp(1.15rem,2.6vw,1.65rem);
265
+ letter-spacing:.14em;color:var(--bone);text-shadow:0 -1px 0 rgba(0,0,0,.8)}
266
+ .f-mono{font-family:var(--mono);font-size:.56rem;letter-spacing:.3em;color:var(--steel-dim);
267
+ margin-top:1.7rem;text-transform:uppercase;line-height:2.3}
268
+ .f-repo{font-family:var(--mono);font-size:.58rem;color:var(--bone-faint);letter-spacing:.16em;margin-top:1.2rem}
269
+
270
+ @media (prefers-reduced-motion:reduce){
271
+ *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
272
+ }
273
+ </style>
274
+ </head>
275
+ <body>
276
+
277
+ <div class="topbar">
278
+ <span><span class="rn">ᛚ</span>THE WELL · MIMIRSBRUNN</span>
279
+ <span>the memory of all things</span>
280
+ </div>
281
+
282
+ <canvas id="gap" aria-hidden="true"></canvas>
283
+
284
+ <main>
285
+ <!-- ============ THE LORE ============ -->
286
+ <header class="lore-head">
287
+ <p class="lh-k">the lore · §iv · mimirsbrunn</p>
288
+ <h1>The Well</h1>
289
+ <p class="lh-motto">drink before you act · water it afterwards</p>
290
+ <div class="lh-rule"></div>
291
+ <p class="lh-body">At the root of Yggdrasil lies Mimirsbrunn, the well of wisdom. Mimir guards it;
292
+ whoever drinks its water receives the memory of all things — even Odin paid an eye for a single
293
+ drink.</p>
294
+ <p class="lh-body">In Ymir the water is engram — every meaningful action is observed into the well,
295
+ and before any orchestration the fleet recalls what it already knows about the work at hand. The
296
+ one who speaks from the well is Kaia, the oracle that answers what we remember before anyone acts.</p>
297
+ <p class="lh-close">Drink before you act; water it afterwards.</p>
298
+ </header>
299
+
300
+ <!-- ============ THE UNDERGROUND CARVING ============ -->
301
+ <section class="stage">
302
+ <p class="anno anno-l">Mimirsbrunn — the well of wisdom at the root of the tree. Mimir keeps it;
303
+ whoever drinks receives the memory of all things. Even Odin paid an eye, and the eye still lies
304
+ at the bottom, watching.</p>
305
+ <p class="anno anno-r">single-file sqlite<br>vector + full-text + embeddings<br>post /observe · get /recall<br>memory: boost, never blocker</p>
306
+
307
+ <div class="scenewrap">
308
+ <svg id="scene" viewBox="0 0 760 1100" role="group" aria-label="A cross-section through the earth: the well of Mimirsbrunn, its keeper, the drinking root, the standing runestone, and the eye at the bottom of the water">
309
+ <defs>
310
+ <radialGradient id="halo" cx="50%" cy="50%" r="50%">
311
+ <stop offset="0" stop-color="#1c160e" stop-opacity=".5"/>
312
+ <stop offset="1" stop-color="#1c160e" stop-opacity="0"/>
313
+ </radialGradient>
314
+ </defs>
315
+
316
+ <!-- the earth, cut through -->
317
+ <rect x="0" y="76" width="760" height="1024" fill="#100d09"/>
318
+ <ellipse cx="380" cy="620" rx="320" ry="440" fill="url(#halo)" pointer-events="none"/>
319
+
320
+ <!-- the world above: the underside of the tree -->
321
+ <g pointer-events="none">
322
+ <path d="M0 0 H760 V70 C680 84 600 64 520 76 C440 88 360 66 280 78 C200 88 120 68 40 80 L0 70 Z"
323
+ fill="#171209" stroke="#4a4232" stroke-width="2"/>
324
+ <path d="M-6 26 C100 14 210 36 330 22 C450 10 570 34 766 20" fill="none" stroke="#4a3d2a" stroke-width="16" stroke-linecap="round"/>
325
+ <path d="M-6 26 C100 14 210 36 330 22 C450 10 570 34 766 20" fill="none" stroke="#0e0c09" stroke-width="8" stroke-linecap="round"/>
326
+ <path d="M-6 52 C120 42 240 62 380 48 C520 36 640 58 766 46" fill="none" stroke="#4a3d2a" stroke-width="12" stroke-linecap="round"/>
327
+ <path d="M-6 52 C120 42 240 62 380 48 C520 36 640 58 766 46" fill="none" stroke="#0e0c09" stroke-width="6" stroke-linecap="round"/>
328
+ <path d="M-6 40 C90 50 180 32 280 44 C380 54 470 36 570 48" fill="none" stroke="#4a3d2a" stroke-width="8" stroke-linecap="round"/>
329
+ <path d="M180 78 C178 88 182 96 180 104" fill="none" stroke="#4a3d2a" stroke-width="5" stroke-linecap="round"/>
330
+ <path d="M610 82 C608 92 612 100 610 106" fill="none" stroke="#4a3d2a" stroke-width="5" stroke-linecap="round"/>
331
+ <path d="M300 84 C298 92 302 98 300 104" fill="none" stroke="#4a3d2a" stroke-width="4" stroke-linecap="round"/>
332
+ <path d="M420 74 C418 84 422 92 420 100" fill="none" stroke="#4a3d2a" stroke-width="4" stroke-linecap="round"/>
333
+ </g>
334
+
335
+ <!-- generated earth texture -->
336
+ <g id="speckles" pointer-events="none"></g>
337
+
338
+ <!-- ======== THE LEDGER — the standing runestone ======== -->
339
+ <g id="ledgerG" class="bpart" data-part="ledger" tabindex="0" role="button"
340
+ aria-label="the ledger — the standing runestone, carved with every draught">
341
+ <path class="stn" style="stroke-width:2" d="M98 955 L98 650 C98 604 118 580 138 580 C158 580 178 604 178 650 L178 955 Z"/>
342
+ <path class="stn2" d="M106 948 L106 652 C106 612 124 590 138 590 C152 590 170 612 170 652 L170 948"/>
343
+ <path class="gs" style="fill:#14100b" d="M88 955 L188 955 L196 972 L80 972 Z"/>
344
+ <g id="rrows"></g>
345
+ <text class="lab" x="138" y="996" text-anchor="middle">THE LEDGER</text>
346
+ <text class="lab3" x="138" y="1014" text-anchor="middle">runes · append-only</text>
347
+ <path class="lnk" data-part="ledger" d="M178 646 C224 636 258 584 290 516"/>
348
+ <rect class="hit" x="86" y="572" width="104" height="404"/>
349
+ </g>
350
+
351
+ <!-- ======== THE GUARDIAN — Mimir at the rim ======== -->
352
+ <g id="guardG" class="bpart" data-part="guardian" tabindex="0" role="button"
353
+ aria-label="the guardian — Mimir, the keeper of the well">
354
+ <path class="gs" style="fill:#14100b" d="M100 505 L212 505 L206 517 L106 517 Z"/>
355
+ <path class="gs" style="fill:#161009" d="M132 372 C124 414 118 460 112 505 L202 505 C196 460 190 414 182 372 C170 384 146 384 132 372 Z"/>
356
+ <path class="det" style="stroke-width:1.6" d="M146 392 C142 432 140 470 138 502 M158 390 C158 436 158 470 158 500 M172 392 C174 432 176 470 178 502"/>
357
+ <path class="gs" style="fill:#161009" d="M130 374 C124 350 138 332 157 332 C176 332 190 350 184 374 C170 384 146 384 130 374 Z"/>
358
+ <path style="fill:#0b0906;stroke:#2e261a;stroke-width:1.5" d="M140 364 C144 348 170 348 174 364 C166 371 148 371 140 364 Z"/>
359
+ <circle class="keppe" cx="159" cy="359" r="1.8"/>
360
+ <path class="limb-o" style="stroke-width:9" d="M182 398 C198 404 208 412 216 420"/>
361
+ <path class="limb-i" style="stroke-width:4.5" d="M182 398 C198 404 208 412 216 420"/>
362
+ <circle class="gs" cx="219" cy="422" r="4.5"/>
363
+ <g class="kkey">
364
+ <circle cx="225" cy="424" r="4.5" style="fill:none;stroke-width:2"/>
365
+ <path style="fill:none;stroke-width:2.5" d="M229 426 L244 432 M238 433 l1 7 M243 435 l1 6"/>
366
+ </g>
367
+ <text class="lab" x="158" y="306" text-anchor="middle">THE GUARDIAN</text>
368
+ <text class="lab3" x="158" y="324" text-anchor="middle">mimir · the keeper</text>
369
+ <path class="lnk" data-part="guardian" d="M222 424 C254 414 282 434 304 456"/>
370
+ <rect class="hit" x="96" y="328" width="116" height="192"/>
371
+ </g>
372
+
373
+ <!-- ======== THE WELL ======== -->
374
+ <g id="wellG" pointer-events="none">
375
+ <rect x="306" y="470" width="148" height="462" fill="#0a0806"/>
376
+ <rect x="270" y="512" width="36" height="420" fill="#14100b" stroke="#4a4232" stroke-width="2"/>
377
+ <path class="joints" d="M270 565 h36 M270 618 h36 M270 671 h36 M270 724 h36 M270 777 h36 M270 830 h36 M270 883 h36"/>
378
+ <path class="jv" d="M288 512 v53 M278 565 v53 M288 618 v53 M278 671 v53 M288 724 v53 M278 777 v53 M288 830 v53 M278 883 v49"/>
379
+ <rect x="454" y="512" width="36" height="420" fill="#14100b" stroke="#4a4232" stroke-width="2"/>
380
+ <path class="joints" d="M454 565 h36 M454 618 h36 M454 671 h36 M454 724 h36 M454 777 h36 M454 830 h36 M454 883 h36"/>
381
+ <path class="jv" d="M472 512 v53 M482 565 v53 M472 618 v53 M482 671 v53 M472 724 v53 M482 777 v53 M472 830 v53 M482 883 v49"/>
382
+ <rect x="238" y="470" width="68" height="42" fill="#171209" stroke="#4a4232" stroke-width="2"/>
383
+ <rect x="454" y="470" width="68" height="42" fill="#171209" stroke="#4a4232" stroke-width="2"/>
384
+ <path class="jv" d="M246 480 l-4 6 M296 486 l-4 6 M470 478 l-4 6 M512 486 l-4 6"/>
385
+ <text class="caprune" x="272" y="498" text-anchor="middle">ᛚ</text>
386
+ <text class="caprune" x="488" y="498" text-anchor="middle">ᚢ</text>
387
+ <path style="stroke:#4a4232;stroke-width:4" d="M268 932 L492 932" fill="none"/>
388
+ </g>
389
+
390
+ <!-- ======== THE WATER ======== -->
391
+ <g id="waterG" class="bpart" data-part="water" tabindex="0" role="button"
392
+ aria-label="the water — draw a draught from the well">
393
+ <path class="gs wbody" style="fill:#080706;stroke:#241d14" d="M306 800 C326 792 352 808 380 800 C408 792 434 808 454 800 L454 928 L306 928 Z"/>
394
+ <path class="wv" d="M306 800 C326 792 352 808 380 800 C408 792 434 808 454 800"/>
395
+ <path class="wv2" d="M318 838 C338 832 362 844 388 838 C412 833 434 843 448 838"/>
396
+ <path class="wv3" d="M326 868 C346 863 372 873 398 868 C418 864 436 872 446 868"/>
397
+ <text class="lab" x="380" y="758" text-anchor="middle">THE WATER</text>
398
+ <text class="lab3" x="380" y="776" text-anchor="middle">engram</text>
399
+ <rect class="hit" x="302" y="742" width="156" height="192"/>
400
+ </g>
401
+
402
+ <!-- ======== THE ROOT — yggdrasil drinking ======== -->
403
+ <g id="rootG" class="bpart" data-part="root" tabindex="0" role="button"
404
+ aria-label="the root of yggdrasil, descending into the well to drink">
405
+ <path class="limb-o" style="stroke-width:19" d="M505 62 C494 128 496 200 482 258 C472 306 460 352 454 400 C450 448 444 508 441 560 C438 640 439 724 442 796"/>
406
+ <path class="limb-i" style="stroke-width:10" d="M505 62 C494 128 496 200 482 258 C472 306 460 352 454 400 C450 448 444 508 441 560 C438 640 439 724 442 796"/>
407
+ <path style="fill:none;stroke:#241a12;stroke-width:8;stroke-linecap:round" d="M442 796 C441 812 438 828 434 840"/>
408
+ <path class="limb-o" style="stroke-width:7" d="M496 150 C508 140 520 138 532 142"/>
409
+ <path class="limb-i" style="stroke-width:3.5" d="M496 150 C508 140 520 138 532 142"/>
410
+ <path class="limb-o" style="stroke-width:6" d="M478 330 C466 322 456 322 448 326"/>
411
+ <path class="limb-i" style="stroke-width:3" d="M478 330 C466 322 456 322 448 326"/>
412
+ <path class="det" style="stroke-width:2" d="M494 120 l-8 3 M486 190 l-8 2 M474 262 l-8 0 M462 330 l-8 -2 M454 400 l-8 -3 M446 470 l-7 -4 M442 545 l-6 -5 M439 620 l-5 -6 M439 700 l-4 -6 M441 770 l-3 -7"/>
413
+ <path class="rflow" d="M505 62 C494 128 496 200 482 258 C472 306 460 352 454 400 C450 448 444 508 441 560 C438 640 439 724 442 796"/>
414
+ <text class="lab" x="628" y="316" text-anchor="middle">THE ROOT</text>
415
+ <text class="lab3" x="628" y="334" text-anchor="middle">yggdrasil · observe &amp; recall</text>
416
+ <path class="lnk" data-part="root" d="M486 322 C516 326 540 322 558 318"/>
417
+ <path class="hit" style="fill:none;stroke-width:48" d="M505 62 C494 128 496 200 482 258 C472 306 460 352 454 400 C450 448 444 508 441 560 C438 640 439 724 442 796"/>
418
+ </g>
419
+
420
+ <!-- ripples + the memory surfacing + motes -->
421
+ <g id="ripples" pointer-events="none">
422
+ <ellipse class="ripSlow" cx="442" cy="800" rx="10" ry="3.5"/>
423
+ </g>
424
+ <g id="memT" pointer-events="none">
425
+ <text id="mem1" class="mem" x="380" y="824" text-anchor="middle"></text>
426
+ <text id="mem2" class="mem" x="380" y="840" text-anchor="middle"></text>
427
+ </g>
428
+ <g id="motesG" pointer-events="none">
429
+ <circle class="moteUp" cx="330" cy="786" r="1.5"/>
430
+ <circle class="moteUp" cx="388" cy="792" r="1.3" style="animation-delay:-2.8s;animation-duration:8.5s"/>
431
+ <circle class="moteUp" cx="428" cy="784" r="1.6" style="animation-delay:-5.2s;animation-duration:6.5s"/>
432
+ </g>
433
+
434
+ <!-- ======== THE EYE — what the water keeps ======== -->
435
+ <g id="eyeG" class="bpart" data-part="eye" tabindex="0" role="button"
436
+ aria-label="the eye at the bottom of the well — the price Odin paid">
437
+ <ellipse class="eg" cx="380" cy="886" rx="40" ry="15" fill="#c9973f" fill-opacity=".08"/>
438
+ <ellipse class="eg" cx="380" cy="886" rx="22" ry="9" fill="#c9973f" fill-opacity=".14"/>
439
+ <path class="ec gs" style="fill:none;stroke-width:2.5" d="M358 886 C368 880 392 880 402 886"/>
440
+ <path class="ec" style="fill:none;stroke:#2e261a;stroke-width:2" d="M370 888 l-3 5 M380 889 l0 5 M390 888 l3 5"/>
441
+ <path class="eo arc" d="M358 886 C366 866 394 866 402 886"/>
442
+ <path class="eo arc" d="M358 886 C366 904 394 904 402 886"/>
443
+ <circle class="eo iris" cx="380" cy="886" r="12.5"/>
444
+ <circle class="eo" cx="380" cy="886" r="5" style="fill:#c9973f;stroke:none"/>
445
+ <circle class="eo" cx="375" cy="881" r="2.2" style="fill:#e8dcb8;stroke:none"/>
446
+ <text class="lab" x="380" y="1004" text-anchor="middle">THE EYE</text>
447
+ <text class="lab3" x="380" y="1022" text-anchor="middle">odin's price · the embedder</text>
448
+ <path class="lnk" data-part="eye" d="M380 988 L380 938"/>
449
+ <circle class="hit" cx="380" cy="886" r="30"/>
450
+ </g>
451
+
452
+ <!-- ======== THE RIG — beam, rope, bucket ======== -->
453
+ <g id="rigG" pointer-events="none">
454
+ <rect x="244" y="378" width="12" height="92" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
455
+ <rect x="504" y="378" width="12" height="92" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
456
+ <path d="M256 442 C262 430 268 412 274 384" fill="none" stroke="#4a3d2a" stroke-width="5" stroke-linecap="round"/>
457
+ <path d="M504 442 C498 430 492 412 486 384" fill="none" stroke="#4a3d2a" stroke-width="5" stroke-linecap="round"/>
458
+ <rect x="228" y="360" width="304" height="20" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
459
+ <path d="M232 370 h296" stroke="#2e261a" stroke-width="1.6" fill="none"/>
460
+ <path d="M236 365 l5 5 l-5 5 l-5 -5 Z M524 365 l5 5 l-5 5 l-5 -5 Z" fill="#161009" stroke="#4a3d2a" stroke-width="1.6"/>
461
+ <circle cx="380" cy="370" r="6" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
462
+ <circle cx="510" cy="362" r="4" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
463
+ <path d="M510 362 L522 348" stroke="#4a3d2a" stroke-width="3" stroke-linecap="round" fill="none"/>
464
+ <circle cx="523" cy="347" r="2.5" fill="#161009" stroke="#4a3d2a" stroke-width="1.6"/>
465
+ <line id="rope" x1="380" y1="374" x2="380" y2="588" stroke="#6b5b3a" stroke-width="2"/>
466
+ <g id="bucketG">
467
+ <circle cx="380" cy="590" r="3" fill="#161009" stroke="#4a3d2a" stroke-width="1.5"/>
468
+ <path d="M362 600 C366 588 394 588 398 600" fill="none" stroke="#4a3d2a" stroke-width="3"/>
469
+ <path d="M360 602 L400 602 L395 648 L365 648 Z" fill="#161009" stroke="#4a3d2a" stroke-width="2"/>
470
+ <path d="M371 604 L370 646 M389 604 L390 646" stroke="#2e261a" stroke-width="1.2" fill="none"/>
471
+ <path d="M361 618 L399 618" stroke="#4a3d2a" stroke-width="2" fill="none"/>
472
+ </g>
473
+ </g>
474
+
475
+ <!-- drops from the world above -->
476
+ <g id="drops" pointer-events="none"></g>
477
+ </svg>
478
+ </div>
479
+ </section>
480
+
481
+ <!-- ============ caption + plinth ============ -->
482
+ <section class="plate">
483
+ <p class="cap-hint"><b>◆</b> draw a draught from the water — the drop from the roots never stops falling, and every draught is carved</p>
484
+ <p class="cap">hover or touch the carving —
485
+ <button data-p="water"><span class="rn">ᛚ</span>the water</button><i>·</i>
486
+ <button data-p="guardian"><span class="rn">ᛗ</span>the guardian</button><i>·</i>
487
+ <button data-p="root"><span class="rn">ᛇ</span>the root</button><i>·</i>
488
+ <button data-p="ledger"><span class="rn">ᚠ</span>the ledger</button><i>·</i>
489
+ <button data-p="eye"><span class="rn">ᛉ</span>the eye</button>
490
+ </p>
491
+
492
+ <div class="plinth carved" id="plinth" aria-live="polite">
493
+ <div class="pl-head">
494
+ <span id="pRune">ᛚ</span>
495
+ <div>
496
+ <p class="pl-k">the well remembers — touch the water</p>
497
+ <p id="pName">Mimirsbrunn</p>
498
+ </div>
499
+ </div>
500
+ <div class="prow">
501
+ <span class="ptag">The myth</span>
502
+ <p class="pmyth" id="pMyth">At the root of the tree lies the well of wisdom; whoever drinks receives the memory of all things.</p>
503
+ </div>
504
+ <div class="prow">
505
+ <span class="ptag">In Ymir</span>
506
+ <p class="pymir" id="pYmir">In Ymir: the water is engram — every action observed, every plan recalls. Memory is a boost, never a blocker.</p>
507
+ </div>
508
+ </div>
509
+
510
+ <blockquote class="quote">She does not act on faith. Before she dispatches a single smith she drinks from
511
+ the well, then passes every plan through the veil — is this grounded in what the well holds, or is it
512
+ spun from air? Only what passes is forged.
513
+ <cite>kaia's charge</cite></blockquote>
514
+
515
+ <details class="unfold">
516
+ <summary><span class="rn">ᛚ</span>Unfold what is observed and what is recalled</summary>
517
+ <div class="tend-grid">
518
+ <div>
519
+ <p class="tg-k"><span class="rn">ᚨ</span>observed — into the well</p>
520
+ <ul>
521
+ <li>every meaningful action — POST /observe, the act of watering</li>
522
+ <li>every word spoken between realms — the squirrel gossips, the ledger remembers</li>
523
+ <li>a skill's creation, before it is deemed live</li>
524
+ <li>skuld's verdict on every branch that enters the tree</li>
525
+ </ul>
526
+ </div>
527
+ <div>
528
+ <p class="tg-k"><span class="rn">ᚱ</span>recalled — from the well</p>
529
+ <ul>
530
+ <li>what the project has taught, before any dispatch — GET /recall</li>
531
+ <li>kaia's drink, before every plan passes the veil</li>
532
+ <li>sága's bearings, when a session takes its seat</li>
533
+ <li>völundr's roster, before the smithy sets its chain</li>
534
+ </ul>
535
+ </div>
536
+ </div>
537
+ <p class="tg-road"><span class="rn">ᛚ</span>and the rule holds through the driest season — memory is always a boost, never a blocker: a dry well fires cold, but never stops the forge.</p>
538
+ </details>
539
+
540
+ <div class="pl-actions">
541
+ <button class="btn-carv" id="drinkBtn"><span class="rn">ᛚ</span><span id="drinkLabel">draw a draught</span></button>
542
+ </div>
543
+
544
+ <div class="rune-ledger carved" aria-label="Runes, the append-only ledger">
545
+ <p class="rl-k">Runes — the append-only ledger · draughts drawn · <b id="dCount">0</b></p>
546
+ <ul id="rlList"></ul>
547
+ </div>
548
+
549
+ <div class="callout carved">
550
+ <span class="rn">ᛚ</span>
551
+ <p><b>RULE OF THE WELL</b> — memory is always a <b>boost, never a blocker</b>. A dry well fires
552
+ cold, but never stops the forge.</p>
553
+ </div>
554
+
555
+ <blockquote class="pull">A rune that is carved cannot be un-carved.
556
+ <small>the lore · §IV</small></blockquote>
557
+
558
+ <div class="pl-actions">
559
+ <button class="btn-carv" id="tourBtn"><span class="rn">ᛚ</span><span id="tourLabel">let the well speak</span></button>
560
+ </div>
561
+ </section>
562
+ </main>
563
+
564
+ <footer>
565
+ <div class="frule"><span>ᚠ ᚢ ᚦ ᚨ ᚱ ᚲ ᚷ ᚹ ᚺ ᚾ ᛁ ᛇ ᛈ ᛉ ᛊ ᛏ ᛒ ᛖ ᛗ ᛚ ᛜ ᛞ ᛟ</span></div>
566
+ <p class="f-big">A rune that is carved cannot be un-carved.</p>
567
+ <p class="f-mono">observe · recall · carve</p>
568
+ <p class="f-repo">zerwiz/ymir · docs/design.md · the lore v2 · §IV</p>
569
+ </footer>
570
+
571
+ <script>
572
+ (function(){
573
+ 'use strict';
574
+ const $ = s => document.querySelector(s);
575
+ const $$ = s => Array.from(document.querySelectorAll(s));
576
+ const NS = 'http://www.w3.org/2000/svg';
577
+ const RM = matchMedia('(prefers-reduced-motion: reduce)').matches;
578
+
579
+ /* ---------- the runestone's twelve waiting rows ---------- */
580
+ const FUTH12 = ['ᚠ','ᚢ','ᚦ','ᚨ','ᚱ','ᚲ','ᚷ','ᚹ','ᚺ','ᚾ','ᛁ','ᛇ'];
581
+ const FUTH = ['ᚠ','ᚢ','ᚦ','ᚨ','ᚱ','ᚲ','ᚷ','ᚹ','ᚺ','ᚾ','ᛁ','ᛇ','ᛈ','ᛉ','ᛊ','ᛏ','ᛒ','ᛖ','ᛗ','ᛚ','ᛜ','ធ','ᛟ'];
582
+ (function(){
583
+ const rr = $('#rrows');
584
+ for(let i = 0; i < 12; i++){
585
+ const y = 622 + i * 26;
586
+ const g = document.createElementNS(NS, 'g');
587
+ g.setAttribute('class', 'rrow');
588
+ const t = document.createElementNS(NS, 'text');
589
+ t.setAttribute('class', 'rslot');
590
+ t.setAttribute('x', 138); t.setAttribute('y', y); t.setAttribute('text-anchor', 'middle');
591
+ t.textContent = FUTH12[i];
592
+ const p = document.createElementNS(NS, 'path');
593
+ p.setAttribute('class', 'rsc');
594
+ p.setAttribute('d', `M112 ${y + 7} L164 ${y + 7}`);
595
+ g.appendChild(t); g.appendChild(p); rr.appendChild(g);
596
+ }
597
+ })();
598
+
599
+ /* ---------- generated earth texture ---------- */
600
+ (function(){
601
+ const Z = [[266,466,494,940],[86,566,192,978],[94,310,266,522],[556,296,700,344]];
602
+ const sp = $('#speckles');
603
+ for(let i = 0; i < 36; i++){
604
+ const x = 14 + Math.random() * 732, y = 120 + Math.random() * 940;
605
+ if(Z.some(z => x > z[0] && x < z[2] && y > z[1] && y < z[3])) continue;
606
+ if(i % 6 === 0){
607
+ let d = '';
608
+ for(let k = 0; k < 3; k++) d += `M${(x+k*7-7).toFixed(1)} ${y.toFixed(1)} l-5 8 `;
609
+ const p = document.createElementNS(NS, 'path');
610
+ p.setAttribute('d', d); p.setAttribute('stroke', '#241d14'); p.setAttribute('stroke-width', '1.3');
611
+ p.setAttribute('fill', 'none');
612
+ sp.appendChild(p);
613
+ }else if(i % 9 === 0){
614
+ const c = document.createElementNS(NS, 'circle');
615
+ c.setAttribute('cx', x.toFixed(1)); c.setAttribute('cy', y.toFixed(1)); c.setAttribute('r', '1.5');
616
+ c.setAttribute('fill', '#4a3820');
617
+ sp.appendChild(c);
618
+ }else{
619
+ const s = (2 + Math.random() * 2.4).toFixed(1);
620
+ const f = ['#1a140d','#1a140d','#241b10','#2e2315'][i % 4];
621
+ const p = document.createElementNS(NS, 'path');
622
+ p.setAttribute('d', `M${x.toFixed(1)} ${y.toFixed(1)}l${s} -${s}l${s} ${s}l-${s} ${s}z`);
623
+ p.setAttribute('fill', f);
624
+ sp.appendChild(p);
625
+ }
626
+ }
627
+ })();
628
+
629
+ /* ---------- the carving speaks ---------- */
630
+ const PARTS = {
631
+ water:{ rune:'ᛚ', name:'the water',
632
+ myth:'Whoever drinks from the well receives the memory of all things — the water remembers everything that has ever touched it, and it never runs dry.',
633
+ ymir:'engram — the memory engine: single-file SQLite, vector and full-text search, local embeddings. Every meaningful action is observed into it; nothing is stored unseen.'},
634
+ guardian:{ rune:'ᛗ', name:'the guardian',
635
+ myth:'Mimir keeps the well. Even the Allfather must pay to drink — and the price he paid still lies at the bottom, watching.',
636
+ ymir:'the keeper of memory — the planner who tends what is remembered: recall before acting, observe after acting, and no answer ever spoken dry.'},
637
+ root:{ rune:'ᛇ', name:'the root',
638
+ myth:'The well lies at the root of Yggdrasil — the tree drinks deep of what the water remembers, and every leaf above waters it in return.',
639
+ ymir:'the flows — POST /observe waters the well after every act; GET /recall drinks before every dispatch. Kaia sends no smith anywhere dry.'},
640
+ ledger:{ rune:'ᚠ', name:'the ledger',
641
+ myth:'The ledger of the gods is written in runes, not ink — and a rune that is carved cannot be un-carved.',
642
+ ymir:'Runes — the append-only audit ledger. Every significant action is inscribed; entries are never edited and never deleted, only ever added.'},
643
+ eye:{ rune:'ᛉ', name:'the eye',
644
+ myth:'Odin paid an eye for a single drink. The eye lies at the bottom still — open, watching, part of the water now.',
645
+ ymir:'the local embedder — everything that enters the well is seen and turned into memory. Kaia is the eye that remembers; she drinks before every dispatch.'}
646
+ };
647
+ const plinth = $('#plinth');
648
+ let swapT = null, pinned = null, touring = false;
649
+
650
+ function light(p){
651
+ $$('[data-part]').forEach(el =>
652
+ el.classList.toggle('lit', !!p && el.dataset.part === p));
653
+ $('#scene').classList.toggle('focus', !!p);
654
+ $$('.cap button').forEach(b => b.classList.toggle('on', !!p && b.dataset.p === p));
655
+ plinth.classList.add('swap');
656
+ clearTimeout(swapT);
657
+ swapT = setTimeout(() => {
658
+ $('#pRune').textContent = p ? PARTS[p].rune : 'ᛚ';
659
+ $('#pName').textContent = p ? PARTS[p].name : 'Mimirsbrunn';
660
+ $('#pMyth').textContent = p ? PARTS[p].myth :
661
+ 'At the root of the tree lies the well of wisdom; whoever drinks receives the memory of all things.';
662
+ $('#pYmir').textContent = p ? PARTS[p].ymir :
663
+ 'In Ymir: the water is engram — every action observed, every plan recalls. Memory is a boost, never a blocker.';
664
+ plinth.classList.remove('swap');
665
+ }, 170);
666
+ }
667
+ function hook(el, p){
668
+ el.addEventListener('mouseenter', () => { if(!touring) light(p); });
669
+ el.addEventListener('mouseleave', () => { if(!touring) light(pinned); });
670
+ el.addEventListener('click', () => {
671
+ if(p === 'water'){ pinned = 'water'; light('water'); drink(); return; }
672
+ pinned = (pinned === p) ? null : p; light(pinned);
673
+ });
674
+ el.addEventListener('keydown', e => {
675
+ if(e.key === 'Enter' || e.key === ' '){
676
+ e.preventDefault();
677
+ if(p === 'water'){ pinned = 'water'; light('water'); drink(); return; }
678
+ pinned = (pinned === p) ? null : p; light(pinned);
679
+ }
680
+ });
681
+ el.addEventListener('focus', () => { if(!touring) light(p); });
682
+ el.addEventListener('blur', () => { if(!touring) light(pinned); });
683
+ }
684
+ $$('#scene g[data-part]').forEach(g => hook(g, g.dataset.part));
685
+ $$('.cap button').forEach(b => {
686
+ b.addEventListener('mouseenter', () => { if(!touring) light(b.dataset.p); });
687
+ b.addEventListener('mouseleave', () => { if(!touring) light(pinned); });
688
+ b.addEventListener('click', () => {
689
+ const p = b.dataset.p;
690
+ if(p === 'water'){ pinned = 'water'; light('water'); drink(); return; }
691
+ pinned = (pinned === p) ? null : p; light(pinned);
692
+ });
693
+ });
694
+
695
+ /* ---------- the draught ---------- */
696
+ const MEM = [
697
+ ['a rune carved', 'stays carved'],
698
+ ['memory is a boost,', 'never a blocker'],
699
+ ['one session,', 'one reins'],
700
+ ['glitnir never', 'auto-approves'],
701
+ ['the veil demands', 'well evidence'],
702
+ ['drink first,', 'water afterwards'],
703
+ ['nothing crosses', 'bifrost unvouched'],
704
+ ['retries are the', "sender's duty"],
705
+ ['terminal states', 'cannot restart'],
706
+ ['the forge is open', 'to good metal']
707
+ ];
708
+ let draughts = 0, busy = false;
709
+ const bucketG = $('#bucketG'), rope = $('#rope');
710
+
711
+ function setTy(ty){
712
+ bucketG.setAttribute('transform', 'translate(0 ' + ty.toFixed(1) + ')');
713
+ rope.setAttribute('y2', (588 + ty).toFixed(1));
714
+ }
715
+ function tween(from, to, dur, cb){
716
+ const t0 = performance.now();
717
+ (function f(t){
718
+ const k = Math.min(1, (t - t0) / dur);
719
+ const e = k < .5 ? 2 * k * k : 1 - Math.pow(-2 * k + 2, 2) / 2;
720
+ setTy(from + (to - from) * e);
721
+ if(k < 1) requestAnimationFrame(f); else cb && cb();
722
+ })(t0);
723
+ }
724
+ function rippleAt(x, y, s){
725
+ const e = document.createElementNS(NS, 'ellipse');
726
+ e.setAttribute('cx', x); e.setAttribute('cy', y);
727
+ e.setAttribute('rx', 12); e.setAttribute('ry', 4.5);
728
+ e.setAttribute('class', 'rip2');
729
+ e.style.setProperty('--rs', s || 1);
730
+ $('#ripples').appendChild(e);
731
+ setTimeout(() => e.remove(), 1300);
732
+ }
733
+ function showMemory(n){
734
+ const m = MEM[(n - 1) % MEM.length];
735
+ const t1 = $('#mem1'), t2 = $('#mem2');
736
+ t1.textContent = m[0]; t2.textContent = m[1];
737
+ t1.classList.add('show');
738
+ setTimeout(() => t2.classList.add('show'), 260);
739
+ clearTimeout(showMemory._t);
740
+ showMemory._t = setTimeout(() => {
741
+ t1.classList.remove('show'); t2.classList.remove('show');
742
+ }, 3100);
743
+ }
744
+ function carveRune(n){
745
+ const rows = $$('.rrow');
746
+ if(n <= rows.length){ rows[n - 1].classList.add('carved'); }
747
+ else{
748
+ const lg = $('#ledgerG');
749
+ lg.classList.remove('flash');
750
+ requestAnimationFrame(() => requestAnimationFrame(() => lg.classList.add('flash')));
751
+ setTimeout(() => lg.classList.remove('flash'), 900);
752
+ }
753
+ }
754
+ function appendLedger(n){
755
+ const li = document.createElement('li');
756
+ const ts = new Date().toLocaleTimeString('en-GB', { hour12: false });
757
+ li.innerHTML = '<span class="rn">' + FUTH[(n - 1) % FUTH.length] + '</span>' +
758
+ '<span>RUNE №' + n + ' · OBSERVE — a draught was drawn from the well · ' + ts +
759
+ ' · carved — it cannot be un-carved' + (n > 12 ? ' · the stone is full — the ledger runs on' : '') + '</span>';
760
+ $('#rlList').appendChild(li);
761
+ const led = document.querySelector('.rune-ledger');
762
+ led.scrollTop = led.scrollHeight;
763
+ }
764
+ function drink(){
765
+ if(busy) return;
766
+ busy = true;
767
+ $('#drinkBtn').classList.add('busy');
768
+ draughts++;
769
+ $('#dCount').textContent = draughts;
770
+ const D = RM ? 60 : 620;
771
+ tween(0, 156, D, () => {
772
+ rippleAt(380, 800);
773
+ const eg = $('#eyeG');
774
+ eg.classList.add('peek');
775
+ setTimeout(() => eg.classList.remove('peek'), 1700);
776
+ showMemory(draughts);
777
+ carveRune(draughts);
778
+ appendLedger(draughts);
779
+ setTimeout(() => tween(156, 0, D + 100, () => {
780
+ busy = false;
781
+ $('#drinkBtn').classList.remove('busy');
782
+ }), 1250);
783
+ });
784
+ }
785
+ $('#drinkBtn').addEventListener('click', drink);
786
+
787
+ /* ---------- the drop from the roots never stops ---------- */
788
+ function spawnDrop(){
789
+ const d = document.createElementNS(NS, 'circle');
790
+ d.setAttribute('cx', 420); d.setAttribute('cy', 106); d.setAttribute('r', 2.1);
791
+ d.setAttribute('class', 'dropFall');
792
+ $('#drops').appendChild(d);
793
+ d.addEventListener('animationend', () => {
794
+ d.remove();
795
+ rippleAt(420, 800, .5);
796
+ }, { once: true });
797
+ }
798
+ if(!RM){
799
+ setTimeout(spawnDrop, 1800);
800
+ setInterval(() => { if(!document.hidden) spawnDrop(); }, 4600);
801
+ }
802
+
803
+ /* ---------- let the well speak ---------- */
804
+ $('#tourBtn').addEventListener('click', () => {
805
+ if(touring) return;
806
+ touring = true; pinned = null;
807
+ const btn = $('#tourBtn');
808
+ btn.classList.add('busy');
809
+ $('#tourLabel').textContent = 'it remembers…';
810
+ const order = ['root', 'water', 'eye', 'guardian', 'ledger'];
811
+ let i = 0;
812
+ (function step(){
813
+ if(i < order.length){
814
+ const p = order[i++];
815
+ light(p);
816
+ if(p === 'water') setTimeout(drink, 420);
817
+ setTimeout(step, 2650);
818
+ }else{
819
+ light(null); touring = false;
820
+ btn.classList.remove('busy');
821
+ $('#tourLabel').textContent = 'let the well speak';
822
+ }
823
+ })();
824
+ });
825
+
826
+ /* ---------- ambience: the cold under the earth ---------- */
827
+ (function(){
828
+ const cv = $('#gap'), ctx = cv.getContext('2d');
829
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
830
+ let W = 0, H = 0, mist = [], dust = [], motes = [], t = 0;
831
+ function size(){
832
+ W = innerWidth; H = innerHeight;
833
+ cv.width = W * dpr; cv.height = H * dpr;
834
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
835
+ }
836
+ function init(){
837
+ mist = []; dust = []; motes = [];
838
+ for(let i = 0; i < 5; i++) mist.push({ x: Math.random() * W, y: H * .2 + Math.random() * H * .7,
839
+ r: 170 + Math.random() * 230, v: (Math.random() - .5) * .12 });
840
+ for(let i = 0; i < 18; i++) dust.push({ x: Math.random() * W, y: Math.random() * H,
841
+ r: .5 + Math.random() * 1, v: .12 + Math.random() * .22, ph: Math.random() * 6.28, sp: .3 + Math.random() * .5 });
842
+ for(let i = 0; i < 4; i++) motes.push({ x: W * (.3 + Math.random() * .4), y: H * (.7 + Math.random() * .3),
843
+ r: .8 + Math.random() * .8, v: .1 + Math.random() * .12, ph: Math.random() * 6.28, sp: .5 + Math.random() * .5 });
844
+ }
845
+ function frame(){
846
+ t += .016; ctx.clearRect(0, 0, W, H);
847
+ for(const m of mist){
848
+ m.x += m.v;
849
+ if(m.x < -m.r) m.x = W + m.r;
850
+ if(m.x > W + m.r) m.x = -m.r;
851
+ const g = ctx.createRadialGradient(m.x, m.y, 0, m.x, m.y, m.r);
852
+ g.addColorStop(0, 'rgba(96,110,128,.045)');
853
+ g.addColorStop(1, 'rgba(0,0,0,0)');
854
+ ctx.fillStyle = g;
855
+ ctx.beginPath(); ctx.arc(m.x, m.y, m.r, 0, 6.283); ctx.fill();
856
+ }
857
+ for(const d of dust){
858
+ d.y += d.v;
859
+ d.x += Math.sin(t * d.sp + d.ph) * .12;
860
+ if(d.y > H + 6){ d.y = -6; d.x = Math.random() * W; }
861
+ ctx.fillStyle = 'rgba(176,166,138,.07)';
862
+ ctx.beginPath(); ctx.arc(d.x, d.y, d.r, 0, 6.283); ctx.fill();
863
+ }
864
+ for(const m of motes){
865
+ m.y -= m.v;
866
+ m.x += Math.sin(t * m.sp + m.ph) * .1;
867
+ if(m.y < H * .5){ m.y = H * (.85 + Math.random() * .15); m.x = W * (.3 + Math.random() * .4); }
868
+ const a = .05 + .13 * (.5 + .5 * Math.sin(t * m.sp * 2 + m.ph));
869
+ ctx.fillStyle = `rgba(201,151,63,${a.toFixed(3)})`;
870
+ ctx.beginPath(); ctx.arc(m.x, m.y, m.r, 0, 6.283); ctx.fill();
871
+ }
872
+ }
873
+ addEventListener('resize', () => { size(); init(); if(RM) frame(); });
874
+ size(); init();
875
+ if(RM) frame(); else (function loop(){ frame(); requestAnimationFrame(loop); })();
876
+ })();
877
+ })();
878
+ </script>
879
+ </body>
880
+ </html>