@jjlmoya/utils-home 1.17.0 → 1.24.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 (162) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/de.ts +10 -10
  3. package/src/category/i18n/en.ts +8 -8
  4. package/src/category/i18n/es.ts +2 -2
  5. package/src/category/i18n/fr.ts +15 -15
  6. package/src/category/i18n/id.ts +8 -8
  7. package/src/category/i18n/it.ts +7 -7
  8. package/src/category/i18n/nl.ts +8 -8
  9. package/src/category/i18n/pl.ts +10 -10
  10. package/src/category/i18n/pt.ts +8 -8
  11. package/src/category/i18n/ru.ts +10 -10
  12. package/src/category/i18n/sv.ts +8 -8
  13. package/src/category/i18n/tr.ts +4 -4
  14. package/src/category/i18n/zh.ts +8 -8
  15. package/src/entries.ts +7 -1
  16. package/src/tests/locale_completeness.test.ts +2 -2
  17. package/src/tests/no_en_dash.test.ts +70 -0
  18. package/src/tests/tool_validation.test.ts +2 -2
  19. package/src/tool/acTonnageCalculator/ac-tonnage-calculator.css +467 -0
  20. package/src/tool/acTonnageCalculator/bibliography.astro +14 -0
  21. package/src/tool/acTonnageCalculator/bibliography.ts +5 -0
  22. package/src/tool/acTonnageCalculator/client-animations.ts +80 -0
  23. package/src/tool/acTonnageCalculator/client.ts +171 -0
  24. package/src/tool/acTonnageCalculator/component.astro +186 -0
  25. package/src/tool/acTonnageCalculator/entry.ts +29 -0
  26. package/src/tool/acTonnageCalculator/i18n/de.ts +63 -0
  27. package/src/tool/acTonnageCalculator/i18n/en.ts +136 -0
  28. package/src/tool/acTonnageCalculator/i18n/es.ts +136 -0
  29. package/src/tool/acTonnageCalculator/i18n/fr.ts +61 -0
  30. package/src/tool/acTonnageCalculator/i18n/id.ts +61 -0
  31. package/src/tool/acTonnageCalculator/i18n/it.ts +61 -0
  32. package/src/tool/acTonnageCalculator/i18n/ja.ts +61 -0
  33. package/src/tool/acTonnageCalculator/i18n/ko.ts +61 -0
  34. package/src/tool/acTonnageCalculator/i18n/nl.ts +61 -0
  35. package/src/tool/acTonnageCalculator/i18n/pl.ts +61 -0
  36. package/src/tool/acTonnageCalculator/i18n/pt.ts +61 -0
  37. package/src/tool/acTonnageCalculator/i18n/ru.ts +61 -0
  38. package/src/tool/acTonnageCalculator/i18n/sv.ts +61 -0
  39. package/src/tool/acTonnageCalculator/i18n/tr.ts +61 -0
  40. package/src/tool/acTonnageCalculator/i18n/zh.ts +61 -0
  41. package/src/tool/acTonnageCalculator/index.ts +8 -0
  42. package/src/tool/acTonnageCalculator/logic.ts +56 -0
  43. package/src/tool/acTonnageCalculator/seo.astro +15 -0
  44. package/src/tool/acTonnageCalculator/ui.ts +39 -0
  45. package/src/tool/dewPointCalculator/bibliography.ts +2 -2
  46. package/src/tool/dewPointCalculator/i18n/de.ts +5 -5
  47. package/src/tool/dewPointCalculator/i18n/en.ts +6 -6
  48. package/src/tool/dewPointCalculator/i18n/es.ts +5 -5
  49. package/src/tool/dewPointCalculator/i18n/fr.ts +6 -6
  50. package/src/tool/dewPointCalculator/i18n/id.ts +5 -5
  51. package/src/tool/dewPointCalculator/i18n/it.ts +5 -5
  52. package/src/tool/dewPointCalculator/i18n/ja.ts +4 -4
  53. package/src/tool/dewPointCalculator/i18n/ko.ts +4 -4
  54. package/src/tool/dewPointCalculator/i18n/nl.ts +5 -5
  55. package/src/tool/dewPointCalculator/i18n/pl.ts +5 -5
  56. package/src/tool/dewPointCalculator/i18n/pt.ts +5 -5
  57. package/src/tool/dewPointCalculator/i18n/ru.ts +11 -11
  58. package/src/tool/dewPointCalculator/i18n/sv.ts +5 -5
  59. package/src/tool/dewPointCalculator/i18n/tr.ts +4 -4
  60. package/src/tool/dewPointCalculator/i18n/zh.ts +5 -5
  61. package/src/tool/heatingComparator/i18n/de.ts +8 -8
  62. package/src/tool/heatingComparator/i18n/en.ts +1 -1
  63. package/src/tool/heatingComparator/i18n/es.ts +1 -1
  64. package/src/tool/heatingComparator/i18n/fr.ts +7 -7
  65. package/src/tool/heatingComparator/i18n/id.ts +1 -1
  66. package/src/tool/heatingComparator/i18n/it.ts +1 -1
  67. package/src/tool/heatingComparator/i18n/nl.ts +1 -1
  68. package/src/tool/heatingComparator/i18n/pl.ts +1 -1
  69. package/src/tool/heatingComparator/i18n/pt.ts +1 -1
  70. package/src/tool/heatingComparator/i18n/ru.ts +12 -12
  71. package/src/tool/heatingComparator/i18n/sv.ts +4 -4
  72. package/src/tool/heatingComparator/i18n/zh.ts +6 -6
  73. package/src/tool/ledSavingCalculator/bibliography.ts +3 -3
  74. package/src/tool/ledSavingCalculator/i18n/de.ts +4 -4
  75. package/src/tool/ledSavingCalculator/i18n/en.ts +4 -4
  76. package/src/tool/ledSavingCalculator/i18n/es.ts +4 -4
  77. package/src/tool/ledSavingCalculator/i18n/fr.ts +8 -8
  78. package/src/tool/ledSavingCalculator/i18n/id.ts +3 -3
  79. package/src/tool/ledSavingCalculator/i18n/it.ts +4 -4
  80. package/src/tool/ledSavingCalculator/i18n/ja.ts +3 -3
  81. package/src/tool/ledSavingCalculator/i18n/ko.ts +2 -2
  82. package/src/tool/ledSavingCalculator/i18n/nl.ts +3 -3
  83. package/src/tool/ledSavingCalculator/i18n/pl.ts +3 -3
  84. package/src/tool/ledSavingCalculator/i18n/pt.ts +3 -3
  85. package/src/tool/ledSavingCalculator/i18n/ru.ts +6 -6
  86. package/src/tool/ledSavingCalculator/i18n/sv.ts +3 -3
  87. package/src/tool/ledSavingCalculator/i18n/tr.ts +3 -3
  88. package/src/tool/ledSavingCalculator/i18n/zh.ts +4 -4
  89. package/src/tool/projectorCalculator/bibliography.ts +3 -3
  90. package/src/tool/projectorCalculator/i18n/de.ts +2 -2
  91. package/src/tool/projectorCalculator/i18n/en.ts +1 -1
  92. package/src/tool/projectorCalculator/i18n/es.ts +2 -2
  93. package/src/tool/projectorCalculator/i18n/fr.ts +4 -4
  94. package/src/tool/projectorCalculator/i18n/id.ts +2 -2
  95. package/src/tool/projectorCalculator/i18n/it.ts +2 -2
  96. package/src/tool/projectorCalculator/i18n/ja.ts +2 -2
  97. package/src/tool/projectorCalculator/i18n/ko.ts +2 -2
  98. package/src/tool/projectorCalculator/i18n/nl.ts +2 -2
  99. package/src/tool/projectorCalculator/i18n/pl.ts +3 -3
  100. package/src/tool/projectorCalculator/i18n/pt.ts +2 -2
  101. package/src/tool/projectorCalculator/i18n/ru.ts +5 -5
  102. package/src/tool/projectorCalculator/i18n/sv.ts +2 -2
  103. package/src/tool/projectorCalculator/i18n/tr.ts +2 -2
  104. package/src/tool/projectorCalculator/i18n/zh.ts +4 -4
  105. package/src/tool/qrGenerator/bibliography.ts +1 -1
  106. package/src/tool/qrGenerator/i18n/en.ts +1 -1
  107. package/src/tool/qrGenerator/i18n/fr.ts +1 -1
  108. package/src/tool/solarCalculator/bibliography.ts +2 -2
  109. package/src/tool/solarCalculator/i18n/de.ts +2 -2
  110. package/src/tool/solarCalculator/i18n/en.ts +5 -5
  111. package/src/tool/solarCalculator/i18n/es.ts +3 -3
  112. package/src/tool/solarCalculator/i18n/fr.ts +6 -6
  113. package/src/tool/solarCalculator/i18n/id.ts +2 -2
  114. package/src/tool/solarCalculator/i18n/it.ts +2 -2
  115. package/src/tool/solarCalculator/i18n/ja.ts +2 -2
  116. package/src/tool/solarCalculator/i18n/ko.ts +2 -2
  117. package/src/tool/solarCalculator/i18n/nl.ts +2 -2
  118. package/src/tool/solarCalculator/i18n/pl.ts +3 -3
  119. package/src/tool/solarCalculator/i18n/pt.ts +2 -2
  120. package/src/tool/solarCalculator/i18n/ru.ts +5 -5
  121. package/src/tool/solarCalculator/i18n/sv.ts +2 -2
  122. package/src/tool/solarCalculator/i18n/tr.ts +2 -2
  123. package/src/tool/solarCalculator/i18n/zh.ts +3 -3
  124. package/src/tool/tariffComparator/bibliography.ts +1 -1
  125. package/src/tool/tariffComparator/i18n/en.ts +3 -3
  126. package/src/tool/tariffComparator/i18n/es.ts +3 -3
  127. package/src/tool/tariffComparator/i18n/fr.ts +6 -6
  128. package/src/tool/tariffComparator/i18n/pl.ts +1 -1
  129. package/src/tool/tariffComparator/i18n/zh.ts +1 -1
  130. package/src/tool/wifiRangeSimulator/bibliography.astro +14 -0
  131. package/src/tool/wifiRangeSimulator/bibliography.ts +14 -0
  132. package/src/tool/wifiRangeSimulator/component.astro +170 -0
  133. package/src/tool/wifiRangeSimulator/entry.ts +29 -0
  134. package/src/tool/wifiRangeSimulator/i18n/de.ts +477 -0
  135. package/src/tool/wifiRangeSimulator/i18n/en.ts +477 -0
  136. package/src/tool/wifiRangeSimulator/i18n/es.ts +477 -0
  137. package/src/tool/wifiRangeSimulator/i18n/fr.ts +477 -0
  138. package/src/tool/wifiRangeSimulator/i18n/id.ts +477 -0
  139. package/src/tool/wifiRangeSimulator/i18n/it.ts +477 -0
  140. package/src/tool/wifiRangeSimulator/i18n/ja.ts +477 -0
  141. package/src/tool/wifiRangeSimulator/i18n/ko.ts +477 -0
  142. package/src/tool/wifiRangeSimulator/i18n/nl.ts +477 -0
  143. package/src/tool/wifiRangeSimulator/i18n/pl.ts +477 -0
  144. package/src/tool/wifiRangeSimulator/i18n/pt.ts +477 -0
  145. package/src/tool/wifiRangeSimulator/i18n/ru.ts +477 -0
  146. package/src/tool/wifiRangeSimulator/i18n/sv.ts +477 -0
  147. package/src/tool/wifiRangeSimulator/i18n/tr.ts +477 -0
  148. package/src/tool/wifiRangeSimulator/i18n/zh.ts +477 -0
  149. package/src/tool/wifiRangeSimulator/i18n-utils.ts +14 -0
  150. package/src/tool/wifiRangeSimulator/index.ts +8 -0
  151. package/src/tool/wifiRangeSimulator/logic.ts +220 -0
  152. package/src/tool/wifiRangeSimulator/seo.astro +15 -0
  153. package/src/tool/wifiRangeSimulator/sketch-actions.ts +168 -0
  154. package/src/tool/wifiRangeSimulator/sketch-events.ts +138 -0
  155. package/src/tool/wifiRangeSimulator/sketch-render-dash.ts +170 -0
  156. package/src/tool/wifiRangeSimulator/sketch-render-device.ts +42 -0
  157. package/src/tool/wifiRangeSimulator/sketch-render.ts +155 -0
  158. package/src/tool/wifiRangeSimulator/sketch-state.ts +186 -0
  159. package/src/tool/wifiRangeSimulator/sketch.ts +100 -0
  160. package/src/tool/wifiRangeSimulator/ui.ts +69 -0
  161. package/src/tool/wifiRangeSimulator/wifi-range-simulator.css +583 -0
  162. package/src/tools.ts +4 -0
@@ -0,0 +1,467 @@
1
+ .ac-wrapper {
2
+ --ac-accent: #0ea5e9;
3
+ --ac-accent-dim: rgba(14,165,233,0.12);
4
+ --ac-surface: var(--bg-surface);
5
+ --ac-border: var(--border-color);
6
+ --ac-text: var(--text-main);
7
+ --ac-muted: var(--text-muted);
8
+
9
+ width: 100%;
10
+ padding: 1rem 0;
11
+ }
12
+
13
+ .ac-card {
14
+ background: var(--ac-surface);
15
+ width: calc(100% - 24px);
16
+ max-width: 900px;
17
+ margin: 0 auto;
18
+ border-radius: 24px;
19
+ overflow: hidden;
20
+ border: 1px solid var(--ac-border);
21
+ color: var(--ac-text);
22
+ display: flex;
23
+ flex-direction: column;
24
+ }
25
+
26
+ @media (min-width: 1024px) {
27
+ .ac-card {
28
+ flex-direction: row;
29
+ min-height: 600px;
30
+ }
31
+ }
32
+
33
+ .ac-left {
34
+ flex: 0 0 auto;
35
+ padding: 24px;
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 18px;
39
+ }
40
+
41
+ @media (min-width: 1024px) {
42
+ .ac-left {
43
+ flex: 1 1 auto;
44
+ width: auto;
45
+ min-width: 280px;
46
+ }
47
+ }
48
+
49
+ .ac-unit-toggle {
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 8px;
53
+ padding: 6px 10px;
54
+ border-radius: 10px;
55
+ background: var(--bg-page);
56
+ border: 1px solid var(--ac-border);
57
+ align-self: flex-start;
58
+ }
59
+
60
+ .ac-unit-toggle button {
61
+ padding: 5px 10px;
62
+ border-radius: 6px;
63
+ border: none;
64
+ background: transparent;
65
+ color: var(--ac-muted);
66
+ font-size: 0.6875rem;
67
+ font-weight: 800;
68
+ cursor: pointer;
69
+ transition: all 0.15s;
70
+ }
71
+
72
+ .ac-power-toggle {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 4px;
76
+ padding: 4px 6px;
77
+ border-radius: 8px;
78
+ background: var(--bg-page);
79
+ border: 1px solid var(--ac-border);
80
+ }
81
+
82
+ .ac-power-toggle button {
83
+ padding: 4px 8px;
84
+ border-radius: 5px;
85
+ border: none;
86
+ background: transparent;
87
+ color: var(--ac-muted);
88
+ font-size: 0.625rem;
89
+ font-weight: 800;
90
+ cursor: pointer;
91
+ transition: all 0.15s;
92
+ white-space: nowrap;
93
+ }
94
+
95
+ .ac-power-toggle button.active {
96
+ background: var(--ac-accent);
97
+ color: #fff;
98
+ }
99
+
100
+ .ac-unit-toggle button.active {
101
+ background: var(--ac-accent);
102
+ color: #fff;
103
+ }
104
+
105
+ .ac-field {
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: 8px;
109
+ }
110
+
111
+ .ac-field-top {
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: space-between;
115
+ gap: 8px;
116
+ }
117
+
118
+ .ac-label {
119
+ font-size: 0.6875rem;
120
+ font-weight: 800;
121
+ text-transform: uppercase;
122
+ letter-spacing: 0.06em;
123
+ color: var(--ac-muted);
124
+ flex-shrink: 0;
125
+ }
126
+
127
+ .ac-val {
128
+ font-size: 0.875rem;
129
+ font-weight: 900;
130
+ color: var(--ac-accent);
131
+ white-space: nowrap;
132
+ }
133
+
134
+ .ac-slider {
135
+ width: 100%;
136
+ height: 6px;
137
+ border-radius: 3px;
138
+ background: var(--bg-muted);
139
+ outline: none;
140
+ -webkit-appearance: none;
141
+ appearance: none;
142
+ }
143
+
144
+ .ac-slider::-webkit-slider-thumb {
145
+ -webkit-appearance: none;
146
+ width: 20px;
147
+ height: 20px;
148
+ border-radius: 50%;
149
+ background: var(--ac-accent);
150
+ cursor: pointer;
151
+ box-shadow: 0 2px 8px rgba(14,165,233,0.3);
152
+ transition: transform 0.15s;
153
+ }
154
+
155
+ .ac-slider::-webkit-slider-thumb:hover {
156
+ transform: scale(1.15);
157
+ }
158
+
159
+ .ac-slider::-moz-range-thumb {
160
+ width: 20px;
161
+ height: 20px;
162
+ border-radius: 50%;
163
+ background: var(--ac-accent);
164
+ cursor: pointer;
165
+ border: none;
166
+ box-shadow: 0 2px 8px rgba(14,165,233,0.3);
167
+ }
168
+
169
+ .ac-steps {
170
+ display: flex;
171
+ gap: 6px;
172
+ }
173
+
174
+ .ac-step {
175
+ width: 28px;
176
+ height: 28px;
177
+ border-radius: 6px;
178
+ border: 1px solid var(--ac-border);
179
+ background: var(--bg-page);
180
+ color: var(--ac-text);
181
+ font-size: 1rem;
182
+ font-weight: 700;
183
+ cursor: pointer;
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ transition: all 0.15s;
188
+ }
189
+
190
+ .ac-step:hover {
191
+ border-color: var(--ac-accent);
192
+ color: var(--ac-accent);
193
+ }
194
+
195
+ .ac-row {
196
+ display: grid;
197
+ grid-template-columns: 1fr 1fr;
198
+ gap: 12px;
199
+ }
200
+
201
+ .ac-select-wrap {
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 6px;
205
+ }
206
+
207
+ .ac-select {
208
+ padding: 10px 36px 10px 12px;
209
+ border-radius: 10px;
210
+ border: 1px solid var(--ac-border);
211
+ background: var(--bg-page);
212
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
213
+ background-repeat: no-repeat;
214
+ background-position: right 12px center;
215
+ color: var(--ac-text);
216
+ font-size: 0.875rem;
217
+ font-weight: 700;
218
+ outline: none;
219
+ cursor: pointer;
220
+ -webkit-appearance: none;
221
+ appearance: none;
222
+ }
223
+
224
+ .ac-select:focus {
225
+ border-color: var(--ac-accent);
226
+ box-shadow: 0 0 0 3px var(--ac-accent-dim);
227
+ }
228
+
229
+ .ac-right {
230
+ flex: 0 0 auto;
231
+ width: 100%;
232
+ padding: 24px;
233
+ border-top: 1px solid var(--ac-border);
234
+ background: var(--bg-page);
235
+ display: flex;
236
+ flex-direction: column;
237
+ gap: 20px;
238
+ align-items: center;
239
+ position: relative;
240
+ overflow: hidden;
241
+ }
242
+
243
+ @media (min-width: 1024px) {
244
+ .ac-right {
245
+ width: 300px;
246
+ border-top: none;
247
+ border-left: 1px solid var(--ac-border);
248
+ }
249
+ }
250
+
251
+ .ac-ring-wrap {
252
+ position: relative;
253
+ width: 160px;
254
+ height: 160px;
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ }
259
+
260
+ .ac-ring-wrap svg {
261
+ position: absolute;
262
+ inset: 0;
263
+ transform: rotate(-90deg);
264
+ }
265
+
266
+ .ac-ring-glow {
267
+ position: absolute;
268
+ width: 160px;
269
+ height: 160px;
270
+ border-radius: 50%;
271
+ pointer-events: none;
272
+ opacity: 0;
273
+ transition: opacity 0.5s;
274
+ }
275
+
276
+ .ac-ring-glow.active {
277
+ opacity: 1;
278
+ animation: ac-glow-pulse 2s ease-in-out infinite;
279
+ }
280
+
281
+ @keyframes ac-glow-pulse {
282
+ 0%, 100% {
283
+ box-shadow: 0 0 20px rgba(14,165,233,0.2);
284
+ transform: scale(1);
285
+ }
286
+ 50% {
287
+ box-shadow: 0 0 40px rgba(14,165,233,0.4);
288
+ transform: scale(1.05);
289
+ }
290
+ }
291
+
292
+ .ac-ring-bg {
293
+ fill: none;
294
+ stroke: var(--bg-muted);
295
+ stroke-width: 10;
296
+ }
297
+
298
+ .ac-ring-fill {
299
+ fill: none;
300
+ stroke-width: 10;
301
+ stroke-linecap: round;
302
+ transition: stroke-dashoffset 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), stroke 0.5s ease;
303
+ }
304
+
305
+ .ac-ring-inner {
306
+ position: relative;
307
+ z-index: 2;
308
+ text-align: center;
309
+ }
310
+
311
+ .ac-ring-label {
312
+ font-size: 0.625rem;
313
+ font-weight: 800;
314
+ text-transform: uppercase;
315
+ letter-spacing: 0.06em;
316
+ color: var(--ac-muted);
317
+ }
318
+
319
+ .ac-ring-btu {
320
+ font-size: 1.75rem;
321
+ font-weight: 900;
322
+ line-height: 1;
323
+ color: var(--ac-text);
324
+ }
325
+
326
+ .ac-ring-unit {
327
+ font-size: 0.625rem;
328
+ font-weight: 700;
329
+ color: var(--ac-muted);
330
+ }
331
+
332
+ .ac-mini-room {
333
+ padding: 10px;
334
+ border-radius: 14px;
335
+ background: var(--ac-surface);
336
+ border: 1px solid var(--ac-border);
337
+ transition: all 0.5s;
338
+ }
339
+
340
+ .ac-mini-room svg {
341
+ display: block;
342
+ }
343
+
344
+ .ac-room-wall {
345
+ fill: var(--bg-page);
346
+ stroke: var(--ac-border);
347
+ stroke-width: 1.5;
348
+ transition: fill 0.5s;
349
+ }
350
+
351
+ .ac-room-window {
352
+ fill: #bfdbfe;
353
+ transition: fill 0.5s;
354
+ }
355
+
356
+ .ac-room-person {
357
+ fill: #94a3b8;
358
+ transition: fill 0.5s;
359
+ }
360
+
361
+ .ac-room-pc {
362
+ fill: #475569;
363
+ transition: fill 0.5s;
364
+ }
365
+
366
+ .ac-room-sun {
367
+ fill: #fbbf24;
368
+ transition: fill 0.5s;
369
+ }
370
+
371
+ .ac-theme-low .ac-room-wall { fill: #e0f2fe; }
372
+ .ac-theme-low .ac-card { border-color: #7dd3fc; }
373
+
374
+ .ac-theme-med .ac-room-wall { fill: #fef3c7; }
375
+ .ac-theme-med .ac-card { border-color: #fcd34d; }
376
+
377
+ .ac-theme-high .ac-room-wall { fill: #fee2e2; }
378
+ .ac-theme-high .ac-card { border-color: #fca5a5; }
379
+
380
+ .ac-stats {
381
+ width: 100%;
382
+ display: grid;
383
+ grid-template-columns: 1fr 1fr;
384
+ gap: 10px;
385
+ }
386
+
387
+ .ac-stat {
388
+ padding: 12px;
389
+ border-radius: 12px;
390
+ background: var(--ac-surface);
391
+ border: 1px solid var(--ac-border);
392
+ display: flex;
393
+ flex-direction: column;
394
+ gap: 4px;
395
+ text-align: center;
396
+ }
397
+
398
+ .ac-stat-label {
399
+ font-size: 0.625rem;
400
+ font-weight: 800;
401
+ text-transform: uppercase;
402
+ letter-spacing: 0.06em;
403
+ color: var(--ac-muted);
404
+ }
405
+
406
+ .ac-stat-val {
407
+ font-size: 1.25rem;
408
+ font-weight: 900;
409
+ color: var(--ac-accent);
410
+ }
411
+
412
+ .ac-breakdown {
413
+ width: 100%;
414
+ display: flex;
415
+ flex-direction: column;
416
+ gap: 8px;
417
+ }
418
+
419
+ .ac-bd-row {
420
+ display: flex;
421
+ align-items: center;
422
+ gap: 10px;
423
+ padding: 7px 9px;
424
+ border-radius: 8px;
425
+ background: var(--ac-surface);
426
+ border: 1px solid var(--ac-border);
427
+ }
428
+
429
+ .ac-bd-label {
430
+ font-size: 0.6875rem;
431
+ font-weight: 700;
432
+ color: var(--ac-text);
433
+ min-width: 100px;
434
+ }
435
+
436
+ .ac-bd-bar-wrap {
437
+ flex: 1;
438
+ height: 6px;
439
+ border-radius: 3px;
440
+ background: var(--bg-muted);
441
+ overflow: hidden;
442
+ }
443
+
444
+ .ac-bd-bar {
445
+ height: 100%;
446
+ border-radius: 3px;
447
+ background: linear-gradient(90deg, #0ea5e9, #22c55e);
448
+ transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
449
+ }
450
+
451
+ .ac-bd-val {
452
+ font-size: 0.6875rem;
453
+ font-weight: 800;
454
+ color: var(--ac-muted);
455
+ min-width: 48px;
456
+ text-align: right;
457
+ }
458
+
459
+ .ac-peak {
460
+ animation: ac-peak-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
461
+ }
462
+
463
+ @keyframes ac-peak-in {
464
+ 0% { transform: scale(1); }
465
+ 50% { transform: scale(1.02); }
466
+ 100% { transform: scale(1); }
467
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { acTonnageCalculator } 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 acTonnageCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,5 @@
1
+ export const bibliography = [
2
+ { name: 'ASHRAE Fundamentals Handbook 2021', url: 'https://www.ashrae.org/technical-resources/ashrae-handbook' },
3
+ { name: 'Energy Star Room Air Conditioners', url: 'https://www.energystar.gov/products/room_air_conditioners' },
4
+ { name: 'Carrier BTU Sizing Guide', url: 'https://www.carrier.com/residential/en/us/products/air-conditioners/' },
5
+ ];
@@ -0,0 +1,80 @@
1
+ function getLabels(): Record<string, string> {
2
+ const el = document.getElementById('ac-labels');
3
+ if (!el) return {};
4
+ try {
5
+ return JSON.parse(el.dataset.labels ?? '{}');
6
+ } catch {
7
+ return {};
8
+ }
9
+ }
10
+
11
+ export function updateTheme(btu: number) {
12
+ const card = document.getElementById('ac-card');
13
+ if (!card) return;
14
+ card.classList.remove('ac-theme-low', 'ac-theme-med', 'ac-theme-high');
15
+ if (btu < 18000) card.classList.add('ac-theme-low');
16
+ else if (btu < 36000) card.classList.add('ac-theme-med');
17
+ else card.classList.add('ac-theme-high');
18
+ }
19
+
20
+ function roomLabel(btu: number): string {
21
+ const labels = getLabels();
22
+ if (btu < 18000) return labels.labelRoomCozy ?? '';
23
+ if (btu < 36000) return labels.labelRoomWarm ?? '';
24
+ return labels.labelRoomHot ?? '';
25
+ }
26
+
27
+ export function updateMiniRoom(btu: number) {
28
+ const svg = document.querySelector('.ac-mini-room svg');
29
+ if (!svg) return;
30
+ const txt = svg.querySelector('text');
31
+ if (txt) txt.textContent = roomLabel(btu);
32
+ }
33
+
34
+ export function spawnRipple() {
35
+ const wrap = document.querySelector('.ac-ring-wrap');
36
+ if (!wrap) return;
37
+ const el = document.getElementById('ac-ring-fill');
38
+ const color = el ? getComputedStyle(el).stroke : '#0ea5e9';
39
+ const ripple = document.createElement('div');
40
+ ripple.className = 'ac-ripple';
41
+ ripple.style.left = '50%';
42
+ ripple.style.top = '50%';
43
+ ripple.style.transform = 'translate(-50%, -50%)';
44
+ ripple.style.border = `2px solid ${color}`;
45
+ wrap.appendChild(ripple);
46
+ setTimeout(() => ripple.remove(), 800);
47
+ }
48
+
49
+ export function pulseGlow() {
50
+ const glow = document.getElementById('ac-ring-glow');
51
+ if (!glow) return;
52
+ glow.classList.add('active');
53
+ setTimeout(() => glow.classList.remove('active'), 2000);
54
+ }
55
+
56
+ function makeParticle(): HTMLElement {
57
+ const p = document.createElement('div');
58
+ p.className = 'ac-particle';
59
+ p.style.left = `${Math.random() * 100}%`;
60
+ p.style.animationDuration = `${2 + Math.random() * 3}s`;
61
+ p.style.animationDelay = `${Math.random() * 2}s`;
62
+ return p;
63
+ }
64
+
65
+ export function spawnParticles() {
66
+ const container = document.getElementById('ac-particles');
67
+ if (!container) return;
68
+ container.innerHTML = '';
69
+ for (let i = 0; i < 12; i++) {
70
+ container.appendChild(makeParticle());
71
+ }
72
+ }
73
+
74
+ export function buildBdRow(key: string, val: number, total: number): string {
75
+ const labels = getLabels();
76
+ const labelKey = `bd${key.charAt(0).toUpperCase()}${key.slice(1)}`;
77
+ const label = labels[labelKey] ?? key;
78
+ const pct = total > 0 ? Math.min(100, (val / total) * 100) : 0;
79
+ return `<div class="ac-bd-row"><span class="ac-bd-label">${label}</span><div class="ac-bd-bar-wrap"><div class="ac-bd-bar" style="width:${pct}%"></div></div><span class="ac-bd-val">${val.toLocaleString()}</span></div>`;
80
+ }