@conduction/docusaurus-preset 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/MISSING_COMPONENTS.md +109 -0
  2. package/README.md +171 -0
  3. package/package.json +59 -0
  4. package/src/components/AgentTrace/AgentTrace.jsx +128 -0
  5. package/src/components/AgentTrace/AgentTrace.module.css +115 -0
  6. package/src/components/AppMock/AppMock.jsx +86 -0
  7. package/src/components/AppMock/AppMock.module.css +629 -0
  8. package/src/components/AppMock/variants/DeciDeskMock.jsx +71 -0
  9. package/src/components/AppMock/variants/DocuDeskMock.jsx +69 -0
  10. package/src/components/AppMock/variants/LarpingAppMock.jsx +59 -0
  11. package/src/components/AppMock/variants/MyDashBiMock.jsx +135 -0
  12. package/src/components/AppMock/variants/MyDashMock.jsx +96 -0
  13. package/src/components/AppMock/variants/MyDashTilesMock.jsx +103 -0
  14. package/src/components/AppMock/variants/MyDashWidgetsMock.jsx +123 -0
  15. package/src/components/AppMock/variants/NLDesignMock.jsx +70 -0
  16. package/src/components/AppMock/variants/OpenCatalogiMock.jsx +61 -0
  17. package/src/components/AppMock/variants/OpenConnectorMock.jsx +83 -0
  18. package/src/components/AppMock/variants/OpenRegisterMock.jsx +100 -0
  19. package/src/components/AppMock/variants/OpenWooMock.jsx +61 -0
  20. package/src/components/AppMock/variants/PipelinQMock.jsx +88 -0
  21. package/src/components/AppMock/variants/ProcestMock.jsx +87 -0
  22. package/src/components/AppMock/variants/SoftwareCatalogMock.jsx +71 -0
  23. package/src/components/AppMock/variants/ZaakAfhandelAppMock.jsx +71 -0
  24. package/src/components/AppsGrid/AppsGrid.jsx +84 -0
  25. package/src/components/AppsGrid/AppsGrid.module.css +46 -0
  26. package/src/components/AppsPreview/AppsPreview.jsx +85 -0
  27. package/src/components/AppsPreview/AppsPreview.module.css +128 -0
  28. package/src/components/Clients/Clients.jsx +205 -0
  29. package/src/components/Clients/Clients.module.css +166 -0
  30. package/src/components/ComposeBlock/ComposeBlock.jsx +70 -0
  31. package/src/components/ComposeBlock/ComposeBlock.module.css +74 -0
  32. package/src/components/ConductionBg/ConductionBg.jsx +150 -0
  33. package/src/components/ConductionBg/ConductionBg.module.css +41 -0
  34. package/src/components/ContentCard/ContentCard.jsx +126 -0
  35. package/src/components/ContentCard/ContentCard.module.css +84 -0
  36. package/src/components/ContentDetailHero/ContentDetailHero.jsx +136 -0
  37. package/src/components/ContentDetailHero/ContentDetailHero.module.css +96 -0
  38. package/src/components/ContentTypeFilter/ContentTypeFilter.jsx +103 -0
  39. package/src/components/ContentTypeFilter/ContentTypeFilter.module.css +60 -0
  40. package/src/components/ContentTypeFilter/contentTypes.js +58 -0
  41. package/src/components/CookieCli/CookieCli.jsx +223 -0
  42. package/src/components/CookieCli/CookieCli.module.css +166 -0
  43. package/src/components/CtaBanner/CtaBanner.jsx +61 -0
  44. package/src/components/CtaBanner/CtaBanner.module.css +65 -0
  45. package/src/components/DetailHero/DetailHero.jsx +143 -0
  46. package/src/components/DetailHero/DetailHero.module.css +154 -0
  47. package/src/components/Diagrams/Diagrams.jsx +148 -0
  48. package/src/components/EmployeeCard/EmployeeCard.jsx +127 -0
  49. package/src/components/EmployeeCard/EmployeeCard.module.css +144 -0
  50. package/src/components/ExternalAppShelf/ExternalAppShelf.jsx +61 -0
  51. package/src/components/ExternalAppShelf/ExternalAppShelf.module.css +90 -0
  52. package/src/components/FAQ/FAQ.jsx +42 -0
  53. package/src/components/FAQ/FAQ.module.css +74 -0
  54. package/src/components/FacetedFilters/FacetedFilters.jsx +125 -0
  55. package/src/components/FacetedFilters/FacetedFilters.module.css +133 -0
  56. package/src/components/FeatureGrid/FeatureGrid.jsx +94 -0
  57. package/src/components/FeatureGrid/FeatureGrid.module.css +114 -0
  58. package/src/components/FeatureList/FeatureList.jsx +54 -0
  59. package/src/components/FeatureList/FeatureList.module.css +52 -0
  60. package/src/components/FeaturedCard/FeaturedCard.jsx +101 -0
  61. package/src/components/FeaturedCard/FeaturedCard.module.css +98 -0
  62. package/src/components/GameModal/GameModal.jsx +197 -0
  63. package/src/components/GameModal/GameModal.module.css +184 -0
  64. package/src/components/Hero/Hero.jsx +101 -0
  65. package/src/components/Hero/Hero.module.css +95 -0
  66. package/src/components/HexBackground/HexBackground.jsx +56 -0
  67. package/src/components/HexBackground/HexBackground.module.css +73 -0
  68. package/src/components/HexNetwork/HexNetwork.jsx +141 -0
  69. package/src/components/HexNetwork/HexNetwork.module.css +187 -0
  70. package/src/components/HexRain/HexRain.jsx +81 -0
  71. package/src/components/HowSteps/HowSteps.jsx +57 -0
  72. package/src/components/HowSteps/HowSteps.module.css +52 -0
  73. package/src/components/ManagedCommonGround/ManagedCommonGround.jsx +78 -0
  74. package/src/components/ManagedCommonGround/ManagedCommonGround.module.css +16 -0
  75. package/src/components/NewsletterCta/NewsletterCta.jsx +83 -0
  76. package/src/components/NewsletterCta/NewsletterCta.module.css +103 -0
  77. package/src/components/PairCard/PairCard.jsx +58 -0
  78. package/src/components/PairCard/PairCard.module.css +54 -0
  79. package/src/components/PartnerCard/PartnerCard.jsx +130 -0
  80. package/src/components/PartnerCard/PartnerCard.module.css +198 -0
  81. package/src/components/PartnerDirectory/PartnerDirectory.jsx +122 -0
  82. package/src/components/PartnerDirectory/PartnerDirectory.module.css +25 -0
  83. package/src/components/PartnerSidecard/PartnerSidecard.jsx +116 -0
  84. package/src/components/PartnerSidecard/PartnerSidecard.module.css +185 -0
  85. package/src/components/Pipeline/Pipeline.jsx +198 -0
  86. package/src/components/Pipeline/Pipeline.module.css +206 -0
  87. package/src/components/PlatformDiagram/PlatformDiagram.jsx +110 -0
  88. package/src/components/PlatformOverview/PlatformOverview.jsx +68 -0
  89. package/src/components/PlatformOverview/PlatformOverview.module.css +71 -0
  90. package/src/components/ReferenceCard/ReferenceCard.jsx +44 -0
  91. package/src/components/ReferenceCard/ReferenceCard.module.css +57 -0
  92. package/src/components/RelatedPosts/RelatedPosts.jsx +58 -0
  93. package/src/components/RelatedPosts/RelatedPosts.module.css +51 -0
  94. package/src/components/RotatingCards/RotatingCards.jsx +98 -0
  95. package/src/components/RotatingCards/RotatingCards.module.css +153 -0
  96. package/src/components/Showcase/Showcase.jsx +129 -0
  97. package/src/components/Showcase/Showcase.module.css +168 -0
  98. package/src/components/SolutionCard/SolutionCard.jsx +83 -0
  99. package/src/components/SolutionCard/SolutionCard.module.css +99 -0
  100. package/src/components/StatsStrip/StatsStrip.jsx +38 -0
  101. package/src/components/StatsStrip/StatsStrip.module.css +53 -0
  102. package/src/components/WidgetShelf/WidgetShelf.jsx +67 -0
  103. package/src/components/WidgetShelf/WidgetShelf.module.css +73 -0
  104. package/src/components/index.js +96 -0
  105. package/src/components/primitives/AuthorByline.jsx +85 -0
  106. package/src/components/primitives/AuthorByline.module.css +57 -0
  107. package/src/components/primitives/BrandCitation.jsx +71 -0
  108. package/src/components/primitives/Button.jsx +46 -0
  109. package/src/components/primitives/Button.module.css +88 -0
  110. package/src/components/primitives/Card.jsx +42 -0
  111. package/src/components/primitives/Card.module.css +42 -0
  112. package/src/components/primitives/Eyebrow.jsx +37 -0
  113. package/src/components/primitives/Eyebrow.module.css +19 -0
  114. package/src/components/primitives/HexBullet.jsx +37 -0
  115. package/src/components/primitives/HexBullet.module.css +16 -0
  116. package/src/components/primitives/HexThumbnail.jsx +70 -0
  117. package/src/components/primitives/HexThumbnail.module.css +45 -0
  118. package/src/components/primitives/Pill.jsx +42 -0
  119. package/src/components/primitives/Pill.module.css +30 -0
  120. package/src/components/primitives/Section.jsx +51 -0
  121. package/src/components/primitives/Section.module.css +31 -0
  122. package/src/components/primitives/SectionHead.jsx +36 -0
  123. package/src/components/primitives/SectionHead.module.css +43 -0
  124. package/src/components/primitives/index.js +22 -0
  125. package/src/css/brand.css +158 -0
  126. package/src/css/tokens.css +12 -0
  127. package/src/data/app-downloads.js +42 -0
  128. package/src/diagrams/README.md +74 -0
  129. package/src/diagrams/cn-domain-tree.js +105 -0
  130. package/src/diagrams/cn-hex-prism.js +163 -0
  131. package/src/diagrams/cn-hex.js +181 -0
  132. package/src/diagrams/cn-honeycomb-bg.js +135 -0
  133. package/src/diagrams/cn-pipeline.js +150 -0
  134. package/src/diagrams/cn-platform.js +156 -0
  135. package/src/diagrams/cn-side-box.js +104 -0
  136. package/src/diagrams/index.js +28 -0
  137. package/src/index.js +183 -0
  138. package/src/theme/Footer/index.jsx +516 -0
  139. package/src/theme/MDXPage/index.jsx +134 -0
  140. package/src/theme/Navbar/index.jsx +120 -0
  141. package/src/theme/Navbar/styles.module.css +114 -0
  142. package/src/theme/brand.jsx +63 -0
  143. package/src/theme.js +45 -0
  144. package/src/utils/lazyScript.js +37 -0
  145. package/static/img/favicon.svg +14 -0
  146. package/static/img/honeycomb-scatter.svg +23 -0
  147. package/static/img/honeycomb-watermark.svg +108 -0
  148. package/static/img/logo-dark.svg +11 -0
  149. package/static/img/logo.svg +14 -0
  150. package/static/img/nextcloud-logo.svg +5 -0
  151. package/static/lib/canal-footer.css +418 -0
  152. package/static/lib/canal-footer.js +499 -0
  153. package/static/lib/clients-flow.js +317 -0
  154. package/static/lib/conduction-bg.css +50 -0
  155. package/static/lib/conduction-bg.js +122 -0
  156. package/static/lib/hex-rain.css +128 -0
  157. package/static/lib/hex-rain.js +284 -0
  158. package/static/lib/kade-cyclist.css +264 -0
  159. package/static/lib/kade-cyclist.js +420 -0
  160. package/static/lib/logo-memory.css +219 -0
  161. package/static/lib/logo-memory.js +540 -0
  162. package/static/lib/platform-diagram.css +458 -0
  163. package/static/lib/platform-diagram.js +414 -0
@@ -0,0 +1,317 @@
1
+ /* Clients Flow: continuous-spawn marquee runtime for the Clients hex
2
+ wall. Replaces the old CSS-animated track-with-duplicate approach,
3
+ which had a one-pixel seam at the wrap boundary. Each row is a
4
+ simple relative container; the runtime spawns absolutely-positioned
5
+ <a class="hex"> children on the right, drifts them left at a per-row
6
+ speed via translateX, and removes them once they leave the left
7
+ edge. Logos are picked at random from the supplied client pool,
8
+ avoiding immediate repeats so the same shield doesn't keep flashing
9
+ in one row.
10
+
11
+ Hooks:
12
+ - data-memory-marquee on the marquee element (also serves as the
13
+ Logo Memory game trigger surface)
14
+ - data-clients='[{"src":"...","name":"..."}, ...]' on the marquee
15
+ - data-hex-class on the marquee (optional) — class name for spawned
16
+ hex anchors. CSS Modules hashes the React build's class to
17
+ something like hex_QXzL; pass that through. Falls back to "hex"
18
+ which the preview HTML uses literally.
19
+ - data-row-stagger on a row (optional) — px offset added to the
20
+ row's first hex x. Used by row 2 to slot half-hex into the
21
+ honeycomb stagger between row 1 and row 3.
22
+ - data-row-speed on a row (optional) — drift speed in px/sec.
23
+ Defaults to 50; row 2 defaults to 58 and row 3 to 46 for slight
24
+ visual desync.
25
+
26
+ Public API:
27
+ window.ConductionClientsFlow.hydrate() — find + initialise marquees
28
+ window.ConductionClientsFlow.pause() — freeze drift (used by Logo
29
+ Memory while a game is in
30
+ progress)
31
+ window.ConductionClientsFlow.resume() — un-freeze
32
+ window.ConductionClientsFlow.reset() — clear all hex children
33
+ and re-fill from scratch.
34
+ Logo Memory calls this on
35
+ game tear-down so the
36
+ marquee comes back fresh.
37
+ */
38
+ (function () {
39
+ const DEFAULT_SPEED = 50;
40
+ /* All rows use the same default speed so honeycomb alignment is
41
+ preserved (rows 1 and 3 stay column-aligned, row 2 stays at the
42
+ half-hex offset). Per-row data-row-speed still lets a host page
43
+ override this if that's the look they want. The "more dynamic"
44
+ feel comes from random-logo selection on each spawn, not from
45
+ per-row drift desync. */
46
+ const DEFAULT_ROW_SPEEDS = [50, 50, 50];
47
+ const RECENT_REPEAT_AVOID = 5;
48
+ const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
49
+
50
+ /* All running marquees. Each entry holds the per-row state for that
51
+ marquee plus the rAF id so global pause/resume can address them. */
52
+ const REGISTRY = [];
53
+ let rafId = null;
54
+
55
+ function readPx(value, fallback) {
56
+ const n = parseFloat(value);
57
+ return isFinite(n) && n >= 0 ? n : fallback;
58
+ }
59
+
60
+ function pickLogo(state, clients) {
61
+ let pick;
62
+ let attempts = 0;
63
+ do {
64
+ pick = clients[(Math.random() * clients.length) | 0];
65
+ attempts++;
66
+ } while (state.recent.includes(pick.src) && attempts < 10);
67
+ state.recent.push(pick.src);
68
+ if (state.recent.length > RECENT_REPEAT_AVOID) state.recent.shift();
69
+ return pick;
70
+ }
71
+
72
+ function spawnHex(state, baseX, scrollPx) {
73
+ /* baseX is the hex's "world" x at scrollPx=0. Current visible
74
+ position = baseX - scrollPx. Storing baseX lets every tick
75
+ recompute current x from the shared scroll accumulator without
76
+ per-hex per-row drift accumulation. */
77
+ const logo = pickLogo(state, state.clients);
78
+ const x = baseX - (scrollPx || 0);
79
+ const a = document.createElement('a');
80
+ a.className = state.hexClass;
81
+ a.href = '#';
82
+ a.setAttribute('aria-label', logo.name);
83
+ a.style.position = 'absolute';
84
+ a.style.top = '0';
85
+ a.style.left = '0';
86
+ a.style.transform = 'translateX(' + x + 'px)';
87
+ a.style.willChange = 'transform';
88
+ const img = document.createElement('img');
89
+ img.src = logo.src;
90
+ img.alt = logo.name;
91
+ img.title = logo.name;
92
+ img.loading = 'lazy';
93
+ if (state.hexLogoClass) img.className = state.hexLogoClass;
94
+ a.appendChild(img);
95
+ a.addEventListener('click', function (e) { e.preventDefault(); });
96
+ state.row.appendChild(a);
97
+ state.hexes.push({ el: a, baseX: baseX, x: x });
98
+ return a;
99
+ }
100
+
101
+ function initialFill(state) {
102
+ /* Pre-populate the row with hexes from its stagger offset out
103
+ past the right edge, so users never see an empty row. baseX
104
+ starts at stagger; advance by pitch and spawn while still in
105
+ the right-edge spawn zone. */
106
+ const containerWidth = state.row.getBoundingClientRect().width;
107
+ let baseX = state.stagger;
108
+ while (baseX < containerWidth + state.hexW) {
109
+ spawnHex(state, baseX, 0);
110
+ baseX += state.pitch;
111
+ }
112
+ state.cursorBaseX = baseX;
113
+ }
114
+
115
+ function tickMarquee(marqueeState, scrollPx) {
116
+ /* `scrollPx` is the cumulative drift since the marquee started, in
117
+ pixels, shared across all rows. Each hex stores its spawn-time
118
+ baseline (h.baseX = the x at scroll = 0); current position is
119
+ baseX - scrollPx. Going via this single shared accumulator
120
+ guarantees rows stay perfectly in lockstep — there is no per-
121
+ row dt to diverge on. */
122
+ marqueeState.rows.forEach(function (state) {
123
+ if (state.paused) return;
124
+ const containerWidth = state.row.getBoundingClientRect().width;
125
+ /* Update every hex's transform from its baseline. */
126
+ for (let i = 0; i < state.hexes.length; i++) {
127
+ const h = state.hexes[i];
128
+ h.x = h.baseX - scrollPx;
129
+ h.el.style.transform = 'translateX(' + h.x + 'px)';
130
+ }
131
+ /* Cull anything that fully left the viewport on the left. */
132
+ for (let i = state.hexes.length - 1; i >= 0; i--) {
133
+ if (state.hexes[i].x + state.hexW < -16) {
134
+ state.hexes[i].el.remove();
135
+ state.hexes.splice(i, 1);
136
+ }
137
+ }
138
+ /* Cursor is also baseline-relative: cursor world position =
139
+ cursorBaseX - scrollPx. Spawn while it falls inside the spawn
140
+ zone (the right-edge buffer). */
141
+ let safety = 0;
142
+ while ((state.cursorBaseX - scrollPx) < containerWidth + state.hexW && safety < 50) {
143
+ const newBaseX = state.cursorBaseX;
144
+ spawnHex(state, newBaseX, scrollPx);
145
+ state.cursorBaseX += state.pitch;
146
+ safety++;
147
+ }
148
+ });
149
+ }
150
+
151
+ /* Single shared scroll accumulator for all marquees + rows. Stays
152
+ constant while paused, advances by speed*dt while running. Each
153
+ hex's visible position is baseX - scrollPx, so all hexes (any
154
+ row, any marquee) move in lockstep. */
155
+ let scrollStart = null; // ts when the current run began
156
+ let scrollAccum = 0; // total scroll px captured at last pause
157
+ let running = true;
158
+
159
+ function currentScroll(timestamp) {
160
+ const speed = REGISTRY[0]?.rows[0]?.speed ?? DEFAULT_SPEED;
161
+ if (!running) return scrollAccum;
162
+ if (scrollStart === null) scrollStart = timestamp;
163
+ return scrollAccum + (timestamp - scrollStart) * speed / 1000;
164
+ }
165
+
166
+ function tick(timestamp) {
167
+ const scrollPx = currentScroll(timestamp);
168
+ REGISTRY.forEach(function (m) { tickMarquee(m, scrollPx); });
169
+ rafId = requestAnimationFrame(tick);
170
+ }
171
+
172
+ function startLoop() {
173
+ if (rafId !== null) return;
174
+ lastTimestamp = null;
175
+ rafId = requestAnimationFrame(tick);
176
+ }
177
+
178
+ function hydrateMarquee(marquee) {
179
+ if (marquee.dataset.flowHydrated === '1') return;
180
+ marquee.dataset.flowHydrated = '1';
181
+
182
+ let clients;
183
+ /* Two ways to pass the client pool: a JSON string in
184
+ data-clients (compact, used by the React build) or an inner
185
+ <script type="application/json" data-clients-source> (less
186
+ quote-noise, used by the preview HTML). */
187
+ try { clients = JSON.parse(marquee.dataset.clients || '[]'); }
188
+ catch (e) { clients = []; }
189
+ if (!clients.length) {
190
+ const inline = marquee.querySelector('script[type="application/json"][data-clients-source]');
191
+ if (inline) {
192
+ try { clients = JSON.parse(inline.textContent); }
193
+ catch (e) { clients = []; }
194
+ }
195
+ }
196
+ if (!clients || !clients.length) return;
197
+
198
+ const hexClass = marquee.dataset.hexClass || 'hex';
199
+ const hexLogoClass = marquee.dataset.hexLogoClass || 'hexLogo';
200
+ const hexW = readPx(getComputedStyle(marquee).getPropertyValue('--hex-w'), 120);
201
+ const gap = readPx(getComputedStyle(marquee).getPropertyValue('--gap'), 8);
202
+ const pitch = hexW + gap;
203
+
204
+ const rowEls = Array.from(marquee.querySelectorAll('[data-flow-row]'));
205
+ if (!rowEls.length) return;
206
+
207
+ const rows = rowEls.map(function (row, idx) {
208
+ const stagger = readPx(row.dataset.rowStagger, idx === 1 ? pitch / 2 : 0);
209
+ const speedAttr = parseFloat(row.dataset.rowSpeed);
210
+ const speed = isFinite(speedAttr) && speedAttr > 0
211
+ ? speedAttr
212
+ : (DEFAULT_ROW_SPEEDS[idx] || DEFAULT_SPEED);
213
+ return {
214
+ row: row,
215
+ stagger: stagger,
216
+ speed: reduceMotion ? 0 : speed,
217
+ hexW: hexW,
218
+ pitch: pitch,
219
+ clients: clients,
220
+ hexClass: hexClass,
221
+ hexLogoClass: hexLogoClass,
222
+ hexes: [],
223
+ cursorBaseX: stagger,
224
+ recent: [],
225
+ paused: false,
226
+ };
227
+ });
228
+
229
+ const marqueeState = { marquee: marquee, rows: rows };
230
+ rows.forEach(initialFill);
231
+ REGISTRY.push(marqueeState);
232
+ if (!reduceMotion) startLoop();
233
+ }
234
+
235
+ function hydrate() {
236
+ document.querySelectorAll('[data-memory-marquee]').forEach(hydrateMarquee);
237
+ }
238
+
239
+ function pause() {
240
+ if (!running) return;
241
+ /* Capture scroll into the accumulator and FULLY halt the rAF.
242
+ Setting paused flags isn't enough — Logo Memory snapshots and
243
+ lifts hex positions, and any subsequent tick that reads/writes
244
+ a transform on a now-lifted hex would corrupt its placement. */
245
+ scrollAccum = currentScroll(performance.now());
246
+ scrollStart = null;
247
+ running = false;
248
+ if (rafId !== null) {
249
+ cancelAnimationFrame(rafId);
250
+ rafId = null;
251
+ }
252
+ REGISTRY.forEach(function (m) {
253
+ m.rows.forEach(function (r) { r.paused = true; });
254
+ });
255
+ }
256
+
257
+ function resume() {
258
+ if (running) return;
259
+ scrollStart = null; // first tick after resume re-anchors
260
+ running = true;
261
+ REGISTRY.forEach(function (m) {
262
+ m.rows.forEach(function (r) { r.paused = false; });
263
+ });
264
+ if (!reduceMotion) startLoop();
265
+ }
266
+
267
+ function reset() {
268
+ /* Halt rAF before mutating state so a stray tick can't write to
269
+ elements we're about to remove. */
270
+ if (rafId !== null) {
271
+ cancelAnimationFrame(rafId);
272
+ rafId = null;
273
+ }
274
+ scrollAccum = 0;
275
+ scrollStart = null;
276
+ running = true;
277
+ REGISTRY.forEach(function (m) {
278
+ m.rows.forEach(function (r) {
279
+ r.hexes.forEach(function (h) { h.el.remove(); });
280
+ r.hexes.length = 0;
281
+ r.recent.length = 0;
282
+ r.cursorBaseX = r.stagger;
283
+ r.paused = false;
284
+ initialFill(r);
285
+ });
286
+ });
287
+ if (!reduceMotion) startLoop();
288
+ }
289
+
290
+ window.ConductionClientsFlow = window.ConductionClientsFlow || {};
291
+ window.ConductionClientsFlow.hydrate = hydrate;
292
+ window.ConductionClientsFlow.pause = pause;
293
+ window.ConductionClientsFlow.resume = resume;
294
+ window.ConductionClientsFlow.reset = reset;
295
+ window.ConductionClientsFlow._inspect = function () {
296
+ return REGISTRY.map(function (m) {
297
+ return {
298
+ rows: m.rows.map(function (r) {
299
+ return {
300
+ stagger: r.stagger,
301
+ speed: r.speed,
302
+ cursorBaseX: r.cursorBaseX,
303
+ hexCount: r.hexes.length,
304
+ baseXs: r.hexes.map(function (h) { return h.baseX; }),
305
+ currentXs: r.hexes.map(function (h) { return Math.round(h.x * 100) / 100; }),
306
+ };
307
+ }),
308
+ };
309
+ });
310
+ };
311
+
312
+ if (document.readyState === 'loading') {
313
+ document.addEventListener('DOMContentLoaded', hydrate);
314
+ } else {
315
+ hydrate();
316
+ }
317
+ })();
@@ -0,0 +1,50 @@
1
+ /* ============================================================
2
+ Conduction background — reusable parallax honeycomb depth field
3
+ ------------------------------------------------------------
4
+ Drop <div class="conduction-bg" aria-hidden="true"></div> as
5
+ the first child of any position:relative + overflow:hidden
6
+ container. The hydrator (conduction-bg.js) generates
7
+ depth-tiered hexes that parallax on scroll and slowly
8
+ breathe in opacity.
9
+
10
+ Themes (set by class on .conduction-bg):
11
+ (default) white hexes — for cobalt / cobalt-800 surfaces
12
+ .conduction-bg.on-light cobalt hexes — for white / cobalt-50 surfaces
13
+
14
+ Per huisstijl: hexes are always pointy-top, never rotated.
15
+ Requires tokens.css to provide --hex-pointy-top, --c-blue-cobalt,
16
+ and --c-orange-knvb.
17
+ ============================================================ */
18
+ .conduction-bg {
19
+ position: absolute;
20
+ inset: 0;
21
+ overflow: hidden;
22
+ pointer-events: none;
23
+ --cbg-hex: #ffffff;
24
+ --cbg-accent: var(--c-orange-knvb);
25
+ }
26
+ .conduction-bg.on-light {
27
+ --cbg-hex: var(--c-blue-cobalt);
28
+ --cbg-accent: var(--c-orange-knvb);
29
+ }
30
+ .conduction-bg .hex {
31
+ position: absolute;
32
+ clip-path: var(--hex-pointy-top);
33
+ background: var(--cbg-hex);
34
+ will-change: transform, opacity;
35
+ transform: translate(-50%, -50%);
36
+ animation: conduction-hex-breathe var(--cbg-dur, 14s) ease-in-out var(--cbg-delay, 0s) infinite;
37
+ }
38
+ .conduction-bg .hex.accent {
39
+ background: var(--cbg-accent);
40
+ }
41
+ @keyframes conduction-hex-breathe {
42
+ 0%, 100% { opacity: var(--cbg-opa-min, 0.04); }
43
+ 50% { opacity: var(--cbg-opa-max, 0.18); }
44
+ }
45
+ @media (prefers-reduced-motion: reduce) {
46
+ .conduction-bg .hex {
47
+ animation: none;
48
+ opacity: var(--cbg-opa-max, 0.12);
49
+ }
50
+ }
@@ -0,0 +1,122 @@
1
+ /* Conduction background — parallax honeycomb hydrator.
2
+
3
+ Walks every .conduction-bg in the document, injects depth-tiered hex
4
+ spans, and wires them up to scroll-based parallax. Smaller hexes have
5
+ smaller depth → less translate per scroll-pixel → read as further away.
6
+
7
+ Per huisstijl: hexes are pointy-top only, never rotated.
8
+
9
+ Hosts opt in by adding <div class="conduction-bg"></div> inside any
10
+ position:relative + overflow:hidden container, plus this script tag
11
+ and conduction-bg.css. The script auto-runs on DOMContentLoaded and
12
+ is idempotent — calling init() twice on the same element is a no-op.
13
+
14
+ Optional dataset hooks on .conduction-bg:
15
+ data-count="N" — override default hex count (default 18)
16
+ */
17
+ (function () {
18
+ const TIERS = [
19
+ { d: 0.10, sMin: 32, sMax: 56, oMin: 0.03, oMax: 0.10, w: 5 },
20
+ { d: 0.25, sMin: 56, sMax: 96, oMin: 0.04, oMax: 0.13, w: 4 },
21
+ { d: 0.45, sMin: 96, sMax: 150, oMin: 0.05, oMax: 0.16, w: 3 },
22
+ { d: 0.65, sMin: 150, sMax: 220, oMin: 0.07, oMax: 0.18, w: 2 },
23
+ { d: 0.85, sMin: 220, sMax: 300, oMin: 0.08, oMax: 0.20, w: 1 },
24
+ ];
25
+ const PARALLAX_FACTOR = 0.4;
26
+ const TIER_TOTAL = TIERS.reduce((s, t) => s + t.w, 0);
27
+
28
+ const rand = (a, b) => Math.random() * (b - a) + a;
29
+ const pickTier = () => {
30
+ let r = Math.random() * TIER_TOTAL;
31
+ for (const t of TIERS) { if ((r -= t.w) <= 0) return t; }
32
+ return TIERS[0];
33
+ };
34
+
35
+ function hydrate(bg) {
36
+ if (bg.dataset.cbgHydrated === '1') return;
37
+ bg.dataset.cbgHydrated = '1';
38
+
39
+ const host = bg.parentElement;
40
+ if (!host) return;
41
+ const onLight = bg.classList.contains('on-light');
42
+ /* Cobalt-on-white reads stronger per opacity-unit than white-on-cobalt,
43
+ so scale back the on-light range to keep the field a soft veil. */
44
+ const opaScale = onLight ? 0.55 : 1.0;
45
+ const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
46
+
47
+ const hexCount = parseInt(bg.dataset.count, 10) || 18;
48
+ const accentIndex = Math.floor(hexCount / 2);
49
+
50
+ const hexes = [];
51
+ for (let i = 0; i < hexCount; i++) {
52
+ const tier = pickTier();
53
+ const size = rand(tier.sMin, tier.sMax);
54
+ const isAccent = (i === accentIndex);
55
+ const hex = document.createElement('span');
56
+ hex.className = isAccent ? 'hex accent' : 'hex';
57
+ hex.style.width = size + 'px';
58
+ hex.style.height = (size * 1.1547) + 'px';
59
+ hex.style.left = rand(-5, 105) + '%';
60
+ hex.style.top = rand(-25, 125) + '%';
61
+ hex.style.setProperty('--cbg-opa-min', (tier.oMin * opaScale).toFixed(3));
62
+ hex.style.setProperty(
63
+ '--cbg-opa-max',
64
+ ((isAccent ? Math.min(tier.oMax + 0.05, 0.28) : tier.oMax) * opaScale).toFixed(3)
65
+ );
66
+ hex.style.setProperty('--cbg-dur', rand(11, 18).toFixed(1) + 's');
67
+ hex.style.setProperty('--cbg-delay', rand(-18, 0).toFixed(1) + 's');
68
+ hex.dataset.depth = tier.d;
69
+ bg.appendChild(hex);
70
+ hexes.push(hex);
71
+ }
72
+
73
+ if (reduceMotion) return;
74
+
75
+ let visible = false;
76
+ let ticking = false;
77
+
78
+ const update = () => {
79
+ const rect = host.getBoundingClientRect();
80
+ const offset = (rect.top + rect.height / 2) - window.innerHeight / 2;
81
+ for (const h of hexes) {
82
+ const d = parseFloat(h.dataset.depth);
83
+ const ty = -offset * d * PARALLAX_FACTOR;
84
+ h.style.transform = `translate(-50%, -50%) translateY(${ty.toFixed(1)}px)`;
85
+ }
86
+ ticking = false;
87
+ };
88
+
89
+ const onScroll = () => {
90
+ if (!visible || ticking) return;
91
+ ticking = true;
92
+ requestAnimationFrame(update);
93
+ };
94
+
95
+ const io = new IntersectionObserver(([entry]) => {
96
+ visible = entry.isIntersecting;
97
+ if (visible) update();
98
+ }, { rootMargin: '300px 0px' });
99
+ io.observe(host);
100
+
101
+ window.addEventListener('scroll', onScroll, { passive: true });
102
+ window.addEventListener('resize', onScroll, { passive: true });
103
+ update();
104
+ }
105
+
106
+ function init() {
107
+ /* hydrate() has its own dataset.cbgHydrated guard so calling it
108
+ repeatedly across SPA route changes is a no-op for already-
109
+ mounted nodes. */
110
+ document.querySelectorAll('.conduction-bg').forEach(hydrate);
111
+ }
112
+
113
+ /* SPA-friendly: expose so React/MDX components can re-trigger after
114
+ route changes without double-hydrating already-hydrated nodes. */
115
+ window.ConductionBg = {hydrate: init};
116
+
117
+ if (document.readyState === 'loading') {
118
+ document.addEventListener('DOMContentLoaded', init);
119
+ } else {
120
+ init();
121
+ }
122
+ })();
@@ -0,0 +1,128 @@
1
+
2
+ /* ===== Hex rain, slow vertical fall of icon hexes =====
3
+ The IIFE at the end of the page hydrates this with icon-bearing
4
+ hexes that fall finite cycles (one pass each) and get garbage-
5
+ collected on `animationend` so the spawn loop can ramp without
6
+ stacking density. Each hex carries an app id 0-11 in dataset.app
7
+ and clicking it toggles that app's collection state in the game.
8
+ Always pointy-top per huisstijl. */
9
+ .hex-rain {
10
+ position: relative;
11
+ width: 100%;
12
+ height: 100%;
13
+ min-height: 520px;
14
+ overflow: hidden;
15
+ }
16
+ .hex-rain .h {
17
+ position: absolute;
18
+ top: 0;
19
+ left: var(--rain-x, 50%);
20
+ width: var(--rain-size, 80px);
21
+ height: calc(var(--rain-size, 80px) * 1.1547);
22
+ clip-path: var(--hex-pointy-top);
23
+ background: var(--c-blue-cobalt);
24
+ color: white;
25
+ display: flex; align-items: center; justify-content: center;
26
+ will-change: transform, opacity;
27
+ cursor: pointer;
28
+ animation: hex-rain-fall var(--rain-dur, 28s) linear var(--rain-delay, 0s) 1 forwards;
29
+ }
30
+ .hex-rain .h.alt { background: var(--c-cobalt-700); }
31
+ .hex-rain .h.accent { background: var(--c-orange-knvb); }
32
+ .hex-rain .h.clicking { pointer-events: none; }
33
+ .hex-rain .h svg {
34
+ width: 44%; height: 44%;
35
+ stroke: currentColor;
36
+ stroke-width: 1.6;
37
+ fill: none;
38
+ pointer-events: none;
39
+ }
40
+ @keyframes hex-rain-fall {
41
+ 0% { transform: translate(-50%, calc(-1 * var(--rain-size, 80px) - 24px)); opacity: 0; }
42
+ 8% { opacity: 1; }
43
+ 92% { opacity: 1; }
44
+ 100% { transform: translate(-50%, var(--rain-end, 600px)); opacity: 0; }
45
+ }
46
+ @media (prefers-reduced-motion: reduce) {
47
+ .hex-rain .h { animation: none; transform: translate(-50%, 50%); opacity: 1; }
48
+ }
49
+
50
+ /* Game HUD that fades in on first hex click. Counter (apps / 12),
51
+ timer (60s) and a row of 12 mini app-hex icons that light orange
52
+ as the player collects each app. Cobalt-on-white card per the
53
+ GameModal styling so the HUD reads cleanly over a falling-hex
54
+ background instead of fighting with white-on-cobalt shadows. */
55
+ .hex-rain .rain-hud {
56
+ position: absolute;
57
+ top: 14px; right: 14px;
58
+ z-index: 2;
59
+ pointer-events: none;
60
+ font-family: var(--conduction-typography-font-family-code);
61
+ color: var(--c-cobalt-900);
62
+ background: rgba(255, 255, 255, 0.94);
63
+ backdrop-filter: blur(4px);
64
+ border-radius: var(--radius-lg);
65
+ padding: 12px 14px;
66
+ box-shadow: 0 4px 16px -4px rgba(11, 32, 73, 0.18);
67
+ opacity: 0;
68
+ transform: translateY(-8px);
69
+ transition: opacity 240ms ease, transform 240ms ease;
70
+ text-align: right;
71
+ }
72
+ .hex-rain .rain-hud.active { opacity: 1; transform: translateY(0); }
73
+ .hex-rain .rain-hud .hud-top {
74
+ display: flex; gap: 22px; justify-content: flex-end;
75
+ }
76
+ .hex-rain .rain-hud .hud-block { text-align: right; }
77
+ .hex-rain .rain-hud .hud-num {
78
+ display: block;
79
+ font-size: 28px; font-weight: 700;
80
+ letter-spacing: -0.02em;
81
+ line-height: 1;
82
+ font-variant-numeric: tabular-nums;
83
+ color: var(--c-cobalt-900);
84
+ }
85
+ .hex-rain .rain-hud .hud-label {
86
+ display: block;
87
+ font-size: 9px;
88
+ letter-spacing: 0.16em;
89
+ text-transform: uppercase;
90
+ color: var(--c-cobalt-400);
91
+ margin-top: 4px;
92
+ }
93
+ .hex-rain .rain-hud .hud-block.hud-tick .hud-num { animation: rain-tick 420ms ease; }
94
+ .hex-rain .rain-hud .hud-block.hud-untick .hud-num { animation: rain-untick 420ms ease; }
95
+ @keyframes rain-tick { 0% { transform: scale(1); color: var(--c-cobalt-900); } 35% { transform: scale(1.4); color: var(--c-orange-knvb); } 100% { transform: scale(1); color: var(--c-cobalt-900); } }
96
+ @keyframes rain-untick { 0% { transform: scale(1); color: var(--c-cobalt-900); } 35% { transform: scale(0.85); color: #d64633; } 100% { transform: scale(1); color: var(--c-cobalt-900); } }
97
+ .hex-rain .rain-hud .hud-timer.urgent .hud-num {
98
+ color: #d64633;
99
+ animation: rain-pulse 800ms ease infinite;
100
+ }
101
+ @keyframes rain-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
102
+ .hex-rain .rain-hud .hud-icons {
103
+ display: flex; gap: 3px; margin-top: 12px; justify-content: flex-end;
104
+ }
105
+ .hex-rain .rain-hud .hud-icon {
106
+ width: 22px; height: 26px;
107
+ clip-path: var(--hex-pointy-top);
108
+ background: var(--c-cobalt-100);
109
+ display: inline-flex; align-items: center; justify-content: center;
110
+ color: var(--c-cobalt-400);
111
+ transition: background 220ms, color 220ms;
112
+ }
113
+ .hex-rain .rain-hud .hud-icon.collected {
114
+ background: var(--c-orange-knvb);
115
+ color: var(--c-cobalt-900);
116
+ }
117
+ .hex-rain .rain-hud .hud-icon.collected.just { animation: rain-icon-pop 380ms ease; }
118
+ @keyframes rain-icon-pop {
119
+ 0% { transform: scale(0.6); }
120
+ 60% { transform: scale(1.2); }
121
+ 100% { transform: scale(1); }
122
+ }
123
+ .hex-rain .rain-hud .hud-icon svg {
124
+ width: 60%; height: 60%;
125
+ stroke: currentColor; stroke-width: 1.7; fill: none;
126
+ stroke-linecap: round; stroke-linejoin: round;
127
+ }
128
+