eyeling 1.16.3 → 1.16.4

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 (45) hide show
  1. package/README.md +0 -1
  2. package/package.json +2 -3
  3. package/arctifacts/README.md +0 -59
  4. package/arctifacts/ackermann.html +0 -678
  5. package/arctifacts/auroracare.html +0 -1297
  6. package/arctifacts/bike-trip.html +0 -752
  7. package/arctifacts/binomial-theorem.html +0 -631
  8. package/arctifacts/bmi.html +0 -511
  9. package/arctifacts/building-performance.html +0 -750
  10. package/arctifacts/clinical-care.html +0 -726
  11. package/arctifacts/collatz.html +0 -403
  12. package/arctifacts/complex.html +0 -321
  13. package/arctifacts/control-system.html +0 -482
  14. package/arctifacts/delfour.html +0 -849
  15. package/arctifacts/earthquake-epicenter.html +0 -982
  16. package/arctifacts/eco-route.html +0 -662
  17. package/arctifacts/euclid-infinitude.html +0 -564
  18. package/arctifacts/euler-identity.html +0 -667
  19. package/arctifacts/exoplanet-transit.html +0 -1000
  20. package/arctifacts/faltings-theorem.html +0 -1046
  21. package/arctifacts/fibonacci.html +0 -299
  22. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  23. package/arctifacts/godel-numbering.html +0 -743
  24. package/arctifacts/gps-bike.html +0 -759
  25. package/arctifacts/gps-clinical-bench.html +0 -792
  26. package/arctifacts/graph-french.html +0 -449
  27. package/arctifacts/grass-molecular.html +0 -592
  28. package/arctifacts/group-theory.html +0 -740
  29. package/arctifacts/health-info.html +0 -833
  30. package/arctifacts/kaprekar-constant.html +0 -576
  31. package/arctifacts/lee.html +0 -805
  32. package/arctifacts/linked-lists.html +0 -502
  33. package/arctifacts/lldm.html +0 -612
  34. package/arctifacts/matrix-multiplication.html +0 -502
  35. package/arctifacts/matrix.html +0 -651
  36. package/arctifacts/newton-raphson.html +0 -944
  37. package/arctifacts/peano-factorial.html +0 -456
  38. package/arctifacts/pi.html +0 -363
  39. package/arctifacts/polynomial.html +0 -646
  40. package/arctifacts/prime.html +0 -366
  41. package/arctifacts/pythagorean-theorem.html +0 -468
  42. package/arctifacts/rest-path.html +0 -469
  43. package/arctifacts/roots-of-unity.html +0 -363
  44. package/arctifacts/turing.html +0 -409
  45. package/arctifacts/wind-turbines.html +0 -726
@@ -1,592 +0,0 @@
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" />
6
- <title>Grass seed — molecular germination</title>
7
- <style>
8
- :root {
9
- --fg: #101014;
10
- --bg: #ffffff;
11
- --muted: #666;
12
- --accent: #2563eb;
13
- --chip: #eef2ff;
14
- --ok: #16a34a;
15
- --bad: #dc2626;
16
- --warn: #ca8a04;
17
- --w: #2563eb;
18
- --s: #ea580c;
19
- --e: #16a34a;
20
- --g: #7c3aed;
21
- --aba: #b91c1c;
22
- --d: #0ea5e9;
23
- --x: #d97706;
24
- }
25
- @media (prefers-color-scheme: dark) {
26
- :root {
27
- --fg: #eaeaf0;
28
- --bg: #0b0b10;
29
- --muted: #a0a0b0;
30
- --accent: #60a5fa;
31
- --chip: #0e1a32;
32
- --w: #60a5fa;
33
- --s: #fb923c;
34
- --e: #34d399;
35
- --g: #c084fc;
36
- --aba: #ef4444;
37
- --d: #67e8f9;
38
- --x: #fbbf24;
39
- }
40
- }
41
- html,
42
- body {
43
- margin: 0;
44
- padding: 0;
45
- background: var(--bg);
46
- color: var(--fg);
47
- font:
48
- 15px/1.6 ui-sans-serif,
49
- system-ui,
50
- -apple-system,
51
- Segoe UI,
52
- Roboto,
53
- Helvetica,
54
- Arial;
55
- }
56
- main {
57
- max-width: 1100px;
58
- margin: 0 auto;
59
- padding: 28px 16px 80px;
60
- }
61
- h1 {
62
- font-size: clamp(1.6rem, 2.6vw + 1rem, 2.2rem);
63
- margin: 0 0 6px;
64
- }
65
- header p {
66
- margin: 0;
67
- color: var(--muted);
68
- }
69
- section {
70
- margin: 18px 0 22px;
71
- padding: 14px 14px 16px;
72
- border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
73
- border-radius: 14px;
74
- background: color-mix(in srgb, var(--accent) 4%, transparent);
75
- }
76
- section h2 {
77
- margin: 0 0 8px;
78
- font-size: 1.15rem;
79
- }
80
- .grid {
81
- display: grid;
82
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
83
- gap: 10px;
84
- }
85
- .chip {
86
- display: inline-block;
87
- padding: 2px 8px;
88
- border-radius: 999px;
89
- background: var(--chip);
90
- font-weight: 600;
91
- }
92
- .muted {
93
- color: var(--muted);
94
- }
95
- .row {
96
- display: flex;
97
- gap: 12px;
98
- align-items: center;
99
- flex-wrap: wrap;
100
- }
101
- .btn {
102
- appearance: none;
103
- border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
104
- background: color-mix(in srgb, var(--accent) 8%, transparent);
105
- color: var(--fg);
106
- border-radius: 10px;
107
- padding: 8px 12px;
108
- font-weight: 700;
109
- cursor: pointer;
110
- }
111
- .btn:disabled {
112
- opacity: 0.6;
113
- cursor: not-allowed;
114
- }
115
- .mono {
116
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
117
- }
118
- .tbl {
119
- width: 100%;
120
- border-collapse: collapse;
121
- }
122
- .tbl th,
123
- .tbl td {
124
- padding: 6px 8px;
125
- border-bottom: 1px dashed color-mix(in srgb, var(--fg) 18%, transparent);
126
- vertical-align: top;
127
- }
128
- .tbl th {
129
- text-align: left;
130
- }
131
- .ok {
132
- color: var(--ok);
133
- }
134
- .bad {
135
- color: var(--bad);
136
- }
137
- .warn {
138
- color: var(--warn);
139
- }
140
- .small {
141
- font-size: 0.92em;
142
- }
143
- code {
144
- background: color-mix(in srgb, var(--accent) 10%, transparent);
145
- padding: 0.1rem 0.35rem;
146
- border-radius: 0.35rem;
147
- }
148
- input[type='number'] {
149
- padding: 8px 10px;
150
- border-radius: 10px;
151
- border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
152
- min-width: 120px;
153
- }
154
- .timeline {
155
- display: flex;
156
- gap: 8px;
157
- align-items: center;
158
- flex-wrap: wrap;
159
- }
160
- .badge {
161
- border-radius: 999px;
162
- padding: 4px 10px;
163
- background: var(--chip);
164
- border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
165
- }
166
- .badge .day {
167
- font-weight: 700;
168
- margin-left: 6px;
169
- }
170
- .legend {
171
- display: flex;
172
- gap: 12px;
173
- flex-wrap: wrap;
174
- align-items: center;
175
- }
176
- .dot {
177
- width: 10px;
178
- height: 10px;
179
- border-radius: 50%;
180
- display: inline-block;
181
- vertical-align: middle;
182
- margin-right: 6px;
183
- }
184
- .spark {
185
- width: 100%;
186
- height: 72px;
187
- border-radius: 10px;
188
- background: color-mix(in srgb, var(--accent) 6%, transparent);
189
- border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
190
- }
191
- details summary {
192
- cursor: pointer;
193
- font-weight: 700;
194
- }
195
- #advanced-rules {
196
- white-space: pre-line;
197
- }
198
- </style>
199
- </head>
200
- <body>
201
- <main>
202
- <header class="row">
203
- <div>
204
- <h1>Grass seed — molecular germination</h1>
205
- <p>A simple, visual story of what happens inside a seed right after it takes up water.</p>
206
- </div>
207
- <div class="row" style="margin-left: auto">
208
- <label class="muted small" for="days">Days to simulate (1–60):</label>
209
- <input id="days" type="number" min="1" max="60" step="1" value="20" aria-label="Days to simulate" />
210
- <button id="run" class="btn">Run</button>
211
- </div>
212
- </header>
213
-
214
- <section>
215
- <h2>What this is?</h2>
216
- <p>
217
- A friendly walkthrough of early <em>germination</em>. We track a few helpful dials: water in the seed, two
218
- hormones (a “brake” and a “go” signal), an enzyme that frees up food, the amount of usable sugar, and a
219
- “wall‑softening” factor that lets the root tip push out. When the three key needs are met — enough water,
220
- enough sugar, and soft walls — the <strong>radicle</strong> (first root) pops out. A few days later, the
221
- <strong>coleoptile</strong> (shoot sheath) shows.
222
- </p>
223
- <div class="grid small">
224
- <div>
225
- <strong>How to use it</strong><br />Pick the number of days and press <em>Run</em>. Read the milestones and
226
- watch the trend lines.
227
- </div>
228
- <div>
229
- <strong>What to look for</strong><br />Do water (W), sugar (S), and wall‑softening (E) cross their marks?
230
- That’s the radicle moment.
231
- </div>
232
- <div>
233
- <strong>Keep in mind</strong><br />This is an <em>educational toy</em>: numbers are illustrative, not
234
- lab‑calibrated.
235
- </div>
236
- </div>
237
- </section>
238
-
239
- <section>
240
- <h2>Glossary (plain → scientific)</h2>
241
- <div class="grid small">
242
- <div>
243
- <span class="dot" style="background: var(--w)"></span><strong>Water level</strong> → <em>W</em> (hydration,
244
- 0–1)
245
- </div>
246
- <div>
247
- <span class="dot" style="background: var(--aba)"></span><strong>Brake hormone</strong> →
248
- <em>ABA</em> (abscisic acid)
249
- </div>
250
- <div>
251
- <span class="dot" style="background: var(--g)"></span><strong>Go hormone</strong> →
252
- <em>GA</em> (gibberellin)
253
- </div>
254
- <div>
255
- <span class="dot" style="background: var(--d)"></span><strong>Growth brakes</strong> →
256
- <em>DELLA</em> proteins
257
- </div>
258
- <div>
259
- <span class="dot" style="background: var(--x)"></span><strong>Starch‑unlocker</strong> → <em>α‑amylase</em>
260
- </div>
261
- <div>
262
- <span class="dot" style="background: var(--s)"></span><strong>Fuel</strong> → <em>S</em> (metabolizable
263
- sugar)
264
- </div>
265
- <div>
266
- <span class="dot" style="background: var(--e)"></span><strong>Wall softeners</strong> →
267
- <em>E</em> (expansins)
268
- </div>
269
- </div>
270
- </section>
271
-
272
- <section id="answer">
273
- <h2>Answer</h2>
274
- <div id="timeline" class="timeline small"></div>
275
- <div id="charts"></div>
276
- <div id="answer-body"><em class="muted">Not computed yet.</em></div>
277
- </section>
278
-
279
- <section id="reason">
280
- <h2>Reason why</h2>
281
- <div class="mono small" id="advanced-rules"></div>
282
- </section>
283
-
284
- <section id="check">
285
- <h2>Check (harness)</h2>
286
- <div id="check-body"><em class="muted">Not computed yet.</em></div>
287
- </section>
288
- </main>
289
-
290
- <script>
291
- (function () {
292
- 'use strict';
293
-
294
- const $ = (id) => document.getElementById(id);
295
- const setHTML = (id, html) => {
296
- const el = $(id);
297
- if (el) el.innerHTML = html;
298
- };
299
-
300
- // ------------------ Parameters & thresholds (dimensionless) ------------------
301
- const P = {
302
- kw: 0.45, // hydration speed
303
- ka: 0.3, // ABA decay weight (scaled by W)
304
- kg: 0.35, // GA rise
305
- kd: 0.4, // DELLA degradation by GA
306
- kx: 0.25, // amylase induction by GA
307
- ks: 0.2, // sugar accumulation per amylase
308
- ke: 0.22, // expansin induction by GA
309
- // thresholds
310
- W_hydrated: 0.85,
311
- ABA_low: 0.2,
312
- G_active: 0.6,
313
- D_low: 0.2,
314
- X_on: 0.6,
315
- S_ready: 1.0,
316
- E_soft: 0.8,
317
- coleoptile_delay: 3,
318
- };
319
-
320
- // Render advanced rules text
321
- (function () {
322
- const txt = [
323
- 'State: 0 ≤ W,G,X,E ≤ 1; ABA,DELLA ≥ 0; sugar S ≥ 0.',
324
- 'Hydration: W_{t+1} = W_t + k_w(1 − W_t).',
325
- 'ABA decay: ABA_{t+1} = ABA_t · (1 − k_a W_t).',
326
- 'GA rise: G_{t+1} = G_t + k_g W_t (1 − G_t).',
327
- 'DELLA degradation: D_{t+1} = D_t · (1 − k_d G_t).',
328
- 'α‑amylase: X_{t+1} = X_t + k_x G_t (1 − X_t).',
329
- 'Sugar: S_{t+1} = S_t + k_s X_t.',
330
- 'Expansins: E_{t+1} = E_t + k_e G_t (1 − E_t).',
331
- 'Milestones at first t with thresholds; coleoptile = radicle + 3 days.',
332
- ];
333
- const el = $('advanced-rules');
334
- if (el) el.textContent = txt.join('\n');
335
- })();
336
-
337
- // ------------------ Simulator ------------------
338
- function clamp01(x) {
339
- return Math.max(0, Math.min(1, x));
340
- }
341
- function run(days = 20) {
342
- // Initial conditions
343
- let W = 0.1,
344
- ABA = 1.0,
345
- G = 0.0,
346
- D = 1.0,
347
- X = 0.0,
348
- S = 0.0,
349
- E = 0.0;
350
- const hist = [{ day: 0, W, ABA, G, D, X, S, E }];
351
-
352
- for (let t = 0; t < days; t++) {
353
- W = clamp01(W + P.kw * (1 - W));
354
- ABA = Math.max(0, ABA * (1 - P.ka * W));
355
- G = clamp01(G + P.kg * W * (1 - G));
356
- D = Math.max(0, D * (1 - P.kd * G));
357
- X = clamp01(X + P.kx * G * (1 - X));
358
- S = Math.max(0, S + P.ks * X);
359
- E = clamp01(E + P.ke * G * (1 - E));
360
- hist.push({ day: t + 1, W, ABA, G, D, X, S, E });
361
- }
362
- return hist;
363
- }
364
-
365
- function firstDay(hist, pred) {
366
- for (const row of hist) {
367
- if (pred(row)) return row.day;
368
- }
369
- return null;
370
- }
371
-
372
- function computeMilestones(hist) {
373
- const m = {};
374
- m.hydrated = firstDay(hist, (r) => r.W >= P.W_hydrated);
375
- m.ABA_low = firstDay(hist, (r) => r.ABA <= P.ABA_low);
376
- m.G_active = firstDay(hist, (r) => r.G >= P.G_active);
377
- m.D_low = firstDay(hist, (r) => r.D <= P.D_low);
378
- m.X_on = firstDay(hist, (r) => r.X >= P.X_on);
379
- m.S_ready = firstDay(hist, (r) => r.S >= P.S_ready);
380
- m.E_soft = firstDay(hist, (r) => r.E >= P.E_soft);
381
- m.radicle = firstDay(hist, (r) => r.W >= P.W_hydrated && r.S >= P.S_ready && r.E >= P.E_soft);
382
- m.coleoptile = m.radicle == null ? null : m.radicle + P.coleoptile_delay;
383
- return m;
384
- }
385
-
386
- // ------------------ Rendering helpers ------------------
387
- function fmt2(x) {
388
- return (Math.round(x * 100) / 100).toFixed(2);
389
- }
390
- function badge(label, day, colorVar) {
391
- const dayStr =
392
- day == null ? '<span class="bad">not reached</span>' : '<span class="day">' + day + '</span> d';
393
- return `<span class="badge"><span class="dot" style="background:var(${colorVar})"></span>${label}: ${dayStr}</span>`;
394
- }
395
- function renderTimeline(m) {
396
- const html = [
397
- badge('Hydrated', m.hydrated, '--w'),
398
- badge('ABA low', m.ABA_low, '--aba'),
399
- badge('GA active', m.G_active, '--g'),
400
- badge('DELLA low', m.D_low, '--d'),
401
- badge('Amylase on', m.X_on, '--x'),
402
- badge('Sugar ready', m.S_ready, '--s'),
403
- badge('Wall soft', m.E_soft, '--e'),
404
- badge('Radicle', m.radicle, '--e'),
405
- badge('Coleoptile', m.coleoptile, '--g'),
406
- ].join('');
407
- setHTML('timeline', html);
408
- }
409
-
410
- function renderAnswer(hist, m) {
411
- renderTimeline(m);
412
-
413
- // Table
414
- const milestoneRows = [
415
- ['Hydrated (W ≥ 0.85)', m.hydrated],
416
- ['Brake hormone low (ABA ≤ 0.20)', m.ABA_low],
417
- ['Go hormone active (GA ≥ 0.60)', m.G_active],
418
- ['Growth brakes low (DELLA ≤ 0.20)', m.D_low],
419
- ['Starch‑unlocker on (α‑amylase ≥ 0.60)', m.X_on],
420
- ['Fuel ready (Sugar ≥ 1.00)', m.S_ready],
421
- ['Walls soft (Expansins ≥ 0.80)', m.E_soft],
422
- ['Radicle emergence', m.radicle],
423
- ['Coleoptile visible', m.coleoptile],
424
- ];
425
- let html = '<table class="tbl small"><thead><tr><th>Milestone</th><th>Day</th></tr></thead><tbody>';
426
- for (const [name, day] of milestoneRows) {
427
- const pretty = day == null ? '<span class="bad">not reached</span>' : day;
428
- html += `<tr><td>${name}</td><td>${pretty}</td></tr>`;
429
- }
430
- html += '</tbody></table>';
431
-
432
- // State trace (first 16 days)
433
- const tail = hist
434
- .slice(0, 16)
435
- .map(
436
- (r) =>
437
- `${r.day.toString().padStart(2)} W=${fmt2(r.W)} ABA=${fmt2(r.ABA)} GA=${fmt2(r.G)} DELLA=${fmt2(r.D)} α-amylase=${fmt2(r.X)} S=${fmt2(r.S)} E=${fmt2(r.E)}`,
438
- );
439
- html += `<p class="small muted">First 16 days (state trace):</p>`;
440
- html += `<pre class="mono" style="white-space:pre-wrap">${tail.join('\n')}</pre>`;
441
- setHTML('answer-body', html);
442
-
443
- // Mini‑charts
444
- renderSparklines(hist);
445
- }
446
-
447
- // Simple sparklines (inline SVG)
448
- function sparkline(id, series, colorVar, yMax) {
449
- const w = 1000,
450
- h = 72,
451
- pad = 6;
452
- const maxX = series.length - 1;
453
- const maxY = yMax !== undefined ? yMax : Math.max(...series);
454
- const pts = series
455
- .map((v, i) => {
456
- const x = pad + (w - 2 * pad) * (i / maxX);
457
- const y = h - pad - (h - 2 * pad) * (v / (maxY || 1));
458
- return `${x.toFixed(2)},${y.toFixed(2)}`;
459
- })
460
- .join(' ');
461
- return `<svg class="spark" viewBox="0 0 ${w} ${h}" preserveAspectRatio="none" role="img" aria-label="trend">
462
- <polyline fill="none" stroke="var(${colorVar})" stroke-width="3" points="${pts}" />
463
- </svg>`;
464
- }
465
-
466
- function renderSparklines(hist) {
467
- const W = hist.map((r) => r.W),
468
- S = hist.map((r) => r.S),
469
- E = hist.map((r) => r.E),
470
- G = hist.map((r) => r.G);
471
- const box = document.getElementById('charts');
472
- let html = '<div class="legend small">';
473
- html += '<span><span class="dot" style="background:var(--w)"></span>Water (W)</span>';
474
- html += '<span><span class="dot" style="background:var(--s)"></span>Sugar (S)</span>';
475
- html += '<span><span class="dot" style="background:var(--e)"></span>Wall softening (E)</span>';
476
- html += '<span><span class="dot" style="background:var(--g)"></span>Go hormone (GA)</span>';
477
- html += '</div>';
478
- html += sparkline('w', W, '--w', 1);
479
- html += sparkline('s', S, '--s', Math.max(1, Math.max(...S)));
480
- html += sparkline('e', E, '--e', 1);
481
- html += sparkline('g', G, '--g', 1);
482
- box.innerHTML = html;
483
- }
484
-
485
- // ------------------ Checks ------------------
486
- function runChecks(hist, m) {
487
- const lines = [];
488
- let okAll = true;
489
-
490
- // 1) In-range & trend direction
491
- function nonDecreasing(seq) {
492
- for (let i = 1; i < seq.length; i++) {
493
- if (seq[i] + 1e-12 < seq[i - 1]) return false;
494
- }
495
- return true;
496
- }
497
- function nonIncreasing(seq) {
498
- for (let i = 1; i < seq.length; i++) {
499
- if (seq[i] - 1e-12 > seq[i - 1]) return false;
500
- }
501
- return true;
502
- }
503
- const Wseries = hist.map((r) => r.W),
504
- ABAseries = hist.map((r) => r.ABA),
505
- Dseries = hist.map((r) => r.D);
506
- const Gseries = hist.map((r) => r.G),
507
- Xseries = hist.map((r) => r.X),
508
- Eseries = hist.map((r) => r.E),
509
- Sseries = hist.map((r) => r.S);
510
- const okBounds =
511
- Math.min(...Wseries) >= 0 &&
512
- Math.max(...Wseries) <= 1 &&
513
- Math.min(...Gseries) >= 0 &&
514
- Math.max(...Gseries) <= 1 &&
515
- Math.min(...Xseries) >= 0 &&
516
- Math.max(...Xseries) <= 1 &&
517
- Math.min(...Eseries) >= 0 &&
518
- Math.max(...Eseries) <= 1 &&
519
- Math.min(...ABAseries) >= 0 &&
520
- Math.min(...Dseries) >= 0 &&
521
- Math.min(...Sseries) >= 0;
522
- const okMono =
523
- nonDecreasing(Wseries) &&
524
- nonDecreasing(Gseries) &&
525
- nonDecreasing(Xseries) &&
526
- nonDecreasing(Eseries) &&
527
- nonDecreasing(Sseries) &&
528
- nonIncreasing(ABAseries) &&
529
- nonIncreasing(Dseries);
530
- lines.push(
531
- `Signals stay within sensible ranges and move in the expected direction: ${okBounds && okMono ? 'OK' : 'Issue'}`,
532
- );
533
- okAll = okAll && okBounds && okMono;
534
-
535
- // 2) Logical ordering
536
- const orderOK =
537
- (m.ABA_low ?? 1e9) >= (m.hydrated ?? -1) &&
538
- (m.G_active ?? 1e9) >= (m.hydrated ?? -1) &&
539
- (m.D_low ?? 1e9) >= (m.G_active ?? -1) &&
540
- (m.X_on ?? 1e9) >= (m.G_active ?? -1) &&
541
- (m.S_ready ?? 1e9) >= (m.X_on ?? -1) &&
542
- (m.E_soft ?? 1e9) >= (m.G_active ?? -1) &&
543
- (m.radicle ?? 1e9) >= Math.max(m.S_ready ?? -1, m.E_soft ?? -1, m.hydrated ?? -1);
544
- lines.push(`Milestones happen in a sensible order: ${orderOK ? 'OK' : 'Check thresholds'}`);
545
- okAll = okAll && orderOK;
546
-
547
- // 3) Radicle condition equivalence
548
- let okRad = true;
549
- if (m.radicle != null) {
550
- const r = hist[m.radicle];
551
- okRad = r && r.W >= P.W_hydrated && r.S >= P.S_ready && r.E >= P.E_soft;
552
- }
553
- lines.push(
554
- `On the radicle day, water + sugar + soft walls are all high enough: ${okRad ? 'OK' : 'Not satisfied'}`,
555
- );
556
- okAll = okAll && okRad;
557
-
558
- // 4) Coleoptile offset
559
- const okCol = (m.coleoptile == null && m.radicle == null) || m.coleoptile === m.radicle + P.coleoptile_delay;
560
- lines.push(`Coleoptile appears ${P.coleoptile_delay} days after radicle: ${okCol ? 'OK' : 'Mismatch'}`);
561
-
562
- lines.push('');
563
- lines.push(`All checks passed? ${okAll ? 'Yes ✓' : 'Some items need attention'}`);
564
- return lines;
565
- }
566
-
567
- function renderChecks(hist, m) {
568
- const lines = runChecks(hist, m);
569
- const wrap = $('check-body');
570
- const pre = document.createElement('pre');
571
- pre.className = 'mono';
572
- pre.style.whiteSpace = 'pre-wrap';
573
- pre.textContent = lines.join('\n');
574
- wrap.replaceChildren(pre);
575
- }
576
-
577
- // ------------------ Wire up ------------------
578
- function runAll() {
579
- const days = Math.max(1, Math.min(60, parseInt($('days').value || '20', 10)));
580
- const hist = run(days);
581
- const m = computeMilestones(hist);
582
- renderAnswer(hist, m);
583
- renderChecks(hist, m);
584
- }
585
-
586
- $('run').addEventListener('click', runAll);
587
- // initial
588
- runAll();
589
- })();
590
- </script>
591
- </body>
592
- </html>