@jjlmoya/utils-science 1.33.0 → 1.35.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 (60) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +2 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/natural-selection-drift/component.astro +37 -6
  8. package/src/tool/natural-selection-drift/natural-selection-drift.css +134 -0
  9. package/src/tool/roche-limit-satellite-disruption/bibliography.astro +14 -0
  10. package/src/tool/roche-limit-satellite-disruption/bibliography.ts +16 -0
  11. package/src/tool/roche-limit-satellite-disruption/component.astro +97 -0
  12. package/src/tool/roche-limit-satellite-disruption/entry.ts +28 -0
  13. package/src/tool/roche-limit-satellite-disruption/i18n/de.ts +229 -0
  14. package/src/tool/roche-limit-satellite-disruption/i18n/en.ts +229 -0
  15. package/src/tool/roche-limit-satellite-disruption/i18n/es.ts +229 -0
  16. package/src/tool/roche-limit-satellite-disruption/i18n/fr.ts +229 -0
  17. package/src/tool/roche-limit-satellite-disruption/i18n/id.ts +229 -0
  18. package/src/tool/roche-limit-satellite-disruption/i18n/it.ts +229 -0
  19. package/src/tool/roche-limit-satellite-disruption/i18n/ja.ts +229 -0
  20. package/src/tool/roche-limit-satellite-disruption/i18n/ko.ts +229 -0
  21. package/src/tool/roche-limit-satellite-disruption/i18n/nl.ts +229 -0
  22. package/src/tool/roche-limit-satellite-disruption/i18n/pl.ts +229 -0
  23. package/src/tool/roche-limit-satellite-disruption/i18n/pt.ts +229 -0
  24. package/src/tool/roche-limit-satellite-disruption/i18n/ru.ts +229 -0
  25. package/src/tool/roche-limit-satellite-disruption/i18n/sv.ts +229 -0
  26. package/src/tool/roche-limit-satellite-disruption/i18n/tr.ts +229 -0
  27. package/src/tool/roche-limit-satellite-disruption/i18n/zh.ts +229 -0
  28. package/src/tool/roche-limit-satellite-disruption/index.ts +11 -0
  29. package/src/tool/roche-limit-satellite-disruption/logic.ts +102 -0
  30. package/src/tool/roche-limit-satellite-disruption/particle-system.ts +66 -0
  31. package/src/tool/roche-limit-satellite-disruption/roche-limit-satellite-disruption-calculator.css +568 -0
  32. package/src/tool/roche-limit-satellite-disruption/script.ts +274 -0
  33. package/src/tool/roche-limit-satellite-disruption/seo.astro +15 -0
  34. package/src/tool/roche-limit-satellite-disruption/storage.ts +28 -0
  35. package/src/tool/roche-limit-satellite-disruption/visual-data.ts +16 -0
  36. package/src/tool/three-body-problem/app.ts +274 -0
  37. package/src/tool/three-body-problem/bibliography.astro +14 -0
  38. package/src/tool/three-body-problem/bibliography.ts +16 -0
  39. package/src/tool/three-body-problem/component.astro +70 -0
  40. package/src/tool/three-body-problem/entry.ts +26 -0
  41. package/src/tool/three-body-problem/i18n/de.ts +162 -0
  42. package/src/tool/three-body-problem/i18n/en.ts +162 -0
  43. package/src/tool/three-body-problem/i18n/es.ts +162 -0
  44. package/src/tool/three-body-problem/i18n/fr.ts +162 -0
  45. package/src/tool/three-body-problem/i18n/id.ts +162 -0
  46. package/src/tool/three-body-problem/i18n/it.ts +162 -0
  47. package/src/tool/three-body-problem/i18n/ja.ts +162 -0
  48. package/src/tool/three-body-problem/i18n/ko.ts +162 -0
  49. package/src/tool/three-body-problem/i18n/nl.ts +162 -0
  50. package/src/tool/three-body-problem/i18n/pl.ts +162 -0
  51. package/src/tool/three-body-problem/i18n/pt.ts +162 -0
  52. package/src/tool/three-body-problem/i18n/ru.ts +162 -0
  53. package/src/tool/three-body-problem/i18n/sv.ts +162 -0
  54. package/src/tool/three-body-problem/i18n/tr.ts +162 -0
  55. package/src/tool/three-body-problem/i18n/zh.ts +162 -0
  56. package/src/tool/three-body-problem/index.ts +11 -0
  57. package/src/tool/three-body-problem/logic/ThreeBodyEngine.ts +179 -0
  58. package/src/tool/three-body-problem/seo.astro +15 -0
  59. package/src/tool/three-body-problem/three-body-problem-simulator.css +503 -0
  60. package/src/tools.ts +4 -0
@@ -0,0 +1,568 @@
1
+ .roche-console {
2
+ --ink: #1f2430;
3
+ --muted: #667085;
4
+ --panel: #fffaf2;
5
+ --panel-soft: rgba(255, 253, 248, 0.72);
6
+ --visual-a: rgba(255, 250, 242, 0.95);
7
+ --visual-b: rgba(235, 246, 247, 0.95);
8
+ --line: #d9c7ad;
9
+ --bar-bg: #eadcc9;
10
+ --orbital-line: rgba(40, 44, 52, 0.22);
11
+ --planet-line-rgb: 209 154 102;
12
+ --orbital-boundary: rgba(160, 54, 48, 0.68);
13
+ --debris-alpha: 0.88;
14
+ --accent: #237c8f;
15
+ --hot: #d35244;
16
+ --gold: #d8a93a;
17
+ --radius: 5px;
18
+ --ring-opacity: 0.18;
19
+ --stress: 0;
20
+
21
+ display: grid;
22
+ grid-template-columns: 1fr;
23
+ gap: 0.85rem;
24
+ color: var(--ink);
25
+ border: 1px solid var(--line);
26
+ border-radius: var(--radius);
27
+ background: var(--panel);
28
+ box-shadow: 0 18px 42px rgba(67, 43, 20, 0.1);
29
+ padding: 0.9rem;
30
+ }
31
+
32
+ .theme-dark .roche-console {
33
+ --ink: #f4efe7;
34
+ --muted: #b8c1c4;
35
+ --panel: #151923;
36
+ --panel-soft: rgba(255, 255, 255, 0.03);
37
+ --visual-a: rgba(21, 25, 35, 0.96);
38
+ --visual-b: rgba(22, 35, 39, 0.96);
39
+ --line: #364050;
40
+ --bar-bg: #2a3140;
41
+ --orbital-line: rgba(255, 255, 255, 0.18);
42
+ --orbital-boundary: rgba(255, 130, 118, 0.78);
43
+ --debris-alpha: 1;
44
+ --accent: #70d4e1;
45
+ --hot: #ff8276;
46
+ --gold: #f1c95e;
47
+
48
+ box-shadow: 0 18px 42px rgba(0, 0, 0, 0.28);
49
+ }
50
+
51
+ .roche-visual {
52
+ overflow: hidden;
53
+ background:
54
+ linear-gradient(135deg, var(--visual-a), var(--visual-b)),
55
+ repeating-linear-gradient(90deg, rgba(31, 36, 48, 0.05) 0 1px, transparent 1px 24px);
56
+ border: 1px solid var(--line);
57
+ border-radius: var(--radius);
58
+ }
59
+
60
+ .theme-dark .roche-visual,
61
+ .theme-dark .roche-readouts div {
62
+ background-color: rgba(255, 255, 255, 0.03);
63
+ backdrop-filter: blur(12px);
64
+ }
65
+
66
+ .roche-status {
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: space-between;
70
+ gap: 0.75rem;
71
+ }
72
+
73
+ .roche-field span,
74
+ .roche-readouts span,
75
+ .roche-bar span,
76
+ .roche-material span {
77
+ color: color-mix(in srgb, var(--muted) 72%, var(--ink));
78
+ font-size: 0.78rem;
79
+ font-weight: 700;
80
+ letter-spacing: 0;
81
+ text-transform: uppercase;
82
+ }
83
+
84
+ #roche-verdict {
85
+ min-width: 7.8rem;
86
+ border-radius: var(--radius);
87
+ padding: 0.5rem 0.7rem;
88
+ background: var(--accent);
89
+ color: white;
90
+ text-align: center;
91
+ }
92
+
93
+ .roche-distance-readout {
94
+ display: grid;
95
+ gap: 0.12rem;
96
+ min-width: 0;
97
+ text-align: right;
98
+ }
99
+
100
+ .roche-distance-readout small {
101
+ color: var(--muted);
102
+ font-size: 0.66rem;
103
+ font-weight: 800;
104
+ letter-spacing: 0.05em;
105
+ line-height: 1;
106
+ text-transform: uppercase;
107
+ }
108
+
109
+ #roche-distance-output {
110
+ color: var(--accent);
111
+ font-size: clamp(1.05rem, 5vw, 1.45rem);
112
+ font-weight: 900;
113
+ text-align: right;
114
+ }
115
+
116
+ .roche-console[data-verdict="grazing"] #roche-verdict {
117
+ background: var(--gold);
118
+ color: #302610;
119
+ }
120
+
121
+ .roche-console[data-verdict="fragmenting"] #roche-verdict,
122
+ .roche-console[data-verdict="ring"] #roche-verdict {
123
+ background: var(--hot);
124
+ }
125
+
126
+ .roche-orbit-stage {
127
+ position: relative;
128
+ padding: 0;
129
+ }
130
+
131
+ .roche-particle-canvas {
132
+ position: absolute;
133
+ inset: 0;
134
+ z-index: 0;
135
+ width: 100%;
136
+ height: 100%;
137
+ pointer-events: none;
138
+ }
139
+
140
+ .roche-orbit-map {
141
+ position: relative;
142
+ z-index: 1;
143
+ display: block;
144
+ width: 100%;
145
+ max-height: 52vh;
146
+ }
147
+
148
+ .roche-planet {
149
+ fill: url("#roche-planet-gradient");
150
+ stroke: #3c314f;
151
+ stroke-width: 2;
152
+ }
153
+
154
+ .roche-boundary {
155
+ fill: none;
156
+ stroke: var(--orbital-boundary);
157
+ stroke-dasharray: 8 9;
158
+ stroke-width: 2.6;
159
+ opacity: calc(0.35 + var(--stress) * 0.55);
160
+ }
161
+
162
+ .roche-orbit {
163
+ fill: none;
164
+ stroke: color-mix(in srgb, rgb(var(--planet-line-rgb)) 55%, var(--orbital-line));
165
+ stroke-width: 2.2;
166
+ opacity: 1;
167
+ }
168
+
169
+ .roche-debris {
170
+ fill: none;
171
+ stroke: url("#roche-ring-gradient");
172
+ stroke-linecap: round;
173
+ stroke-width: 12;
174
+ opacity: calc(var(--ring-opacity) * var(--debris-alpha));
175
+ transform-origin: 280px 280px;
176
+ }
177
+
178
+ .roche-debris-wide {
179
+ stroke-width: 7;
180
+ opacity: calc(var(--ring-opacity) * 0.75);
181
+ }
182
+
183
+ .roche-debris-back {
184
+ opacity: calc(var(--ring-opacity) * 0.42);
185
+ }
186
+
187
+ .roche-debris-front {
188
+ opacity: var(--ring-opacity);
189
+ }
190
+
191
+ .roche-moon circle {
192
+ fill: #d7dce6;
193
+ stroke: #677084;
194
+ stroke-width: 2;
195
+ }
196
+
197
+ .roche-moon ellipse {
198
+ fill: #d7dce6;
199
+ stroke: #677084;
200
+ stroke-width: 2;
201
+ transition: rx 180ms ease, ry 180ms ease;
202
+ }
203
+
204
+ .roche-moon path {
205
+ fill: none;
206
+ stroke: #939bad;
207
+ stroke-width: 3;
208
+ stroke-linecap: round;
209
+ }
210
+
211
+ .roche-fragment {
212
+ fill: color-mix(in srgb, var(--gold) 78%, var(--ink));
213
+ opacity: calc(0.12 + var(--ring-opacity));
214
+ animation: roche-spark 2.8s ease-in-out infinite;
215
+ animation-delay: var(--delay);
216
+ }
217
+
218
+ .roche-map-label {
219
+ fill: var(--muted);
220
+ font-size: 18px;
221
+ font-weight: 800;
222
+ text-anchor: middle;
223
+ }
224
+
225
+ .roche-readouts {
226
+ display: grid;
227
+ grid-template-columns: repeat(2, 1fr);
228
+ gap: 1px;
229
+ overflow: hidden;
230
+ background: var(--line);
231
+ border-radius: var(--radius);
232
+ }
233
+
234
+ .roche-readouts div {
235
+ min-width: 0;
236
+ padding: 0.75rem;
237
+ background: var(--panel-soft);
238
+ }
239
+
240
+ .roche-readouts strong {
241
+ display: block;
242
+ margin-top: 0.25rem;
243
+ font-size: clamp(1.1rem, 5vw, 1.75rem);
244
+ line-height: 1;
245
+ }
246
+
247
+ .roche-readouts span {
248
+ color: color-mix(in srgb, var(--ink) 64%, transparent);
249
+ font-size: 0.68rem;
250
+ letter-spacing: 0.05em;
251
+ }
252
+
253
+ .theme-dark .roche-readouts span {
254
+ color: rgba(255, 255, 255, 0.68);
255
+ }
256
+
257
+ .theme-dark .roche-readouts strong {
258
+ color: rgba(255, 255, 255, 0.96);
259
+ }
260
+
261
+ .roche-readouts strong small {
262
+ margin-left: 0.16rem;
263
+ color: var(--muted);
264
+ font-size: 0.58em;
265
+ font-weight: 800;
266
+ }
267
+
268
+ .roche-controls {
269
+ display: grid;
270
+ gap: 0.95rem;
271
+ }
272
+
273
+ .roche-field {
274
+ display: grid;
275
+ gap: 0.4rem;
276
+ }
277
+
278
+ .roche-field input {
279
+ width: 100%;
280
+ min-height: 2.7rem;
281
+ }
282
+
283
+ .roche-picker {
284
+ position: relative;
285
+ }
286
+
287
+ .roche-picker-trigger {
288
+ display: grid;
289
+ grid-template-columns: minmax(0, 1fr) auto 0.7rem;
290
+ align-items: center;
291
+ gap: 0.45rem;
292
+ width: 100%;
293
+ min-height: 2.85rem;
294
+ border: 0;
295
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 68%, transparent);
296
+ background: transparent;
297
+ color: var(--ink);
298
+ font-size: inherit;
299
+ line-height: inherit;
300
+ padding: 0 0 0.18rem;
301
+ text-align: left;
302
+ cursor: pointer;
303
+ }
304
+
305
+ .roche-picker-trigger:hover,
306
+ .roche-picker-trigger:focus-visible {
307
+ border-bottom-color: var(--accent);
308
+ outline: 0;
309
+ }
310
+
311
+ .roche-picker-trigger i {
312
+ width: 0.42rem;
313
+ height: 0.42rem;
314
+ border-right: 1px solid var(--muted);
315
+ border-bottom: 1px solid var(--muted);
316
+ transform: rotate(45deg) translateY(-0.12rem);
317
+ }
318
+
319
+ .roche-picker-trigger strong {
320
+ min-width: 0;
321
+ color: var(--ink);
322
+ font-size: 1rem;
323
+ font-weight: 900;
324
+ line-height: 1.1;
325
+ white-space: normal;
326
+ }
327
+
328
+ .roche-picker-trigger small {
329
+ color: color-mix(in srgb, var(--muted) 78%, var(--ink));
330
+ font-size: 0.72rem;
331
+ font-weight: 800;
332
+ letter-spacing: 0;
333
+ text-align: right;
334
+ text-transform: uppercase;
335
+ white-space: nowrap;
336
+ }
337
+
338
+ .roche-picker-menu {
339
+ position: absolute;
340
+ z-index: 999;
341
+ top: calc(100% + 0.35rem);
342
+ left: 0;
343
+ right: 0;
344
+ display: none;
345
+ overflow: hidden;
346
+ border: 1px solid color-mix(in srgb, var(--line) 86%, var(--ink));
347
+ border-radius: var(--radius);
348
+ background: var(--panel);
349
+ box-shadow: 0 22px 55px rgba(0, 0, 0, 0.2);
350
+ }
351
+
352
+ .theme-dark .roche-picker-menu {
353
+ background: #151923;
354
+ box-shadow: 0 26px 70px rgba(0, 0, 0, 0.42);
355
+ }
356
+
357
+ .roche-picker.is-open .roche-picker-menu {
358
+ display: grid;
359
+ }
360
+
361
+ .roche-picker-menu button {
362
+ appearance: none;
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: space-between;
366
+ gap: 0.75rem;
367
+ min-height: 2.55rem;
368
+ border: 0;
369
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 48%, transparent);
370
+ background: transparent;
371
+ color: var(--ink);
372
+ font-size: inherit;
373
+ line-height: inherit;
374
+ padding: 0.55rem 0.65rem;
375
+ text-align: left;
376
+ cursor: pointer;
377
+ }
378
+
379
+ .roche-picker-menu button:last-child {
380
+ border-bottom: 0;
381
+ }
382
+
383
+ .roche-picker-menu button:hover,
384
+ .roche-picker-menu button:focus-visible,
385
+ .roche-picker-menu button.is-selected {
386
+ outline: 0;
387
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
388
+ }
389
+
390
+ .roche-picker-menu strong {
391
+ font-size: 0.92rem;
392
+ }
393
+
394
+ .roche-picker-menu small {
395
+ color: var(--muted);
396
+ font-size: 0.7rem;
397
+ font-weight: 800;
398
+ white-space: nowrap;
399
+ }
400
+
401
+ .roche-distance {
402
+ grid-column: 1 / -1;
403
+ }
404
+
405
+ .roche-field input[type="range"] {
406
+ height: 1.8rem;
407
+ appearance: none;
408
+ -webkit-appearance: none;
409
+ background: transparent;
410
+ cursor: pointer;
411
+ }
412
+
413
+ .roche-field input[type="range"]::-webkit-slider-runnable-track {
414
+ height: 2px;
415
+ background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--line) 72%, transparent));
416
+ }
417
+
418
+ .roche-field input[type="range"]::-webkit-slider-thumb {
419
+ width: 8px;
420
+ height: 8px;
421
+ margin-top: -3px;
422
+ appearance: none;
423
+ -webkit-appearance: none;
424
+ border: 0;
425
+ border-radius: 50%;
426
+ background: var(--accent);
427
+ box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 14%, transparent);
428
+ }
429
+
430
+ .roche-field input[type="range"]::-moz-range-track {
431
+ height: 2px;
432
+ background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--line) 72%, transparent));
433
+ }
434
+
435
+ .roche-field input[type="range"]::-moz-range-thumb {
436
+ width: 8px;
437
+ height: 8px;
438
+ border: 0;
439
+ border-radius: 50%;
440
+ background: var(--accent);
441
+ box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 14%, transparent);
442
+ }
443
+
444
+ .roche-button-row {
445
+ display: grid;
446
+ grid-template-columns: 1fr 1fr;
447
+ gap: 0.6rem;
448
+ }
449
+
450
+ .roche-button-row button {
451
+ min-height: 2.8rem;
452
+ border: 1px solid var(--line);
453
+ border-radius: var(--radius);
454
+ background: transparent;
455
+ color: var(--ink);
456
+ font-size: inherit;
457
+ line-height: inherit;
458
+ font-weight: 800;
459
+ cursor: pointer;
460
+ }
461
+
462
+ .roche-button-row button:first-child {
463
+ border-color: color-mix(in srgb, var(--hot) 64%, var(--line));
464
+ color: var(--hot);
465
+ }
466
+
467
+ .roche-button-row button#roche-reset {
468
+ border-color: transparent;
469
+ color: var(--muted);
470
+ }
471
+
472
+ .roche-button-row button#roche-reset:hover,
473
+ .roche-button-row button#roche-reset:focus-visible {
474
+ border-color: color-mix(in srgb, var(--line) 70%, transparent);
475
+ color: var(--ink);
476
+ outline: 0;
477
+ }
478
+
479
+ .roche-comparison {
480
+ display: grid;
481
+ gap: 0.9rem;
482
+ }
483
+
484
+ .roche-bar {
485
+ display: grid;
486
+ grid-template-columns: minmax(6.5rem, 0.8fr) 1.2fr minmax(5.5rem, 0.7fr);
487
+ align-items: center;
488
+ gap: 0.7rem;
489
+ }
490
+
491
+ .roche-bar div {
492
+ height: 0.7rem;
493
+ overflow: hidden;
494
+ border-radius: 999px;
495
+ background: var(--bar-bg);
496
+ }
497
+
498
+ .roche-bar i {
499
+ display: block;
500
+ height: 100%;
501
+ border-radius: inherit;
502
+ background: linear-gradient(90deg, var(--accent), var(--gold), var(--hot));
503
+ }
504
+
505
+ .roche-bar strong {
506
+ text-align: right;
507
+ }
508
+
509
+ .roche-material {
510
+ display: grid;
511
+ grid-template-columns: 1fr;
512
+ gap: 0.45rem;
513
+ border-top: 1px solid var(--line);
514
+ padding-top: 0.85rem;
515
+ }
516
+
517
+ @keyframes roche-spark {
518
+ 0%,
519
+ 100% {
520
+ transform: scale(0.72);
521
+ }
522
+ 50% {
523
+ transform: scale(1.18);
524
+ }
525
+ }
526
+
527
+ @media (min-width: 760px) {
528
+ .roche-console {
529
+ grid-template-columns: minmax(0, 1.05fr) minmax(18rem, 0.95fr);
530
+ align-items: start;
531
+ }
532
+
533
+ .roche-status {
534
+ grid-column: 1 / -1;
535
+ }
536
+
537
+ .roche-visual {
538
+ grid-row: span 3;
539
+ }
540
+
541
+ .roche-controls {
542
+ grid-template-columns: 1fr 1fr;
543
+ }
544
+
545
+ .roche-readouts {
546
+ grid-template-columns: 1fr 1fr;
547
+ }
548
+
549
+ .roche-material {
550
+ grid-template-columns: 1fr 1fr 1fr;
551
+ }
552
+ }
553
+
554
+ @media (prefers-color-scheme: dark) {
555
+ :root:not(.theme-light) .roche-console {
556
+ --ink: #f4efe7;
557
+ --muted: #b8c1c4;
558
+ --panel: #151923;
559
+ --panel-soft: #1d2330;
560
+ --visual-a: rgba(21, 25, 35, 0.96);
561
+ --visual-b: rgba(22, 35, 39, 0.96);
562
+ --line: #364050;
563
+ --bar-bg: #2a3140;
564
+ --accent: #70d4e1;
565
+ --hot: #ff8276;
566
+ --gold: #f1c95e;
567
+ }
568
+ }