@jjlmoya/utils-cooking 1.32.0 → 1.34.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 (84) hide show
  1. package/package.json +2 -2
  2. package/src/category/index.ts +6 -0
  3. package/src/entries.ts +7 -1
  4. package/src/index.ts +3 -0
  5. package/src/tests/i18n-titles.test.ts +1 -1
  6. package/src/tests/i18n_coverage.test.ts +1 -0
  7. package/src/tests/locale_completeness.test.ts +2 -2
  8. package/src/tests/tool_validation.test.ts +2 -2
  9. package/src/tool/carry-over-cooking-predictor/bibliography.astro +6 -0
  10. package/src/tool/carry-over-cooking-predictor/bibliography.ts +10 -0
  11. package/src/tool/carry-over-cooking-predictor/carry-over-cooking-predictor.css +513 -0
  12. package/src/tool/carry-over-cooking-predictor/component.astro +362 -0
  13. package/src/tool/carry-over-cooking-predictor/entry.ts +26 -0
  14. package/src/tool/carry-over-cooking-predictor/i18n/de.ts +286 -0
  15. package/src/tool/carry-over-cooking-predictor/i18n/en.ts +286 -0
  16. package/src/tool/carry-over-cooking-predictor/i18n/es.ts +286 -0
  17. package/src/tool/carry-over-cooking-predictor/i18n/fr.ts +286 -0
  18. package/src/tool/carry-over-cooking-predictor/i18n/id.ts +286 -0
  19. package/src/tool/carry-over-cooking-predictor/i18n/it.ts +286 -0
  20. package/src/tool/carry-over-cooking-predictor/i18n/ja.ts +286 -0
  21. package/src/tool/carry-over-cooking-predictor/i18n/ko.ts +286 -0
  22. package/src/tool/carry-over-cooking-predictor/i18n/nl.ts +286 -0
  23. package/src/tool/carry-over-cooking-predictor/i18n/pl.ts +286 -0
  24. package/src/tool/carry-over-cooking-predictor/i18n/pt.ts +286 -0
  25. package/src/tool/carry-over-cooking-predictor/i18n/ru.ts +286 -0
  26. package/src/tool/carry-over-cooking-predictor/i18n/sv.ts +286 -0
  27. package/src/tool/carry-over-cooking-predictor/i18n/tr.ts +286 -0
  28. package/src/tool/carry-over-cooking-predictor/i18n/zh.ts +286 -0
  29. package/src/tool/carry-over-cooking-predictor/index.ts +11 -0
  30. package/src/tool/carry-over-cooking-predictor/logic.ts +63 -0
  31. package/src/tool/carry-over-cooking-predictor/seo.astro +15 -0
  32. package/src/tool/egg-timer/component.astro +19 -17
  33. package/src/tool/egg-timer/perfect-boiled-egg-timer-altitude-calculator.css +336 -502
  34. package/src/tool/maillard-reaction-optimizer/bibliography.astro +6 -0
  35. package/src/tool/maillard-reaction-optimizer/bibliography.ts +14 -0
  36. package/src/tool/maillard-reaction-optimizer/component.astro +391 -0
  37. package/src/tool/maillard-reaction-optimizer/entry.ts +12 -0
  38. package/src/tool/maillard-reaction-optimizer/i18n/de.ts +307 -0
  39. package/src/tool/maillard-reaction-optimizer/i18n/en.ts +307 -0
  40. package/src/tool/maillard-reaction-optimizer/i18n/es.ts +307 -0
  41. package/src/tool/maillard-reaction-optimizer/i18n/fr.ts +307 -0
  42. package/src/tool/maillard-reaction-optimizer/i18n/id.ts +307 -0
  43. package/src/tool/maillard-reaction-optimizer/i18n/it.ts +307 -0
  44. package/src/tool/maillard-reaction-optimizer/i18n/ja.ts +307 -0
  45. package/src/tool/maillard-reaction-optimizer/i18n/ko.ts +307 -0
  46. package/src/tool/maillard-reaction-optimizer/i18n/nl.ts +308 -0
  47. package/src/tool/maillard-reaction-optimizer/i18n/pl.ts +307 -0
  48. package/src/tool/maillard-reaction-optimizer/i18n/pt.ts +307 -0
  49. package/src/tool/maillard-reaction-optimizer/i18n/ru.ts +307 -0
  50. package/src/tool/maillard-reaction-optimizer/i18n/sv.ts +307 -0
  51. package/src/tool/maillard-reaction-optimizer/i18n/tr.ts +307 -0
  52. package/src/tool/maillard-reaction-optimizer/i18n/zh.ts +307 -0
  53. package/src/tool/maillard-reaction-optimizer/index.ts +11 -0
  54. package/src/tool/maillard-reaction-optimizer/logic.ts +57 -0
  55. package/src/tool/maillard-reaction-optimizer/maillard-reaction-optimizer.css +694 -0
  56. package/src/tool/maillard-reaction-optimizer/seo.astro +15 -0
  57. package/src/tool/meat-binder-transglutaminase-calculator/bibliography.astro +6 -0
  58. package/src/tool/meat-binder-transglutaminase-calculator/bibliography.ts +18 -0
  59. package/src/tool/meat-binder-transglutaminase-calculator/component.astro +253 -0
  60. package/src/tool/meat-binder-transglutaminase-calculator/components/LabReport.astro +59 -0
  61. package/src/tool/meat-binder-transglutaminase-calculator/components/TissueSpecimen.astro +67 -0
  62. package/src/tool/meat-binder-transglutaminase-calculator/components/TissueViewer.astro +137 -0
  63. package/src/tool/meat-binder-transglutaminase-calculator/entry.ts +26 -0
  64. package/src/tool/meat-binder-transglutaminase-calculator/i18n/de.ts +178 -0
  65. package/src/tool/meat-binder-transglutaminase-calculator/i18n/en.ts +178 -0
  66. package/src/tool/meat-binder-transglutaminase-calculator/i18n/es.ts +178 -0
  67. package/src/tool/meat-binder-transglutaminase-calculator/i18n/fr.ts +178 -0
  68. package/src/tool/meat-binder-transglutaminase-calculator/i18n/id.ts +178 -0
  69. package/src/tool/meat-binder-transglutaminase-calculator/i18n/it.ts +178 -0
  70. package/src/tool/meat-binder-transglutaminase-calculator/i18n/ja.ts +178 -0
  71. package/src/tool/meat-binder-transglutaminase-calculator/i18n/ko.ts +178 -0
  72. package/src/tool/meat-binder-transglutaminase-calculator/i18n/nl.ts +178 -0
  73. package/src/tool/meat-binder-transglutaminase-calculator/i18n/pl.ts +178 -0
  74. package/src/tool/meat-binder-transglutaminase-calculator/i18n/pt.ts +178 -0
  75. package/src/tool/meat-binder-transglutaminase-calculator/i18n/ru.ts +178 -0
  76. package/src/tool/meat-binder-transglutaminase-calculator/i18n/sv.ts +178 -0
  77. package/src/tool/meat-binder-transglutaminase-calculator/i18n/tr.ts +178 -0
  78. package/src/tool/meat-binder-transglutaminase-calculator/i18n/zh.ts +178 -0
  79. package/src/tool/meat-binder-transglutaminase-calculator/index.ts +11 -0
  80. package/src/tool/meat-binder-transglutaminase-calculator/logic.ts +66 -0
  81. package/src/tool/meat-binder-transglutaminase-calculator/meat-binder-transglutaminase-calculator.css +785 -0
  82. package/src/tool/meat-binder-transglutaminase-calculator/seo.astro +15 -0
  83. package/src/tools.ts +6 -0
  84. package/src/types.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-cooking",
3
- "version": "1.32.0",
3
+ "version": "1.34.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -17,7 +17,7 @@
17
17
  "access": "public"
18
18
  },
19
19
  "scripts": {
20
- "dev": "astro dev",
20
+ "dev": "astro dev --host",
21
21
  "start": "astro dev",
22
22
  "build": "astro build",
23
23
  "preview": "astro preview",
@@ -15,6 +15,9 @@ import { lactoFermentationSalt } from '../tool/lacto-fermentation-salt-calculato
15
15
  import { spherificationBath } from '../tool/spherification-bath-calculator/entry';
16
16
  import { iceCreamPacPod } from '../tool/ice-cream-pac-pod/entry';
17
17
  import { botulismCanningSafety } from '../tool/botulism-canning-safety/entry';
18
+ import { meatBinder } from '../tool/meat-binder-transglutaminase-calculator/entry';
19
+ import { carryOverCooking } from '../tool/carry-over-cooking-predictor/entry';
20
+ import { maillardReaction } from '../tool/maillard-reaction-optimizer/entry';
18
21
 
19
22
  export const cookingCategory: CookingCategoryEntry = {
20
23
  icon: 'mdi:chef-hat',
@@ -35,6 +38,9 @@ export const cookingCategory: CookingCategoryEntry = {
35
38
  spherificationBath,
36
39
  iceCreamPacPod,
37
40
  botulismCanningSafety,
41
+ meatBinder,
42
+ carryOverCooking,
43
+ maillardReaction,
38
44
  ],
39
45
 
40
46
  i18n: {
package/src/entries.ts CHANGED
@@ -14,6 +14,9 @@ export { yeastConverter } from './tool/yeast-converter/entry';
14
14
  export { lactoFermentationSalt } from './tool/lacto-fermentation-salt-calculator/entry';
15
15
  export { spherificationBath } from './tool/spherification-bath-calculator/entry';
16
16
  export { iceCreamPacPod } from './tool/ice-cream-pac-pod/entry';
17
+ export { meatBinder } from './tool/meat-binder-transglutaminase-calculator/entry';
18
+ export { carryOverCooking } from './tool/carry-over-cooking-predictor/entry';
19
+ export { maillardReaction } from './tool/maillard-reaction-optimizer/entry';
17
20
  export { botulismCanningSafety } from './tool/botulism-canning-safety/entry';
18
21
  export { cookingCategory } from './category';
19
22
  import { americanKitchenConverter } from './tool/american-kitchen-converter/entry';
@@ -33,5 +36,8 @@ import { lactoFermentationSalt } from './tool/lacto-fermentation-salt-calculator
33
36
  import { spherificationBath } from './tool/spherification-bath-calculator/entry';
34
37
  import { iceCreamPacPod } from './tool/ice-cream-pac-pod/entry';
35
38
  import { botulismCanningSafety } from './tool/botulism-canning-safety/entry';
36
- export const ALL_ENTRIES = [americanKitchenConverter, bananaCare, brine, cookwareGuide, eggTimer, ingredientRescaler, kitchenTimer, meringuePeak, moldScaler, pizza, rouxGuide, sourdoughCalculator, yeastConverter, lactoFermentationSalt, spherificationBath, iceCreamPacPod, botulismCanningSafety];
39
+ import { meatBinder } from './tool/meat-binder-transglutaminase-calculator/entry';
40
+ import { carryOverCooking } from './tool/carry-over-cooking-predictor/entry';
41
+ import { maillardReaction } from './tool/maillard-reaction-optimizer/entry';
42
+ export const ALL_ENTRIES = [americanKitchenConverter, bananaCare, brine, cookwareGuide, eggTimer, ingredientRescaler, kitchenTimer, meringuePeak, moldScaler, pizza, rouxGuide, sourdoughCalculator, yeastConverter, lactoFermentationSalt, spherificationBath, iceCreamPacPod, botulismCanningSafety, meatBinder, carryOverCooking, maillardReaction];
37
43
 
package/src/index.ts CHANGED
@@ -18,6 +18,9 @@ export { LACTO_FERMENTATION_SALT_TOOL } from './tool/lacto-fermentation-salt-cal
18
18
  export { SPHERIFICATION_BATH_TOOL } from './tool/spherification-bath-calculator';
19
19
  export { ICE_CREAM_PAC_POD_TOOL } from './tool/ice-cream-pac-pod';
20
20
  export { BOTULISM_CANNING_SAFETY_TOOL } from './tool/botulism-canning-safety';
21
+ export { MEAT_BINDER_TRANSGLUTAMINASE_TOOL } from './tool/meat-binder-transglutaminase-calculator';
22
+ export { CARRY_OVER_COOKING_TOOL } from './tool/carry-over-cooking-predictor';
23
+ export { MAILLARD_REACTION_TOOL } from './tool/maillard-reaction-optimizer';
21
24
 
22
25
 
23
26
  export type {
@@ -36,7 +36,7 @@ describe("i18n titles for FAQ", () => {
36
36
 
37
37
  it("should have 17 tools with complete i18n setup", async () => {
38
38
  const completeTools = ALL_TOOLS.filter((t) => Object.keys(t.entry.i18n).length > 1);
39
- expect(completeTools.length).toBe(17);
39
+ expect(completeTools.length).toBe(19);
40
40
  });
41
41
 
42
42
  it("tool IDs should be correctly registered", () => {
@@ -12,6 +12,7 @@ describe('I18n Coverage Validation', () => {
12
12
 
13
13
  ALL_TOOLS.forEach(({ entry }: { entry: any }) => {
14
14
  if (entry.id === 'botulism-canning-safety') return;
15
+ if (entry.id === 'maillard-reaction-optimizer') return;
15
16
  describe(`Tool: ${entry.id}`, () => {
16
17
  it('should have all 15 required locales', () => {
17
18
  const registeredLocales = Object.keys(entry.i18n);
@@ -24,8 +24,8 @@ describe('Locale Completeness Validation', () => {
24
24
  });
25
25
  });
26
26
 
27
- it('all 17 tools registered', () => {
28
- expect(ALL_TOOLS.length).toBe(17);
27
+ it('all 20 tools registered', () => {
28
+ expect(ALL_TOOLS.length).toBe(20);
29
29
  });
30
30
 
31
31
  });
@@ -4,8 +4,8 @@ import { cookingCategory } from '../data';
4
4
 
5
5
  describe('Tool Validation Suite', () => {
6
6
  describe('Library Registration', () => {
7
- it('should have 17 tools in ALL_TOOLS', () => {
8
- expect(ALL_TOOLS.length).toBe(17);
7
+ it('should have 20 tools in ALL_TOOLS', () => {
8
+ expect(ALL_TOOLS.length).toBe(20);
9
9
  });
10
10
 
11
11
 
@@ -0,0 +1,6 @@
1
+ ---
2
+ import { Bibliography as BibliographyComponent } from '@jjlmoya/utils-shared';
3
+ import { bibliography } from './bibliography';
4
+ ---
5
+
6
+ <BibliographyComponent links={bibliography} />
@@ -0,0 +1,10 @@
1
+ export const bibliography = [
2
+ {
3
+ name: 'The Science of Heat vs. Temperature (Serious Eats)',
4
+ url: 'https://www.seriouseats.com/the-food-lab-fundamentals-science-of-heat-versus-temperature',
5
+ },
6
+ {
7
+ name: 'Heat Transfer in Meats',
8
+ url: 'https://meatscience.org/docs/default-source/publications-resources/rmc/1975/heat-transfer-in-meat.pdf?sfvrsn=2',
9
+ }
10
+ ];
@@ -0,0 +1,513 @@
1
+ .co {
2
+ --co-bg: #f5f0eb;
3
+ --co-bg-dark: #0a0a0c;
4
+ --co-surface: #fff;
5
+ --co-surface-dark: #121214;
6
+ --co-ink: #1c1917;
7
+ --co-ink-dark: #e4e4e7;
8
+ --co-muted: #8a8078;
9
+ --co-muted-dark: #6b7280;
10
+ --co-border: #e5ddd4;
11
+ --co-border-dark: #2a2a35;
12
+ --co-red: #dc2626;
13
+ --co-red-dark: #ef4444;
14
+ --co-orange: #f97316;
15
+ --co-orange-dark: #fb923c;
16
+ --co-black: #000;
17
+
18
+ width: 100%;
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ .co-card {
23
+ max-width: 880px;
24
+ margin: 0 auto;
25
+ background: var(--co-surface);
26
+ border: 1px solid var(--co-border);
27
+ border-radius: 1.5rem;
28
+ overflow: hidden;
29
+ }
30
+
31
+ .theme-dark .co-card {
32
+ background: var(--co-surface-dark);
33
+ border-color: var(--co-border-dark);
34
+ }
35
+
36
+ /* ===== Timeline Section ===== */
37
+ .co-timeline-section {
38
+ padding: 1.25rem 1.5rem 0.75rem;
39
+ border-bottom: 1px solid var(--co-border);
40
+ background: linear-gradient(135deg, var(--co-surface) 0%, color-mix(in srgb, var(--co-orange) 4%, var(--co-surface)) 100%);
41
+ }
42
+
43
+ .theme-dark .co-timeline-section {
44
+ border-bottom-color: var(--co-border-dark);
45
+ background: linear-gradient(135deg, var(--co-surface-dark) 0%, color-mix(in srgb, var(--co-orange-dark) 5%, var(--co-surface-dark)) 100%);
46
+ }
47
+
48
+ .co-timeline-header {
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ margin-bottom: 0.5rem;
53
+ }
54
+
55
+ .co-timeline-title {
56
+ font-size: 0.8rem;
57
+ font-weight: 800;
58
+ color: var(--co-muted);
59
+ text-transform: uppercase;
60
+ letter-spacing: 0.12em;
61
+ }
62
+
63
+ .theme-dark .co-timeline-title {
64
+ color: var(--co-muted-dark);
65
+ }
66
+
67
+ .co-timeline-phase {
68
+ font-size: 0.65rem;
69
+ font-weight: 700;
70
+ color: var(--co-muted);
71
+ background: var(--co-bg);
72
+ padding: 0.25rem 0.75rem;
73
+ border-radius: 100px;
74
+ }
75
+
76
+ .theme-dark .co-timeline-phase {
77
+ color: var(--co-muted-dark);
78
+ background: var(--co-surface-dark);
79
+ }
80
+
81
+ .co-timeline-svg {
82
+ width: 100%;
83
+ height: auto;
84
+ display: block;
85
+ }
86
+
87
+ /* ===== Body: controls + result side by side ===== */
88
+ .co-body {
89
+ display: grid;
90
+ grid-template-columns: 1fr 1fr;
91
+ gap: 0;
92
+ }
93
+
94
+ @media (max-width: 700px) {
95
+ .co-body {
96
+ grid-template-columns: 1fr;
97
+ }
98
+ }
99
+
100
+ /* ===== Controls (left) ===== */
101
+ .co-controls {
102
+ padding: 1.25rem 1.5rem;
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 1rem;
106
+ border-right: 1px solid var(--co-border);
107
+ }
108
+
109
+ .theme-dark .co-controls {
110
+ border-right-color: var(--co-border-dark);
111
+ }
112
+
113
+ @media (max-width: 700px) {
114
+ .co-controls {
115
+ border-right: none;
116
+ border-bottom: 1px solid var(--co-border);
117
+ }
118
+ .theme-dark .co-controls {
119
+ border-bottom-color: var(--co-border-dark);
120
+ }
121
+ }
122
+
123
+ .co-control-group {
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: 0.35rem;
127
+ }
128
+
129
+ .co-control-label {
130
+ font-size: 0.65rem;
131
+ font-weight: 700;
132
+ color: var(--co-muted);
133
+ text-transform: uppercase;
134
+ letter-spacing: 0.1em;
135
+ }
136
+
137
+ .theme-dark .co-control-label {
138
+ color: var(--co-muted-dark);
139
+ }
140
+
141
+ .co-select {
142
+ background: var(--co-bg);
143
+ border: 1px solid var(--co-border);
144
+ border-radius: 10px;
145
+ color: var(--co-ink);
146
+ padding: 0.65rem 0.85rem;
147
+ font-size: 0.85rem;
148
+ font-weight: 600;
149
+ width: 100%;
150
+ box-sizing: border-box;
151
+ outline: none;
152
+ cursor: pointer;
153
+ appearance: none;
154
+ -webkit-appearance: none;
155
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a8078' stroke-width='1.5'/%3E%3C/svg%3E");
156
+ background-repeat: no-repeat;
157
+ background-position: right 12px center;
158
+ padding-right: 2.2rem;
159
+ transition: border-color 0.2s ease;
160
+ }
161
+
162
+ .theme-dark .co-select {
163
+ background: var(--co-surface-dark);
164
+ border-color: var(--co-border-dark);
165
+ color: var(--co-ink-dark);
166
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236b7280' stroke-width='1.5'/%3E%3C/svg%3E");
167
+ }
168
+
169
+ .co-select:focus {
170
+ border-color: var(--co-orange);
171
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--co-orange) 25%, transparent);
172
+ }
173
+
174
+ .co-slider-group {
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 0.75rem;
178
+ }
179
+
180
+ .co-range {
181
+ -webkit-appearance: none;
182
+ appearance: none;
183
+ flex: 1;
184
+ height: 6px;
185
+ border-radius: 4px;
186
+ background: var(--co-border);
187
+ outline: none;
188
+ cursor: pointer;
189
+ min-width: 0;
190
+ }
191
+
192
+ .theme-dark .co-range {
193
+ background: var(--co-border-dark);
194
+ }
195
+
196
+ .co-range::-webkit-slider-thumb {
197
+ -webkit-appearance: none;
198
+ appearance: none;
199
+ width: 22px;
200
+ height: 22px;
201
+ border-radius: 50%;
202
+ background: linear-gradient(135deg, var(--co-orange), #ea580c);
203
+ border: 3px solid var(--co-surface);
204
+ box-shadow: 0 2px 6px rgba(249,115,22,0.35);
205
+ transition: transform 0.15s ease;
206
+ cursor: pointer;
207
+ }
208
+
209
+ .theme-dark .co-range::-webkit-slider-thumb {
210
+ border-color: var(--co-surface-dark);
211
+ }
212
+
213
+ .co-range::-webkit-slider-thumb:hover {
214
+ transform: scale(1.2);
215
+ }
216
+
217
+ .co-range::-moz-range-thumb {
218
+ width: 22px;
219
+ height: 22px;
220
+ border-radius: 50%;
221
+ background: linear-gradient(135deg, var(--co-orange), #ea580c);
222
+ border: 3px solid var(--co-surface);
223
+ box-shadow: 0 2px 6px rgba(249,115,22,0.35);
224
+ cursor: pointer;
225
+ }
226
+
227
+ .theme-dark .co-range::-moz-range-thumb {
228
+ border-color: var(--co-surface-dark);
229
+ }
230
+
231
+ .co-range-value {
232
+ font-size: 1rem;
233
+ font-weight: 800;
234
+ color: var(--co-ink);
235
+ white-space: nowrap;
236
+ font-variant-numeric: tabular-nums;
237
+ min-width: 4.5rem;
238
+ text-align: right;
239
+ }
240
+
241
+ .theme-dark .co-range-value {
242
+ color: var(--co-ink-dark);
243
+ }
244
+
245
+ .co-range-unit {
246
+ font-size: 0.6rem;
247
+ font-weight: 600;
248
+ color: var(--co-muted);
249
+ }
250
+
251
+ /* ===== Unit Toggle ===== */
252
+ .co-unit-toggle {
253
+ display: flex;
254
+ gap: 0;
255
+ border: 1px solid var(--co-border);
256
+ border-radius: 8px;
257
+ overflow: hidden;
258
+ }
259
+
260
+ .theme-dark .co-unit-toggle {
261
+ border-color: var(--co-border-dark);
262
+ }
263
+
264
+ .co-unit-btn {
265
+ flex: 1;
266
+ font-size: 0.7rem;
267
+ font-weight: 700;
268
+ padding: 0.5rem 0.75rem;
269
+ border: none;
270
+ cursor: pointer;
271
+ background: transparent;
272
+ color: var(--co-muted);
273
+ transition: all 0.2s ease;
274
+ letter-spacing: 0.04em;
275
+ }
276
+
277
+ .theme-dark .co-unit-btn {
278
+ color: var(--co-muted-dark);
279
+ }
280
+
281
+ .co-unit-btn.active {
282
+ background: var(--co-orange);
283
+ color: var(--co-surface);
284
+ }
285
+
286
+ .theme-dark .co-unit-btn.active {
287
+ background: var(--co-orange-dark);
288
+ color: var(--co-black);
289
+ }
290
+
291
+ .co-unit-btn:first-child {
292
+ border-right: 1px solid var(--co-border);
293
+ }
294
+
295
+ .theme-dark .co-unit-btn:first-child {
296
+ border-right-color: var(--co-border-dark);
297
+ }
298
+
299
+ /* ===== Result Panel (right) ===== */
300
+ .co-result {
301
+ padding: 1.5rem;
302
+ display: flex;
303
+ flex-direction: column;
304
+ align-items: center;
305
+ gap: 1.25rem;
306
+ background: radial-gradient(ellipse at 50% 25%, color-mix(in srgb, var(--co-orange) 6%, transparent) 0%, transparent 70%);
307
+ }
308
+
309
+ /* ===== Gauge ===== */
310
+ .co-gauge-wrap {
311
+ display: flex;
312
+ flex-direction: column;
313
+ align-items: center;
314
+ gap: 0.3rem;
315
+ }
316
+
317
+ .co-gauge-svg {
318
+ width: 170px;
319
+ height: 150px;
320
+ }
321
+
322
+ .co-gauge-track {
323
+ fill: none;
324
+ stroke: var(--co-border);
325
+ stroke-width: 8;
326
+ }
327
+
328
+ .theme-dark .co-gauge-track {
329
+ stroke: var(--co-border-dark);
330
+ }
331
+
332
+ .co-gauge-arc {
333
+ fill: none;
334
+ stroke: var(--co-orange);
335
+ stroke-width: 8;
336
+ stroke-linecap: round;
337
+ transition: stroke-dashoffset 0.3s ease;
338
+ }
339
+
340
+ .theme-dark .co-gauge-arc {
341
+ stroke: var(--co-orange-dark);
342
+ }
343
+
344
+ .co-gauge-center {
345
+ font-size: 2rem;
346
+ font-weight: 900;
347
+ fill: var(--co-ink);
348
+ font-variant-numeric: tabular-nums;
349
+ }
350
+
351
+ .theme-dark .co-gauge-center {
352
+ fill: var(--co-ink-dark);
353
+ }
354
+
355
+ .co-gauge-unit {
356
+ font-size: 0.85rem;
357
+ font-weight: 600;
358
+ fill: var(--co-orange);
359
+ }
360
+
361
+ .theme-dark .co-gauge-unit {
362
+ fill: var(--co-orange-dark);
363
+ }
364
+
365
+ .co-gauge-label {
366
+ font-size: 0.65rem;
367
+ font-weight: 700;
368
+ color: var(--co-muted);
369
+ text-transform: uppercase;
370
+ letter-spacing: 0.1em;
371
+ }
372
+
373
+ .theme-dark .co-gauge-label {
374
+ color: var(--co-muted-dark);
375
+ }
376
+
377
+ .co-gauge-target-line {
378
+ stroke: var(--co-ink);
379
+ stroke-width: 2;
380
+ stroke-dasharray: 3 3;
381
+ transition: transform 0.3s ease;
382
+ opacity: 0.5;
383
+ }
384
+
385
+ .theme-dark .co-gauge-target-line {
386
+ stroke: var(--co-ink-dark);
387
+ }
388
+
389
+ /* ===== Stats Row ===== */
390
+ .co-stats-row {
391
+ display: flex;
392
+ gap: 0.75rem;
393
+ width: 100%;
394
+ justify-content: center;
395
+ }
396
+
397
+ .co-stat-card {
398
+ flex: 1;
399
+ max-width: 130px;
400
+ text-align: center;
401
+ padding: 0.75rem 0.5rem;
402
+ border-radius: 1rem;
403
+ background: var(--co-bg);
404
+ border: 1px solid var(--co-border);
405
+ }
406
+
407
+ .theme-dark .co-stat-card {
408
+ background: var(--co-surface-dark);
409
+ border-color: var(--co-border-dark);
410
+ }
411
+
412
+ .co-stat-label {
413
+ display: block;
414
+ font-size: 0.55rem;
415
+ font-weight: 700;
416
+ color: var(--co-muted);
417
+ text-transform: uppercase;
418
+ letter-spacing: 0.08em;
419
+ margin-bottom: 0.2rem;
420
+ }
421
+
422
+ .theme-dark .co-stat-label {
423
+ color: var(--co-muted-dark);
424
+ }
425
+
426
+ .co-stat-number {
427
+ display: block;
428
+ font-size: 1.5rem;
429
+ font-weight: 900;
430
+ color: var(--co-ink);
431
+ font-variant-numeric: tabular-nums;
432
+ line-height: 1.1;
433
+ }
434
+
435
+ .theme-dark .co-stat-number {
436
+ color: var(--co-ink-dark);
437
+ }
438
+
439
+ .co-stat-unit {
440
+ font-size: 0.65rem;
441
+ font-weight: 600;
442
+ color: var(--co-muted);
443
+ }
444
+
445
+ .theme-dark .co-stat-unit {
446
+ color: var(--co-muted-dark);
447
+ }
448
+
449
+ .co-stat-badge {
450
+ display: inline-block;
451
+ background: color-mix(in srgb, var(--co-orange) 15%, transparent);
452
+ padding: 0.1rem 0.5rem 0.1rem 0.4rem;
453
+ border-radius: 100px;
454
+ font-size: 1.5rem;
455
+ font-weight: 900;
456
+ color: var(--co-orange);
457
+ line-height: 1.3;
458
+ }
459
+
460
+ .theme-dark .co-stat-badge {
461
+ background: color-mix(in srgb, var(--co-orange-dark) 15%, transparent);
462
+ color: var(--co-orange-dark);
463
+ }
464
+
465
+ .co-stat-badge .co-stat-unit {
466
+ font-size: 0.65rem;
467
+ color: inherit;
468
+ opacity: 0.7;
469
+ }
470
+
471
+ /* ===== Error ===== */
472
+ .co-error {
473
+ padding: 0.75rem 1.5rem;
474
+ }
475
+
476
+ .co-error-text {
477
+ font-size: 0.8rem;
478
+ color: var(--co-red);
479
+ line-height: 1.5;
480
+ margin: 0;
481
+ padding: 0.75rem 1rem;
482
+ background: rgba(220,38,38,0.06);
483
+ border: 1px solid rgba(220,38,38,0.12);
484
+ border-radius: 0.75rem;
485
+ }
486
+
487
+ .theme-dark .co-error-text {
488
+ color: var(--co-red-dark);
489
+ background: rgba(239,68,68,0.06);
490
+ border-color: rgba(239,68,68,0.12);
491
+ }
492
+
493
+ /* ===== Footer ===== */
494
+ .co-footer {
495
+ padding: 0 1.5rem 1.25rem;
496
+ }
497
+
498
+ .co-footer-text {
499
+ font-size: 0.75rem;
500
+ color: var(--co-muted);
501
+ margin: 0;
502
+ padding: 0.75rem 1rem;
503
+ background: var(--co-bg);
504
+ border: 1px solid var(--co-border);
505
+ border-radius: 0.75rem;
506
+ line-height: 1.6;
507
+ }
508
+
509
+ .theme-dark .co-footer-text {
510
+ color: var(--co-muted-dark);
511
+ background: var(--co-surface-dark);
512
+ border-color: var(--co-border-dark);
513
+ }