@jjlmoya/utils-health 1.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 (155) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +60 -0
  3. package/src/category/i18n/es.ts +60 -0
  4. package/src/category/i18n/fr.ts +60 -0
  5. package/src/category/index.ts +22 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +28 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +36 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +22 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/binauralTuner/bibliography.astro +14 -0
  25. package/src/tool/binauralTuner/component.astro +687 -0
  26. package/src/tool/binauralTuner/i18n/en.ts +187 -0
  27. package/src/tool/binauralTuner/i18n/es.ts +187 -0
  28. package/src/tool/binauralTuner/i18n/fr.ts +187 -0
  29. package/src/tool/binauralTuner/index.ts +27 -0
  30. package/src/tool/binauralTuner/seo.astro +14 -0
  31. package/src/tool/binauralTuner/ui.ts +18 -0
  32. package/src/tool/bloodUnitConverter/bibliography.astro +14 -0
  33. package/src/tool/bloodUnitConverter/component.astro +915 -0
  34. package/src/tool/bloodUnitConverter/i18n/en.ts +227 -0
  35. package/src/tool/bloodUnitConverter/i18n/es.ts +250 -0
  36. package/src/tool/bloodUnitConverter/i18n/fr.ts +218 -0
  37. package/src/tool/bloodUnitConverter/index.ts +27 -0
  38. package/src/tool/bloodUnitConverter/seo.astro +14 -0
  39. package/src/tool/bloodUnitConverter/ui.ts +38 -0
  40. package/src/tool/bmiCalculator/bibliography.astro +14 -0
  41. package/src/tool/bmiCalculator/component.astro +415 -0
  42. package/src/tool/bmiCalculator/i18n/en.ts +217 -0
  43. package/src/tool/bmiCalculator/i18n/es.ts +221 -0
  44. package/src/tool/bmiCalculator/i18n/fr.ts +217 -0
  45. package/src/tool/bmiCalculator/index.ts +27 -0
  46. package/src/tool/bmiCalculator/seo.astro +14 -0
  47. package/src/tool/bmiCalculator/ui.ts +21 -0
  48. package/src/tool/breathingVisualizer/bibliography.astro +14 -0
  49. package/src/tool/breathingVisualizer/component.astro +636 -0
  50. package/src/tool/breathingVisualizer/i18n/en.ts +206 -0
  51. package/src/tool/breathingVisualizer/i18n/es.ts +206 -0
  52. package/src/tool/breathingVisualizer/i18n/fr.ts +206 -0
  53. package/src/tool/breathingVisualizer/index.ts +27 -0
  54. package/src/tool/breathingVisualizer/seo.astro +14 -0
  55. package/src/tool/breathingVisualizer/ui.ts +31 -0
  56. package/src/tool/caffeineTracker/bibliography.astro +14 -0
  57. package/src/tool/caffeineTracker/component.astro +1210 -0
  58. package/src/tool/caffeineTracker/i18n/en.ts +198 -0
  59. package/src/tool/caffeineTracker/i18n/es.ts +198 -0
  60. package/src/tool/caffeineTracker/i18n/fr.ts +198 -0
  61. package/src/tool/caffeineTracker/index.ts +27 -0
  62. package/src/tool/caffeineTracker/logic.ts +31 -0
  63. package/src/tool/caffeineTracker/seo.astro +14 -0
  64. package/src/tool/caffeineTracker/ui.ts +36 -0
  65. package/src/tool/daltonismSimulator/bibliography.astro +14 -0
  66. package/src/tool/daltonismSimulator/component.astro +383 -0
  67. package/src/tool/daltonismSimulator/i18n/en.ts +188 -0
  68. package/src/tool/daltonismSimulator/i18n/es.ts +218 -0
  69. package/src/tool/daltonismSimulator/i18n/fr.ts +168 -0
  70. package/src/tool/daltonismSimulator/index.ts +27 -0
  71. package/src/tool/daltonismSimulator/seo.astro +14 -0
  72. package/src/tool/daltonismSimulator/ui.ts +20 -0
  73. package/src/tool/digestionStopwatch/bibliography.astro +14 -0
  74. package/src/tool/digestionStopwatch/component.astro +627 -0
  75. package/src/tool/digestionStopwatch/i18n/en.ts +173 -0
  76. package/src/tool/digestionStopwatch/i18n/es.ts +173 -0
  77. package/src/tool/digestionStopwatch/i18n/fr.ts +173 -0
  78. package/src/tool/digestionStopwatch/index.ts +27 -0
  79. package/src/tool/digestionStopwatch/logic.ts +63 -0
  80. package/src/tool/digestionStopwatch/seo.astro +14 -0
  81. package/src/tool/digestionStopwatch/ui.ts +20 -0
  82. package/src/tool/epworthSleepinessScale/bibliography.astro +14 -0
  83. package/src/tool/epworthSleepinessScale/component.astro +528 -0
  84. package/src/tool/epworthSleepinessScale/i18n/en.ts +217 -0
  85. package/src/tool/epworthSleepinessScale/i18n/es.ts +217 -0
  86. package/src/tool/epworthSleepinessScale/i18n/fr.ts +217 -0
  87. package/src/tool/epworthSleepinessScale/index.ts +27 -0
  88. package/src/tool/epworthSleepinessScale/seo.astro +14 -0
  89. package/src/tool/epworthSleepinessScale/ui.ts +27 -0
  90. package/src/tool/hydrationCalculator/bibliography.astro +14 -0
  91. package/src/tool/hydrationCalculator/component.astro +694 -0
  92. package/src/tool/hydrationCalculator/i18n/en.ts +217 -0
  93. package/src/tool/hydrationCalculator/i18n/es.ts +222 -0
  94. package/src/tool/hydrationCalculator/i18n/fr.ts +199 -0
  95. package/src/tool/hydrationCalculator/index.ts +27 -0
  96. package/src/tool/hydrationCalculator/seo.astro +14 -0
  97. package/src/tool/hydrationCalculator/ui.ts +28 -0
  98. package/src/tool/pelliRobsonTest/bibliography.astro +14 -0
  99. package/src/tool/pelliRobsonTest/component.astro +653 -0
  100. package/src/tool/pelliRobsonTest/i18n/en.ts +205 -0
  101. package/src/tool/pelliRobsonTest/i18n/es.ts +205 -0
  102. package/src/tool/pelliRobsonTest/i18n/fr.ts +205 -0
  103. package/src/tool/pelliRobsonTest/index.ts +27 -0
  104. package/src/tool/pelliRobsonTest/seo.astro +14 -0
  105. package/src/tool/pelliRobsonTest/ui.ts +21 -0
  106. package/src/tool/peripheralVisionTrainer/bibliography.astro +14 -0
  107. package/src/tool/peripheralVisionTrainer/component.astro +678 -0
  108. package/src/tool/peripheralVisionTrainer/i18n/en.ts +224 -0
  109. package/src/tool/peripheralVisionTrainer/i18n/es.ts +224 -0
  110. package/src/tool/peripheralVisionTrainer/i18n/fr.ts +211 -0
  111. package/src/tool/peripheralVisionTrainer/index.ts +27 -0
  112. package/src/tool/peripheralVisionTrainer/seo.astro +14 -0
  113. package/src/tool/peripheralVisionTrainer/ui.ts +26 -0
  114. package/src/tool/readingDistanceCalculator/bibliography.astro +14 -0
  115. package/src/tool/readingDistanceCalculator/component.astro +588 -0
  116. package/src/tool/readingDistanceCalculator/i18n/en.ts +202 -0
  117. package/src/tool/readingDistanceCalculator/i18n/es.ts +215 -0
  118. package/src/tool/readingDistanceCalculator/i18n/fr.ts +193 -0
  119. package/src/tool/readingDistanceCalculator/index.ts +31 -0
  120. package/src/tool/readingDistanceCalculator/seo.astro +14 -0
  121. package/src/tool/readingDistanceCalculator/ui.ts +18 -0
  122. package/src/tool/screenDecompressionTime/bibliography.astro +14 -0
  123. package/src/tool/screenDecompressionTime/component.astro +671 -0
  124. package/src/tool/screenDecompressionTime/i18n/en.ts +225 -0
  125. package/src/tool/screenDecompressionTime/i18n/es.ts +247 -0
  126. package/src/tool/screenDecompressionTime/i18n/fr.ts +225 -0
  127. package/src/tool/screenDecompressionTime/index.ts +27 -0
  128. package/src/tool/screenDecompressionTime/seo.astro +14 -0
  129. package/src/tool/screenDecompressionTime/ui.ts +32 -0
  130. package/src/tool/tinnitusReliever/bibliography.astro +14 -0
  131. package/src/tool/tinnitusReliever/component.astro +581 -0
  132. package/src/tool/tinnitusReliever/i18n/en.ts +161 -0
  133. package/src/tool/tinnitusReliever/i18n/es.ts +161 -0
  134. package/src/tool/tinnitusReliever/i18n/fr.ts +161 -0
  135. package/src/tool/tinnitusReliever/index.ts +27 -0
  136. package/src/tool/tinnitusReliever/seo.astro +14 -0
  137. package/src/tool/tinnitusReliever/ui.ts +9 -0
  138. package/src/tool/ubeCalculator/bibliography.astro +14 -0
  139. package/src/tool/ubeCalculator/component.astro +683 -0
  140. package/src/tool/ubeCalculator/i18n/en.ts +200 -0
  141. package/src/tool/ubeCalculator/i18n/es.ts +200 -0
  142. package/src/tool/ubeCalculator/i18n/fr.ts +196 -0
  143. package/src/tool/ubeCalculator/index.ts +27 -0
  144. package/src/tool/ubeCalculator/seo.astro +14 -0
  145. package/src/tool/ubeCalculator/ui.ts +26 -0
  146. package/src/tool/waterPurifier/bibliography.astro +14 -0
  147. package/src/tool/waterPurifier/component.astro +628 -0
  148. package/src/tool/waterPurifier/i18n/en.ts +167 -0
  149. package/src/tool/waterPurifier/i18n/es.ts +167 -0
  150. package/src/tool/waterPurifier/i18n/fr.ts +167 -0
  151. package/src/tool/waterPurifier/index.ts +27 -0
  152. package/src/tool/waterPurifier/seo.astro +14 -0
  153. package/src/tool/waterPurifier/ui.ts +18 -0
  154. package/src/tools.ts +19 -0
  155. package/src/types.ts +72 -0
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography } from '@jjlmoya/utils-shared';
3
+ import { waterPurifier } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await waterPurifier.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <Bibliography items={content.bibliography} title={content.bibliographyTitle} />}
@@ -0,0 +1,628 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { WaterPurifierUI } from './ui';
4
+
5
+ interface Props {
6
+ ui?: Record<string, unknown>;
7
+ }
8
+
9
+ const ui = (Astro.props.ui ?? {}) as WaterPurifierUI;
10
+ ---
11
+
12
+ <section class="wp" id="wp-container" data-ui={JSON.stringify(ui)}>
13
+
14
+ <div class="wp__modal" id="wp-modal">
15
+ <div class="wp__modal-box">
16
+ <div class="wp__modal-icon-wrap">
17
+ <Icon name="mdi:alert-octagon" class="wp__modal-icon" />
18
+ </div>
19
+ <h3 class="wp__modal-title">{ui.disclaimerTitle}</h3>
20
+ <p class="wp__modal-body">
21
+ {ui.disclaimerText1} <strong class="wp__modal-highlight">{ui.disclaimerHighlight}</strong>.
22
+ <br /><br />
23
+ {ui.disclaimerText2}
24
+ </p>
25
+ <button class="wp__modal-btn" id="wp-accept-btn">
26
+ {ui.disclaimerBtn}
27
+ </button>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="wp__grid">
32
+
33
+ <div class="wp__card wp__card--params">
34
+ <div class="wp__card-header">
35
+ <div class="wp__card-icon-wrap">
36
+ <Icon name="mdi:water-plus" class="wp__card-icon" />
37
+ </div>
38
+ <h2 class="wp__card-title">{ui.paramsSectionTitle}</h2>
39
+ </div>
40
+
41
+ <div class="wp__params">
42
+ <div class="wp__field">
43
+ <span class="wp__field-label">{ui.conditionLabel}</span>
44
+ <div class="wp__condition-grid">
45
+ <label class="wp__condition-option">
46
+ <input type="radio" name="wp-condition" value="clear" checked class="wp__condition-radio" />
47
+ <div class="wp__condition-card">
48
+ <Icon name="mdi:water" class="wp__condition-icon" />
49
+ <span class="wp__condition-label">{ui.conditionClear}</span>
50
+ </div>
51
+ </label>
52
+ <label class="wp__condition-option">
53
+ <input type="radio" name="wp-condition" value="cloudy" class="wp__condition-radio" />
54
+ <div class="wp__condition-card">
55
+ <Icon name="mdi:weather-cloudy" class="wp__condition-icon" />
56
+ <span class="wp__condition-label">{ui.conditionCloudy}</span>
57
+ </div>
58
+ </label>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="wp__field">
63
+ <label for="wp-liters" class="wp__field-label">{ui.litersLabel}</label>
64
+ <div class="wp__input-wrap">
65
+ <input
66
+ type="number"
67
+ id="wp-liters"
68
+ value="1"
69
+ min="0.1"
70
+ step="0.1"
71
+ class="wp__input"
72
+ />
73
+ <span class="wp__input-unit">L</span>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <div class="wp__card wp__card--result">
80
+ <div class="wp__drop-area">
81
+ <div class="wp__dropper-wrap">
82
+ <Icon name="mdi:eyedropper" class="wp__dropper-icon" />
83
+ </div>
84
+ <div class="wp__drop" id="wp-drop"></div>
85
+ <div class="wp__drop-surface"></div>
86
+ </div>
87
+
88
+ <div class="wp__result">
89
+ <p class="wp__result-label">{ui.resultLabel}</p>
90
+ <div class="wp__result-amount">
91
+ <span class="wp__result-number" id="wp-result-drops">2</span>
92
+ <span class="wp__result-unit">{ui.resultUnit}</span>
93
+ </div>
94
+ <p class="wp__result-ml" id="wp-result-ml">~ 0.10 ml</p>
95
+
96
+ <div class="wp__wait-box">
97
+ <div class="wp__wait-header">
98
+ <Icon name="mdi:clock-outline" class="wp__wait-icon" />
99
+ <span class="wp__wait-title">{ui.waitTitle}</span>
100
+ </div>
101
+ <p class="wp__wait-text">
102
+ {ui.waitBefore} <strong class="wp__wait-duration">{ui.waitDuration}</strong> {ui.waitAfter}
103
+ </p>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ </div>
109
+ </section>
110
+
111
+ <script>
112
+ const DROPS_PER_ML = 20;
113
+ const BL_CLEAR = 2;
114
+ const BL_CLOUDY = 4;
115
+
116
+ const container = document.getElementById('wp-container');
117
+ const modal = document.getElementById('wp-modal');
118
+ const acceptBtn = document.getElementById('wp-accept-btn');
119
+ const litersInput = container?.querySelector<HTMLInputElement>('#wp-liters');
120
+ const resultDrops = container?.querySelector<HTMLElement>('#wp-result-drops');
121
+ const resultMl = container?.querySelector<HTMLElement>('#wp-result-ml');
122
+ const dropEl = container?.querySelector<HTMLElement>('#wp-drop');
123
+
124
+ function initModal() {
125
+ if (!modal) return;
126
+ if (localStorage.getItem('water-purifier-accepted')) {
127
+ modal.classList.add('wp__modal--hidden');
128
+ }
129
+ }
130
+
131
+ function handleAccept() {
132
+ const el = modal;
133
+ if (!el) return;
134
+ el.style.opacity = '0';
135
+ setTimeout(() => el.classList.add('wp__modal--hidden'), 300);
136
+ localStorage.setItem('water-purifier-accepted', 'true');
137
+ }
138
+
139
+ function getConditionFactor(): number {
140
+ const radios = container?.querySelectorAll<HTMLInputElement>('input[name="wp-condition"]');
141
+ if (!radios) return BL_CLEAR;
142
+ for (const radio of radios) {
143
+ if (radio.checked && radio.value === 'cloudy') return BL_CLOUDY;
144
+ }
145
+ return BL_CLEAR;
146
+ }
147
+
148
+ function triggerDropAnimation() {
149
+ if (!dropEl) return;
150
+ dropEl.classList.remove('wp__drop--animating');
151
+ requestAnimationFrame(() => {
152
+ requestAnimationFrame(() => {
153
+ dropEl.classList.add('wp__drop--animating');
154
+ });
155
+ });
156
+ }
157
+
158
+ function calculate() {
159
+ if (!litersInput || !resultDrops || !resultMl) return;
160
+ const liters = parseFloat(litersInput.value) || 0;
161
+ const factor = getConditionFactor();
162
+ const drops = Math.ceil(liters * factor);
163
+ const ml = drops / DROPS_PER_ML;
164
+ resultDrops.textContent = drops.toString();
165
+ resultMl.textContent = `~ ${ml.toFixed(2)} ml`;
166
+ triggerDropAnimation();
167
+ }
168
+
169
+ initModal();
170
+ acceptBtn?.addEventListener('click', handleAccept);
171
+ litersInput?.addEventListener('input', calculate);
172
+ container?.querySelectorAll<HTMLInputElement>('input[name="wp-condition"]').forEach((radio) => {
173
+ radio.addEventListener('change', calculate);
174
+ });
175
+ calculate();
176
+ </script>
177
+
178
+ <style>
179
+ .wp {
180
+ --wp-primary: #0891b2;
181
+ --wp-primary-dark: #0e7490;
182
+ --wp-primary-light: #ecfeff;
183
+ --wp-selected-bg: rgba(8, 145, 178, 0.08);
184
+ --wp-card-bg: #fff;
185
+ --wp-border: #e5e7eb;
186
+ --wp-text: #111827;
187
+ --wp-text-muted: #6b7280;
188
+ --wp-input-bg: #f9fafb;
189
+ --wp-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
190
+
191
+ max-width: 56rem;
192
+ margin: 0 auto;
193
+ padding: 0 1rem;
194
+ position: relative;
195
+ }
196
+
197
+ :global(.theme-dark) .wp {
198
+ --wp-card-bg: #1f2937;
199
+ --wp-border: #374151;
200
+ --wp-text: #f9fafb;
201
+ --wp-text-muted: #9ca3af;
202
+ --wp-input-bg: #111827;
203
+ --wp-primary-light: rgba(8, 145, 178, 0.15);
204
+ --wp-selected-bg: rgba(8, 145, 178, 0.18);
205
+ --wp-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
206
+ }
207
+
208
+
209
+ .wp__modal {
210
+ position: fixed;
211
+ inset: 0;
212
+ z-index: 50;
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: center;
216
+ padding: 1rem;
217
+ background: rgba(0, 0, 0, 0.82);
218
+ backdrop-filter: blur(4px);
219
+ transition: opacity 0.3s;
220
+ }
221
+
222
+ .wp__modal--hidden {
223
+ display: none;
224
+ }
225
+
226
+ .wp__modal-box {
227
+ background: var(--wp-card-bg);
228
+ border-radius: 1.25rem;
229
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
230
+ max-width: 28rem;
231
+ width: 100%;
232
+ padding: 2rem;
233
+ border: 1px solid rgba(239, 68, 68, 0.2);
234
+ }
235
+
236
+ .wp__modal-icon-wrap {
237
+ width: 4rem;
238
+ height: 4rem;
239
+ background: rgba(239, 68, 68, 0.1);
240
+ border-radius: 50%;
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ margin: 0 auto 1.5rem;
245
+ color: #dc2626;
246
+ }
247
+
248
+ .wp__modal-icon {
249
+ width: 2rem;
250
+ height: 2rem;
251
+ }
252
+
253
+ .wp__modal-title {
254
+ font-size: 1.35rem;
255
+ font-weight: 800;
256
+ text-align: center;
257
+ color: var(--wp-text);
258
+ margin: 0 0 1rem;
259
+ }
260
+
261
+ .wp__modal-body {
262
+ color: var(--wp-text-muted);
263
+ text-align: center;
264
+ line-height: 1.7;
265
+ margin: 0 0 2rem;
266
+ font-size: 0.9rem;
267
+ }
268
+
269
+ .wp__modal-highlight {
270
+ color: var(--wp-text);
271
+ font-weight: 700;
272
+ }
273
+
274
+ .wp__modal-btn {
275
+ width: 100%;
276
+ padding: 0.875rem 1.5rem;
277
+ background: #dc2626;
278
+ color: #fff;
279
+ font-weight: 700;
280
+ font-size: 1rem;
281
+ border: none;
282
+ border-radius: 0.75rem;
283
+ cursor: pointer;
284
+ transition: background 0.2s, transform 0.1s;
285
+ box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);
286
+ }
287
+
288
+ .wp__modal-btn:hover {
289
+ background: #b91c1c;
290
+ transform: scale(1.02);
291
+ }
292
+
293
+ .wp__modal-btn:active {
294
+ transform: scale(0.98);
295
+ }
296
+
297
+
298
+ .wp__grid {
299
+ display: grid;
300
+ gap: 2rem;
301
+ margin-bottom: 3rem;
302
+ }
303
+
304
+ @media (min-width: 768px) {
305
+ .wp__grid {
306
+ grid-template-columns: 1fr 1fr;
307
+ }
308
+ }
309
+
310
+
311
+ .wp__card {
312
+ background: var(--wp-card-bg);
313
+ border-radius: 1.25rem;
314
+ box-shadow: var(--wp-shadow);
315
+ border: 1px solid var(--wp-border);
316
+ overflow: hidden;
317
+ }
318
+
319
+ .wp__card--params {
320
+ padding: 1.5rem 1.75rem;
321
+ }
322
+
323
+ .wp__card--result {
324
+ background: var(--wp-primary);
325
+ border: none;
326
+ display: flex;
327
+ flex-direction: column;
328
+ align-items: center;
329
+ justify-content: center;
330
+ padding: 2rem;
331
+ color: #fff;
332
+ position: relative;
333
+ }
334
+
335
+
336
+ .wp__card-header {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: 0.75rem;
340
+ margin-bottom: 2rem;
341
+ }
342
+
343
+ .wp__card-icon-wrap {
344
+ width: 2.5rem;
345
+ height: 2.5rem;
346
+ background: var(--wp-primary-light);
347
+ border-radius: 0.625rem;
348
+ display: flex;
349
+ align-items: center;
350
+ justify-content: center;
351
+ color: var(--wp-primary);
352
+ flex-shrink: 0;
353
+ }
354
+
355
+ .wp__card-icon {
356
+ width: 1.25rem;
357
+ height: 1.25rem;
358
+ }
359
+
360
+ .wp__card-title {
361
+ font-size: 1.1rem;
362
+ font-weight: 700;
363
+ color: var(--wp-text);
364
+ margin: 0;
365
+ }
366
+
367
+
368
+ .wp__params {
369
+ display: flex;
370
+ flex-direction: column;
371
+ gap: 2rem;
372
+ }
373
+
374
+ .wp__field-label {
375
+ display: block;
376
+ font-size: 0.875rem;
377
+ font-weight: 600;
378
+ color: var(--wp-text);
379
+ margin-bottom: 0.75rem;
380
+ }
381
+
382
+
383
+ .wp__condition-grid {
384
+ display: grid;
385
+ grid-template-columns: 1fr 1fr;
386
+ gap: 0.75rem;
387
+ }
388
+
389
+ .wp__condition-option {
390
+ cursor: pointer;
391
+ display: block;
392
+ }
393
+
394
+ .wp__condition-radio {
395
+ position: absolute;
396
+ opacity: 0;
397
+ width: 0;
398
+ height: 0;
399
+ }
400
+
401
+ .wp__condition-card {
402
+ padding: 1rem;
403
+ border-radius: 0.75rem;
404
+ border: 2px solid var(--wp-border);
405
+ background: var(--wp-input-bg);
406
+ text-align: center;
407
+ transition: border-color 0.2s, background 0.2s;
408
+ }
409
+
410
+ .wp__condition-icon {
411
+ width: 2rem;
412
+ height: 2rem;
413
+ display: block;
414
+ margin: 0 auto 0.5rem;
415
+ color: var(--wp-text-muted);
416
+ transition: color 0.2s;
417
+ }
418
+
419
+ .wp__condition-label {
420
+ display: block;
421
+ font-size: 0.8rem;
422
+ font-weight: 500;
423
+ color: var(--wp-text-muted);
424
+ transition: color 0.2s;
425
+ }
426
+
427
+ .wp__condition-radio:checked ~ .wp__condition-card {
428
+ border-color: var(--wp-primary);
429
+ background: var(--wp-selected-bg);
430
+ }
431
+
432
+ .wp__condition-radio:checked ~ .wp__condition-card .wp__condition-icon {
433
+ color: var(--wp-primary);
434
+ }
435
+
436
+ .wp__condition-radio:checked ~ .wp__condition-card .wp__condition-label {
437
+ color: var(--wp-primary-dark);
438
+ font-weight: 600;
439
+ }
440
+
441
+
442
+ .wp__input-wrap {
443
+ position: relative;
444
+ }
445
+
446
+ .wp__input {
447
+ display: block;
448
+ width: 100%;
449
+ border-radius: 0.75rem;
450
+ border: 2px solid var(--wp-border);
451
+ background: var(--wp-input-bg);
452
+ padding: 1rem 3rem 1rem 1rem;
453
+ font-size: 1.125rem;
454
+ font-weight: 700;
455
+ color: var(--wp-text);
456
+ transition: border-color 0.2s;
457
+ box-sizing: border-box;
458
+ }
459
+
460
+ .wp__input:focus {
461
+ outline: none;
462
+ border-color: var(--wp-primary);
463
+ }
464
+
465
+ .wp__input-unit {
466
+ position: absolute;
467
+ right: 1rem;
468
+ top: 50%;
469
+ transform: translateY(-50%);
470
+ color: var(--wp-text-muted);
471
+ font-weight: 500;
472
+ pointer-events: none;
473
+ }
474
+
475
+
476
+ .wp__drop-area {
477
+ position: relative;
478
+ width: 6rem;
479
+ height: 10rem;
480
+ margin-bottom: 2rem;
481
+ }
482
+
483
+ .wp__dropper-wrap {
484
+ position: absolute;
485
+ top: 0;
486
+ left: 50%;
487
+ transform: translateX(-50%) rotate(180deg);
488
+ width: 2rem;
489
+ height: 5rem;
490
+ color: rgba(255, 255, 255, 0.9);
491
+ }
492
+
493
+ .wp__dropper-icon {
494
+ width: 100%;
495
+ height: 100%;
496
+ }
497
+
498
+ .wp__drop {
499
+ position: absolute;
500
+ top: 4rem;
501
+ left: 50%;
502
+ transform: translateX(-50%) rotate(-45deg) scale(0);
503
+ width: 1rem;
504
+ height: 1rem;
505
+ background: #fff;
506
+ border-radius: 50% 50% 50% 0;
507
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
508
+ opacity: 0;
509
+ }
510
+
511
+ .wp__drop-surface {
512
+ position: absolute;
513
+ bottom: 0;
514
+ width: 100%;
515
+ height: 0.5rem;
516
+ background: rgba(255, 255, 255, 0.2);
517
+ border-radius: 50%;
518
+ filter: blur(2px);
519
+ }
520
+
521
+ @keyframes wp-drop {
522
+ 0% {
523
+ top: 4rem;
524
+ opacity: 1;
525
+ transform: translateX(-50%) rotate(-45deg) scale(0.5);
526
+ }
527
+ 60% {
528
+ top: 8rem;
529
+ opacity: 1;
530
+ transform: translateX(-50%) rotate(-45deg) scale(1);
531
+ }
532
+ 90% {
533
+ top: 9rem;
534
+ opacity: 0;
535
+ transform: translateX(-50%) rotate(-45deg) scale(0.2);
536
+ }
537
+ 100% {
538
+ opacity: 0;
539
+ }
540
+ }
541
+
542
+ .wp__drop--animating {
543
+ animation: wp-drop 1.5s infinite ease-in;
544
+ }
545
+
546
+
547
+ .wp__result {
548
+ text-align: center;
549
+ position: relative;
550
+ z-index: 1;
551
+ }
552
+
553
+ .wp__result-label {
554
+ font-size: 0.75rem;
555
+ font-weight: 600;
556
+ text-transform: uppercase;
557
+ letter-spacing: 0.08em;
558
+ color: rgba(255, 255, 255, 0.8);
559
+ margin: 0 0 0.5rem;
560
+ }
561
+
562
+ .wp__result-amount {
563
+ display: flex;
564
+ align-items: baseline;
565
+ justify-content: center;
566
+ gap: 0.5rem;
567
+ margin-bottom: 0.25rem;
568
+ }
569
+
570
+ .wp__result-number {
571
+ font-size: 4rem;
572
+ font-weight: 900;
573
+ line-height: 1;
574
+ letter-spacing: -0.04em;
575
+ color: #fff;
576
+ }
577
+
578
+ .wp__result-unit {
579
+ font-size: 1.25rem;
580
+ font-weight: 700;
581
+ color: rgba(255, 255, 255, 0.8);
582
+ }
583
+
584
+ .wp__result-ml {
585
+ font-size: 0.85rem;
586
+ color: rgba(255, 255, 255, 0.7);
587
+ margin: 0 0 1.5rem;
588
+ }
589
+
590
+
591
+ .wp__wait-box {
592
+ background: rgba(0, 0, 0, 0.2);
593
+ border-radius: 0.625rem;
594
+ padding: 1rem 1.25rem;
595
+ font-size: 0.875rem;
596
+ line-height: 1.5;
597
+ backdrop-filter: blur(4px);
598
+ }
599
+
600
+ .wp__wait-header {
601
+ display: flex;
602
+ align-items: center;
603
+ justify-content: center;
604
+ gap: 0.5rem;
605
+ font-weight: 700;
606
+ margin-bottom: 0.25rem;
607
+ }
608
+
609
+ .wp__wait-icon {
610
+ width: 1rem;
611
+ height: 1rem;
612
+ }
613
+
614
+ .wp__wait-title {
615
+ font-size: 0.875rem;
616
+ }
617
+
618
+ .wp__wait-text {
619
+ margin: 0;
620
+ color: rgba(255, 255, 255, 0.9);
621
+ text-align: center;
622
+ }
623
+
624
+ .wp__wait-duration {
625
+ color: #fff;
626
+ font-weight: 800;
627
+ }
628
+ </style>