@jjlmoya/utils-cooking 1.27.0 → 1.29.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 (62) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +4 -0
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +2 -0
  5. package/src/tests/i18n-titles.test.ts +4 -2
  6. package/src/tests/lacto-fermentation-salt-calculator.test.ts +64 -0
  7. package/src/tests/locale_completeness.test.ts +2 -2
  8. package/src/tests/spherification-bath-calculator.test.ts +57 -0
  9. package/src/tests/tool_validation.test.ts +2 -2
  10. package/src/tool/lacto-fermentation-salt-calculator/bibliography.astro +6 -0
  11. package/src/tool/lacto-fermentation-salt-calculator/bibliography.ts +10 -0
  12. package/src/tool/lacto-fermentation-salt-calculator/component.astro +163 -0
  13. package/src/tool/lacto-fermentation-salt-calculator/components/DigitalScale.astro +43 -0
  14. package/src/tool/lacto-fermentation-salt-calculator/components/PetriDish.astro +55 -0
  15. package/src/tool/lacto-fermentation-salt-calculator/components/WizardMode.astro +83 -0
  16. package/src/tool/lacto-fermentation-salt-calculator/entry.ts +26 -0
  17. package/src/tool/lacto-fermentation-salt-calculator/i18n/de.ts +195 -0
  18. package/src/tool/lacto-fermentation-salt-calculator/i18n/en.ts +195 -0
  19. package/src/tool/lacto-fermentation-salt-calculator/i18n/es.ts +195 -0
  20. package/src/tool/lacto-fermentation-salt-calculator/i18n/fr.ts +195 -0
  21. package/src/tool/lacto-fermentation-salt-calculator/i18n/id.ts +195 -0
  22. package/src/tool/lacto-fermentation-salt-calculator/i18n/it.ts +195 -0
  23. package/src/tool/lacto-fermentation-salt-calculator/i18n/ja.ts +195 -0
  24. package/src/tool/lacto-fermentation-salt-calculator/i18n/ko.ts +195 -0
  25. package/src/tool/lacto-fermentation-salt-calculator/i18n/nl.ts +195 -0
  26. package/src/tool/lacto-fermentation-salt-calculator/i18n/pl.ts +195 -0
  27. package/src/tool/lacto-fermentation-salt-calculator/i18n/pt.ts +195 -0
  28. package/src/tool/lacto-fermentation-salt-calculator/i18n/ru.ts +195 -0
  29. package/src/tool/lacto-fermentation-salt-calculator/i18n/sv.ts +195 -0
  30. package/src/tool/lacto-fermentation-salt-calculator/i18n/tr.ts +195 -0
  31. package/src/tool/lacto-fermentation-salt-calculator/i18n/zh.ts +195 -0
  32. package/src/tool/lacto-fermentation-salt-calculator/index.ts +11 -0
  33. package/src/tool/lacto-fermentation-salt-calculator/lacto-fermentation-salt-calculator.css +692 -0
  34. package/src/tool/lacto-fermentation-salt-calculator/logic.ts +37 -0
  35. package/src/tool/lacto-fermentation-salt-calculator/seo.astro +15 -0
  36. package/src/tool/spherification-bath-calculator/bibliography.astro +6 -0
  37. package/src/tool/spherification-bath-calculator/bibliography.ts +10 -0
  38. package/src/tool/spherification-bath-calculator/component.astro +213 -0
  39. package/src/tool/spherification-bath-calculator/components/PrecisionControls.astro +85 -0
  40. package/src/tool/spherification-bath-calculator/components/RecipeSummary.astro +60 -0
  41. package/src/tool/spherification-bath-calculator/components/SpherificationReactor.astro +73 -0
  42. package/src/tool/spherification-bath-calculator/entry.ts +26 -0
  43. package/src/tool/spherification-bath-calculator/i18n/de.ts +180 -0
  44. package/src/tool/spherification-bath-calculator/i18n/en.ts +180 -0
  45. package/src/tool/spherification-bath-calculator/i18n/es.ts +179 -0
  46. package/src/tool/spherification-bath-calculator/i18n/fr.ts +179 -0
  47. package/src/tool/spherification-bath-calculator/i18n/id.ts +180 -0
  48. package/src/tool/spherification-bath-calculator/i18n/it.ts +180 -0
  49. package/src/tool/spherification-bath-calculator/i18n/ja.ts +180 -0
  50. package/src/tool/spherification-bath-calculator/i18n/ko.ts +180 -0
  51. package/src/tool/spherification-bath-calculator/i18n/nl.ts +180 -0
  52. package/src/tool/spherification-bath-calculator/i18n/pl.ts +180 -0
  53. package/src/tool/spherification-bath-calculator/i18n/pt.ts +180 -0
  54. package/src/tool/spherification-bath-calculator/i18n/ru.ts +180 -0
  55. package/src/tool/spherification-bath-calculator/i18n/sv.ts +180 -0
  56. package/src/tool/spherification-bath-calculator/i18n/tr.ts +180 -0
  57. package/src/tool/spherification-bath-calculator/i18n/zh.ts +180 -0
  58. package/src/tool/spherification-bath-calculator/index.ts +11 -0
  59. package/src/tool/spherification-bath-calculator/logic.ts +54 -0
  60. package/src/tool/spherification-bath-calculator/seo.astro +15 -0
  61. package/src/tool/spherification-bath-calculator/spherification-bath-calculator.css +568 -0
  62. package/src/tools.ts +4 -2
@@ -0,0 +1,692 @@
1
+ .fermentation-container {
2
+ --color-emerald: #10b981;
3
+ --color-emerald-dark: #059669;
4
+ --color-emerald-light: #34d399;
5
+ --color-emerald-lighter: #6ee7b7;
6
+ --color-purple: #8b5cf6;
7
+ --color-purple-dark: #6d28d9;
8
+ --color-purple-light: #a78bfa;
9
+ --color-red: #ef4444;
10
+ --color-red-dark: #b91c1c;
11
+ --color-red-light: #f87171;
12
+ --color-slate-dark: #0f172a;
13
+ --color-slate-medium: #1e293b;
14
+ --color-slate-light: #475569;
15
+ --color-slate-lighter: #64748b;
16
+ --color-slate-lightest: #94a3b8;
17
+ --color-slate-ultra-light: #cbd5e1;
18
+ --color-slate-white: #e2e8f0;
19
+ --color-slate-bg-light: #f1f5f9;
20
+ --color-slate-bg-lighter: #f8fafc;
21
+ --color-white: #fff;
22
+ --color-black: #000;
23
+
24
+ display: flex;
25
+ flex-direction: column;
26
+ align-items: center;
27
+ justify-content: center;
28
+ width: 100%;
29
+ box-sizing: border-box;
30
+ }
31
+
32
+ .fermentation-card {
33
+ position: relative;
34
+ width: 100%;
35
+ max-width: 900px;
36
+ background: rgba(255, 255, 255, 0.75);
37
+ backdrop-filter: blur(16px);
38
+ -webkit-backdrop-filter: blur(16px);
39
+ border: 1px solid rgba(0, 0, 0, 0.08);
40
+ border-radius: 24px;
41
+ padding: 2rem;
42
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.05);
43
+ overflow: hidden;
44
+ box-sizing: border-box;
45
+ }
46
+
47
+ .theme-dark .fermentation-card {
48
+ background: rgba(15, 23, 42, 0.45);
49
+ border: 1px solid rgba(255, 255, 255, 0.08);
50
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
51
+ }
52
+
53
+ .fermentation-glow-1 {
54
+ position: absolute;
55
+ top: -10%;
56
+ left: -10%;
57
+ width: 300px;
58
+ height: 300px;
59
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.1) 0%, transparent 70%);
60
+ z-index: 0;
61
+ pointer-events: none;
62
+ }
63
+
64
+ .theme-dark .fermentation-glow-1 {
65
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.15) 0%, transparent 70%);
66
+ }
67
+
68
+ .fermentation-glow-2 {
69
+ position: absolute;
70
+ bottom: -10%;
71
+ right: -10%;
72
+ width: 350px;
73
+ height: 350px;
74
+ background: radial-gradient(circle, rgba(139, 92, 246, 0.1) 0%, transparent 70%);
75
+ z-index: 0;
76
+ pointer-events: none;
77
+ }
78
+
79
+ .theme-dark .fermentation-glow-2 {
80
+ background: radial-gradient(circle, rgba(139, 92, 246, 0.15) 0%, transparent 70%);
81
+ }
82
+
83
+ .fermentation-grid {
84
+ position: relative;
85
+ z-index: 1;
86
+ display: grid;
87
+ grid-template-columns: 1.2fr 1fr;
88
+ gap: 2rem;
89
+ }
90
+
91
+ @media (max-width: 768px) {
92
+ .fermentation-grid {
93
+ grid-template-columns: 1fr;
94
+ }
95
+ }
96
+
97
+ .fermentation-controls-section {
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: 1.5rem;
101
+ }
102
+
103
+ .fermentation-wizard {
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: 1.5rem;
107
+ background: rgba(241, 245, 249, 0.6);
108
+ border: 1px solid rgba(0, 0, 0, 0.05);
109
+ border-radius: 16px;
110
+ padding: 1.5rem;
111
+ }
112
+
113
+ .theme-dark .fermentation-wizard {
114
+ background: rgba(30, 41, 59, 0.3);
115
+ border: 1px solid rgba(255, 255, 255, 0.05);
116
+ }
117
+
118
+ .wizard-control-group {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: 0.5rem;
122
+ }
123
+
124
+ .fermentation-wizard .wizard-control-group.hidden {
125
+ display: none;
126
+ }
127
+
128
+ .wizard-label {
129
+ font-size: 0.875rem;
130
+ font-weight: 600;
131
+ color: var(--color-slate-light);
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.05em;
134
+ }
135
+
136
+ .theme-dark .wizard-label {
137
+ color: var(--color-slate-ultra-light);
138
+ }
139
+
140
+ .mode-toggle-container, .unit-toggle-container {
141
+ display: grid;
142
+ grid-template-columns: 1fr 1fr;
143
+ background: rgba(226, 232, 240, 0.8);
144
+ border: 1px solid rgba(0, 0, 0, 0.06);
145
+ border-radius: 10px;
146
+ padding: 0.25rem;
147
+ }
148
+
149
+ .theme-dark .mode-toggle-container, .theme-dark .unit-toggle-container {
150
+ background: rgba(15, 23, 42, 0.6);
151
+ border: 1px solid rgba(255, 255, 255, 0.1);
152
+ }
153
+
154
+ .mode-btn {
155
+ background: transparent;
156
+ border: none;
157
+ color: var(--color-slate-lighter);
158
+ padding: 0.75rem;
159
+ border-radius: 8px;
160
+ font-size: 0.9rem;
161
+ font-weight: 600;
162
+ cursor: pointer;
163
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
164
+ }
165
+
166
+ .theme-dark .mode-btn {
167
+ color: var(--color-slate-lightest);
168
+ }
169
+
170
+ .mode-btn.active {
171
+ background: var(--color-emerald);
172
+ color: var(--color-white);
173
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2);
174
+ }
175
+
176
+ .wizard-number-input {
177
+ background: var(--color-white);
178
+ border: 1px solid rgba(0, 0, 0, 0.12);
179
+ border-radius: 8px;
180
+ color: var(--color-slate-dark);
181
+ padding: 0.75rem 1rem;
182
+ font-size: 1.125rem;
183
+ width: 100%;
184
+ box-sizing: border-box;
185
+ outline: none;
186
+ transition: border-color 0.2s;
187
+ }
188
+
189
+ .theme-dark .wizard-number-input {
190
+ background: rgba(15, 23, 42, 0.6);
191
+ border: 1px solid rgba(255, 255, 255, 0.1);
192
+ color: var(--color-white);
193
+ }
194
+
195
+ .wizard-number-input:focus {
196
+ border-color: var(--color-emerald);
197
+ }
198
+
199
+ .slider-header {
200
+ display: flex;
201
+ justify-content: space-between;
202
+ align-items: center;
203
+ }
204
+
205
+ .slider-val {
206
+ font-size: 1.125rem;
207
+ font-weight: 700;
208
+ color: var(--color-emerald);
209
+ }
210
+
211
+ .wizard-slider {
212
+ -webkit-appearance: none;
213
+ appearance: none;
214
+ width: 100%;
215
+ height: 6px;
216
+ border-radius: 3px;
217
+ background: var(--color-slate-white);
218
+ outline: none;
219
+ margin: 1rem 0;
220
+ }
221
+
222
+ .theme-dark .wizard-slider {
223
+ background: rgba(15, 23, 42, 0.8);
224
+ }
225
+
226
+ .wizard-slider::-webkit-slider-thumb {
227
+ -webkit-appearance: none;
228
+ appearance: none;
229
+ width: 20px;
230
+ height: 20px;
231
+ border-radius: 50%;
232
+ background: var(--color-emerald);
233
+ cursor: pointer;
234
+ box-shadow: 0 0 10px rgba(16, 185, 129, 0.3);
235
+ transition: transform 0.1s;
236
+ }
237
+
238
+ .theme-dark .wizard-slider::-webkit-slider-thumb {
239
+ box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
240
+ }
241
+
242
+ .wizard-slider::-webkit-slider-thumb:hover {
243
+ transform: scale(1.2);
244
+ }
245
+
246
+ .digital-scale-container {
247
+ background: rgba(255, 255, 255, 0.6);
248
+ border: 1px solid rgba(0, 0, 0, 0.05);
249
+ border-radius: 16px;
250
+ padding: 1.5rem;
251
+ display: flex;
252
+ flex-direction: column;
253
+ align-items: center;
254
+ gap: 1rem;
255
+ }
256
+
257
+ .theme-dark .digital-scale-container {
258
+ background: rgba(15, 23, 42, 0.5);
259
+ border: 1px solid rgba(255, 255, 255, 0.05);
260
+ }
261
+
262
+ .scale-plate {
263
+ width: 100%;
264
+ background: linear-gradient(135deg, var(--color-slate-bg-light) 0%, var(--color-slate-white) 100%);
265
+ border: 1px solid rgba(0, 0, 0, 0.08);
266
+ border-radius: 16px;
267
+ padding: 1rem;
268
+ box-sizing: border-box;
269
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02), inset 0 2px 4px var(--color-white);
270
+ display: flex;
271
+ justify-content: center;
272
+ align-items: center;
273
+ }
274
+
275
+ .theme-dark .scale-plate {
276
+ background: linear-gradient(135deg, var(--color-slate-medium) 0%, var(--color-slate-dark) 100%);
277
+ border: 1px solid rgba(255, 255, 255, 0.05);
278
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
279
+ }
280
+
281
+ .scale-display {
282
+ display: flex;
283
+ flex-direction: column;
284
+ align-items: center;
285
+ background: rgba(16, 185, 129, 0.05);
286
+ border: 1px solid rgba(16, 185, 129, 0.15);
287
+ border-radius: 12px;
288
+ padding: 1rem 2rem;
289
+ width: 100%;
290
+ box-sizing: border-box;
291
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02);
292
+ }
293
+
294
+ .theme-dark .scale-display {
295
+ background: rgba(16, 185, 129, 0.03);
296
+ border: 1px solid rgba(16, 185, 129, 0.2);
297
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.5);
298
+ }
299
+
300
+ .scale-digits {
301
+ font-size: 3.25rem;
302
+ font-weight: 800;
303
+ color: var(--color-emerald-dark);
304
+ letter-spacing: -0.02em;
305
+ display: flex;
306
+ align-items: baseline;
307
+ line-height: 1;
308
+ }
309
+
310
+ .theme-dark .scale-digits {
311
+ color: var(--color-emerald);
312
+ text-shadow: 0 0 12px rgba(16, 185, 129, 0.4);
313
+ }
314
+
315
+ .scale-unit {
316
+ font-size: 1.25rem;
317
+ margin-left: 0.25rem;
318
+ color: var(--color-slate-light);
319
+ }
320
+
321
+ .theme-dark .scale-unit {
322
+ color: var(--color-emerald-dark);
323
+ }
324
+
325
+ .scale-label {
326
+ font-size: 0.7rem;
327
+ color: var(--color-slate-lighter);
328
+ text-transform: uppercase;
329
+ letter-spacing: 0.05em;
330
+ margin-top: 0.5rem;
331
+ }
332
+
333
+ .theme-dark .scale-label {
334
+ color: var(--color-slate-light);
335
+ }
336
+
337
+ .scale-conversions {
338
+ width: 100%;
339
+ display: grid;
340
+ grid-template-columns: 1fr 1fr;
341
+ gap: 1rem;
342
+ }
343
+
344
+ .conversion-item {
345
+ background: rgba(241, 245, 249, 0.8);
346
+ border: 1px solid rgba(0, 0, 0, 0.04);
347
+ border-radius: 10px;
348
+ padding: 0.75rem;
349
+ display: flex;
350
+ flex-direction: column;
351
+ align-items: center;
352
+ }
353
+
354
+ .theme-dark .conversion-item {
355
+ background: rgba(30, 41, 59, 0.3);
356
+ border: 1px solid rgba(255, 255, 255, 0.03);
357
+ }
358
+
359
+ .conversion-label {
360
+ font-size: 0.75rem;
361
+ color: var(--color-slate-lighter);
362
+ }
363
+
364
+ .theme-dark .conversion-label {
365
+ color: var(--color-slate-lightest);
366
+ }
367
+
368
+ .conversion-val {
369
+ font-size: 1.25rem;
370
+ font-weight: 700;
371
+ color: var(--color-slate-dark);
372
+ }
373
+
374
+ .theme-dark .conversion-val {
375
+ color: var(--color-slate-white);
376
+ }
377
+
378
+ .conversion-unit {
379
+ font-size: 0.8rem;
380
+ color: var(--color-slate-lighter);
381
+ margin-left: 0.15rem;
382
+ }
383
+
384
+ .scale-disclaimer {
385
+ font-size: 0.75rem;
386
+ color: var(--color-slate-lighter);
387
+ text-align: center;
388
+ margin: 0;
389
+ }
390
+
391
+ .petri-dish-section {
392
+ display: flex;
393
+ flex-direction: column;
394
+ align-items: center;
395
+ background: rgba(255, 255, 255, 0.5);
396
+ border: 1px solid rgba(0, 0, 0, 0.05);
397
+ border-radius: 16px;
398
+ padding: 1.5rem;
399
+ height: 100%;
400
+ box-sizing: border-box;
401
+ }
402
+
403
+ .theme-dark .petri-dish-section {
404
+ background: rgba(15, 23, 42, 0.3);
405
+ border: 1px solid rgba(255, 255, 255, 0.05);
406
+ }
407
+
408
+ .petri-title {
409
+ font-size: 0.75rem;
410
+ font-weight: 700;
411
+ text-transform: uppercase;
412
+ letter-spacing: 0.1em;
413
+ color: var(--color-slate-lighter);
414
+ margin-bottom: 1rem;
415
+ }
416
+
417
+ .petri-container {
418
+ display: flex;
419
+ justify-content: center;
420
+ align-items: center;
421
+ flex-grow: 1;
422
+ margin: 1.5rem 0;
423
+ }
424
+
425
+ .petri-dish {
426
+ position: relative;
427
+ width: 180px;
428
+ height: 180px;
429
+ border-radius: 50%;
430
+ border: 4px solid rgba(0, 0, 0, 0.08);
431
+ background: rgba(255, 255, 255, 0.9);
432
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05), inset 0 0 15px rgba(0, 0, 0, 0.02);
433
+ display: flex;
434
+ align-items: center;
435
+ justify-content: center;
436
+ }
437
+
438
+ .theme-dark .petri-dish {
439
+ border: 4px solid rgba(255, 255, 255, 0.12);
440
+ background: rgba(15, 23, 42, 0.8);
441
+ box-shadow: 0 0 25px rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(255, 255, 255, 0.05);
442
+ }
443
+
444
+ .petri-agar {
445
+ width: 160px;
446
+ height: 160px;
447
+ border-radius: 50%;
448
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.06) 0%, rgba(139, 92, 246, 0.02) 100%);
449
+ position: relative;
450
+ overflow: hidden;
451
+ transition: background 0.3s;
452
+ }
453
+
454
+ .theme-dark .petri-agar {
455
+ background: radial-gradient(circle, rgba(16, 185, 129, 0.08) 0%, rgba(139, 92, 246, 0.03) 100%);
456
+ }
457
+
458
+ .petri-svg {
459
+ width: 100%;
460
+ height: 100%;
461
+ }
462
+
463
+ .bacteria-layer, .mold-layer, .crystals-layer {
464
+ transition: opacity 0.4s ease-in-out;
465
+ }
466
+
467
+ .bacteria-layer {
468
+ opacity: 0;
469
+ }
470
+
471
+ .mold-layer {
472
+ opacity: 0;
473
+ }
474
+
475
+ .crystals-layer {
476
+ opacity: 0;
477
+ }
478
+
479
+ .bac-cell {
480
+ fill: var(--color-emerald-dark);
481
+ opacity: 0.8;
482
+ animation: float-cell 6s infinite ease-in-out alternate;
483
+ }
484
+
485
+ .theme-dark .bac-cell {
486
+ fill: var(--color-emerald);
487
+ }
488
+
489
+ .bac-cell:nth-child(2) {
490
+ animation-delay: -2s;
491
+ animation-duration: 8s;
492
+ fill: var(--color-emerald);
493
+ }
494
+
495
+ .theme-dark .bac-cell:nth-child(2) {
496
+ fill: var(--color-emerald-light);
497
+ }
498
+
499
+ .bac-cell:nth-child(3) {
500
+ animation-delay: -4s;
501
+ animation-duration: 7s;
502
+ fill: var(--color-emerald-dark);
503
+ }
504
+
505
+ .theme-dark .bac-cell:nth-child(3) {
506
+ fill: var(--color-emerald-dark);
507
+ }
508
+
509
+ .bac-coccus {
510
+ fill: var(--color-emerald-light);
511
+ opacity: 0.7;
512
+ animation: float-cell 5s infinite ease-in-out alternate-reverse;
513
+ }
514
+
515
+ .theme-dark .bac-coccus {
516
+ fill: var(--color-emerald-lighter);
517
+ }
518
+
519
+ .bac-coccus:nth-child(5) {
520
+ animation-delay: -1.5s;
521
+ animation-duration: 9s;
522
+ }
523
+
524
+ .bac-coccus:nth-child(6) {
525
+ animation-delay: -3s;
526
+ animation-duration: 6s;
527
+ }
528
+
529
+ .mold-fungus {
530
+ stroke: var(--color-red-dark);
531
+ opacity: 0.8;
532
+ animation: float-mold 10s infinite linear;
533
+ }
534
+
535
+ .theme-dark .mold-fungus {
536
+ stroke: var(--color-red);
537
+ }
538
+
539
+ .mold-fungus:nth-child(2) {
540
+ animation-delay: -3s;
541
+ stroke: var(--color-red);
542
+ }
543
+
544
+ .theme-dark .mold-fungus:nth-child(2) {
545
+ stroke: var(--color-red-light);
546
+ }
547
+
548
+ .mold-fungus:nth-child(3) {
549
+ animation-delay: -6s;
550
+ stroke: var(--color-red-dark);
551
+ }
552
+
553
+ .theme-dark .mold-fungus:nth-child(3) {
554
+ stroke: var(--color-red-dark);
555
+ }
556
+
557
+ .mold-spore {
558
+ fill: var(--color-red-dark);
559
+ opacity: 0.6;
560
+ }
561
+
562
+ .theme-dark .mold-spore {
563
+ fill: var(--color-red);
564
+ }
565
+
566
+ .crystal-cube {
567
+ fill: var(--color-purple);
568
+ stroke: rgba(0, 0, 0, 0.1);
569
+ stroke-width: 1;
570
+ opacity: 0.75;
571
+ animation: rotate-crystal 12s infinite linear;
572
+ transform-origin: center;
573
+ }
574
+
575
+ .theme-dark .crystal-cube {
576
+ fill: var(--color-purple);
577
+ stroke: rgba(255, 255, 255, 0.2);
578
+ }
579
+
580
+ .crystal-cube:nth-child(2) {
581
+ animation-delay: -4s;
582
+ fill: var(--color-purple);
583
+ }
584
+
585
+ .theme-dark .crystal-cube:nth-child(2) {
586
+ fill: var(--color-purple-light);
587
+ }
588
+
589
+ .crystal-cube:nth-child(3) {
590
+ animation-delay: -8s;
591
+ fill: var(--color-purple-dark);
592
+ }
593
+
594
+ .theme-dark .crystal-cube:nth-child(3) {
595
+ fill: var(--color-purple);
596
+ }
597
+
598
+ .status-box {
599
+ width: 100%;
600
+ text-align: center;
601
+ }
602
+
603
+ .status-badge {
604
+ display: inline-block;
605
+ padding: 0.35rem 0.75rem;
606
+ border-radius: 9999px;
607
+ font-size: 0.75rem;
608
+ font-weight: 700;
609
+ text-transform: uppercase;
610
+ letter-spacing: 0.05em;
611
+ margin-bottom: 0.75rem;
612
+ }
613
+
614
+ .status-badge.danger {
615
+ background: rgba(239, 68, 68, 0.1);
616
+ border: 1px solid rgba(239, 68, 68, 0.2);
617
+ color: var(--color-red-dark);
618
+ }
619
+
620
+ .theme-dark .status-badge.danger {
621
+ background: rgba(239, 68, 68, 0.15);
622
+ border: 1px solid rgba(239, 68, 68, 0.3);
623
+ color: var(--color-red-light);
624
+ box-shadow: 0 0 10px rgba(239, 68, 68, 0.1);
625
+ }
626
+
627
+ .status-badge.optimal {
628
+ background: rgba(16, 185, 129, 0.1);
629
+ border: 1px solid rgba(16, 185, 129, 0.2);
630
+ color: var(--color-emerald-dark);
631
+ }
632
+
633
+ .theme-dark .status-badge.optimal {
634
+ background: rgba(16, 185, 129, 0.15);
635
+ border: 1px solid rgba(16, 185, 129, 0.3);
636
+ color: var(--color-emerald-light);
637
+ box-shadow: 0 0 10px rgba(16, 185, 129, 0.1);
638
+ }
639
+
640
+ .status-badge.inhibited {
641
+ background: rgba(139, 92, 246, 0.1);
642
+ border: 1px solid rgba(139, 92, 246, 0.2);
643
+ color: var(--color-purple-dark);
644
+ }
645
+
646
+ .theme-dark .status-badge.inhibited {
647
+ background: rgba(139, 92, 246, 0.15);
648
+ border: 1px solid rgba(139, 92, 246, 0.3);
649
+ color: var(--color-purple-light);
650
+ box-shadow: 0 0 10px rgba(139, 92, 246, 0.1);
651
+ }
652
+
653
+ .status-desc-text {
654
+ font-size: 0.8rem;
655
+ color: var(--color-slate-light);
656
+ margin: 0;
657
+ line-height: 1.4;
658
+ }
659
+
660
+ .theme-dark .status-desc-text {
661
+ color: var(--color-slate-lightest);
662
+ }
663
+
664
+ @keyframes float-cell {
665
+ 0% {
666
+ transform: translate(0, 0) rotate(0deg);
667
+ }
668
+ 100% {
669
+ transform: translate(5px, 8px) rotate(10deg);
670
+ }
671
+ }
672
+
673
+ @keyframes float-mold {
674
+ 0% {
675
+ transform: translate(0, 0) scale(1);
676
+ }
677
+ 50% {
678
+ transform: translate(3px, -3px) scale(1.05);
679
+ }
680
+ 100% {
681
+ transform: translate(0, 0) scale(1);
682
+ }
683
+ }
684
+
685
+ @keyframes rotate-crystal {
686
+ 0% {
687
+ transform: rotate(0deg);
688
+ }
689
+ 100% {
690
+ transform: rotate(360deg);
691
+ }
692
+ }
@@ -0,0 +1,37 @@
1
+ export interface FermentationInput {
2
+ vegWeight: number;
3
+ waterWeight: number;
4
+ salinity: number;
5
+ mode: 'dry' | 'wet';
6
+ }
7
+
8
+ export interface FermentationResult {
9
+ saltGrams: number;
10
+ status: 'danger' | 'optimal' | 'inhibited';
11
+ fineTeaspoons: number;
12
+ kosherTeaspoons: number;
13
+ }
14
+
15
+ export class FermentationLogic {
16
+ static calculate(input: FermentationInput): FermentationResult {
17
+ const totalWeight = input.mode === 'dry' ? input.vegWeight : (input.vegWeight + input.waterWeight);
18
+ const saltGrams = Math.max(0, parseFloat(((totalWeight * input.salinity) / 100).toFixed(2)));
19
+
20
+ let status: 'danger' | 'optimal' | 'inhibited' = 'optimal';
21
+ if (input.salinity < 2.0) {
22
+ status = 'danger';
23
+ } else if (input.salinity > 3.5) {
24
+ status = 'inhibited';
25
+ }
26
+
27
+ const fineTeaspoons = Math.max(0, parseFloat((saltGrams / 5.7).toFixed(1)));
28
+ const kosherTeaspoons = Math.max(0, parseFloat((saltGrams / 4.3).toFixed(1)));
29
+
30
+ return {
31
+ saltGrams,
32
+ status,
33
+ fineTeaspoons,
34
+ kosherTeaspoons,
35
+ };
36
+ }
37
+ }