@jjlmoya/utils-textiles 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 (134) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +76 -0
  3. package/src/category/i18n/es.ts +76 -0
  4. package/src/category/i18n/fr.ts +73 -0
  5. package/src/category/index.ts +17 -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 +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +32 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +253 -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/seo_length.test.ts +22 -0
  21. package/src/tests/title_separators.test.ts +40 -0
  22. package/src/tests/tool_validation.test.ts +17 -0
  23. package/src/tool/burnTest/bibliography.astro +34 -0
  24. package/src/tool/burnTest/component.astro +602 -0
  25. package/src/tool/burnTest/i18n/en.ts +225 -0
  26. package/src/tool/burnTest/i18n/es.ts +225 -0
  27. package/src/tool/burnTest/i18n/fr.ts +225 -0
  28. package/src/tool/burnTest/index.ts +34 -0
  29. package/src/tool/burnTest/logic.ts +3 -0
  30. package/src/tool/burnTest/seo.astro +40 -0
  31. package/src/tool/burnTest/ui.ts +36 -0
  32. package/src/tool/clothingSizeConverter/bibliography.astro +34 -0
  33. package/src/tool/clothingSizeConverter/component.astro +1226 -0
  34. package/src/tool/clothingSizeConverter/i18n/en.ts +356 -0
  35. package/src/tool/clothingSizeConverter/i18n/es.ts +356 -0
  36. package/src/tool/clothingSizeConverter/i18n/fr.ts +356 -0
  37. package/src/tool/clothingSizeConverter/index.ts +34 -0
  38. package/src/tool/clothingSizeConverter/logic.ts +30 -0
  39. package/src/tool/clothingSizeConverter/seo.astro +40 -0
  40. package/src/tool/clothingSizeConverter/ui.ts +70 -0
  41. package/src/tool/fabricProjectCalculator/bibliography.astro +19 -0
  42. package/src/tool/fabricProjectCalculator/component.astro +914 -0
  43. package/src/tool/fabricProjectCalculator/i18n/en.ts +250 -0
  44. package/src/tool/fabricProjectCalculator/i18n/es.ts +250 -0
  45. package/src/tool/fabricProjectCalculator/i18n/fr.ts +250 -0
  46. package/src/tool/fabricProjectCalculator/index.ts +34 -0
  47. package/src/tool/fabricProjectCalculator/logic.ts +89 -0
  48. package/src/tool/fabricProjectCalculator/seo.astro +40 -0
  49. package/src/tool/fabricProjectCalculator/ui.ts +57 -0
  50. package/src/tool/fabricTruth/bibliography.astro +40 -0
  51. package/src/tool/fabricTruth/component.astro +708 -0
  52. package/src/tool/fabricTruth/i18n/en.ts +282 -0
  53. package/src/tool/fabricTruth/i18n/es.ts +316 -0
  54. package/src/tool/fabricTruth/i18n/fr.ts +282 -0
  55. package/src/tool/fabricTruth/index.ts +34 -0
  56. package/src/tool/fabricTruth/logic.ts +91 -0
  57. package/src/tool/fabricTruth/seo.astro +40 -0
  58. package/src/tool/fabricTruth/ui.ts +28 -0
  59. package/src/tool/fiberPrep/bibliography.astro +33 -0
  60. package/src/tool/fiberPrep/component.astro +987 -0
  61. package/src/tool/fiberPrep/i18n/en.ts +225 -0
  62. package/src/tool/fiberPrep/i18n/es.ts +225 -0
  63. package/src/tool/fiberPrep/i18n/fr.ts +225 -0
  64. package/src/tool/fiberPrep/index.ts +34 -0
  65. package/src/tool/fiberPrep/logic.ts +36 -0
  66. package/src/tool/fiberPrep/seo.astro +40 -0
  67. package/src/tool/fiberPrep/ui.ts +38 -0
  68. package/src/tool/knittingGauge/bibliography.astro +14 -0
  69. package/src/tool/knittingGauge/component.astro +828 -0
  70. package/src/tool/knittingGauge/i18n/en.ts +234 -0
  71. package/src/tool/knittingGauge/i18n/es.ts +234 -0
  72. package/src/tool/knittingGauge/i18n/fr.ts +234 -0
  73. package/src/tool/knittingGauge/index.ts +34 -0
  74. package/src/tool/knittingGauge/logic.ts +28 -0
  75. package/src/tool/knittingGauge/seo.astro +40 -0
  76. package/src/tool/knittingGauge/ui.ts +41 -0
  77. package/src/tool/laundryGuide/bibliography.astro +33 -0
  78. package/src/tool/laundryGuide/component.astro +486 -0
  79. package/src/tool/laundryGuide/data-en.ts +166 -0
  80. package/src/tool/laundryGuide/data-es.ts +166 -0
  81. package/src/tool/laundryGuide/data-fr.ts +103 -0
  82. package/src/tool/laundryGuide/i18n/en.ts +275 -0
  83. package/src/tool/laundryGuide/i18n/es.ts +275 -0
  84. package/src/tool/laundryGuide/i18n/fr.ts +291 -0
  85. package/src/tool/laundryGuide/index.ts +29 -0
  86. package/src/tool/laundryGuide/logic.ts +19 -0
  87. package/src/tool/laundryGuide/seo.astro +40 -0
  88. package/src/tool/needleConverter/bibliography.astro +14 -0
  89. package/src/tool/needleConverter/component.astro +518 -0
  90. package/src/tool/needleConverter/i18n/en.ts +217 -0
  91. package/src/tool/needleConverter/i18n/es.ts +217 -0
  92. package/src/tool/needleConverter/i18n/fr.ts +217 -0
  93. package/src/tool/needleConverter/index.ts +34 -0
  94. package/src/tool/needleConverter/logic.ts +31 -0
  95. package/src/tool/needleConverter/seo.astro +15 -0
  96. package/src/tool/needleConverter/ui.ts +17 -0
  97. package/src/tool/sewingPatternScaler/bibliography.astro +14 -0
  98. package/src/tool/sewingPatternScaler/component.astro +550 -0
  99. package/src/tool/sewingPatternScaler/i18n/en.ts +196 -0
  100. package/src/tool/sewingPatternScaler/i18n/es.ts +231 -0
  101. package/src/tool/sewingPatternScaler/i18n/fr.ts +187 -0
  102. package/src/tool/sewingPatternScaler/index.ts +34 -0
  103. package/src/tool/sewingPatternScaler/logic.ts +35 -0
  104. package/src/tool/sewingPatternScaler/seo.astro +49 -0
  105. package/src/tool/sewingPatternScaler/ui.ts +29 -0
  106. package/src/tool/shoeSizeConverter/bibliography.astro +34 -0
  107. package/src/tool/shoeSizeConverter/component.astro +437 -0
  108. package/src/tool/shoeSizeConverter/i18n/en.ts +188 -0
  109. package/src/tool/shoeSizeConverter/i18n/es.ts +188 -0
  110. package/src/tool/shoeSizeConverter/i18n/fr.ts +188 -0
  111. package/src/tool/shoeSizeConverter/index.ts +34 -0
  112. package/src/tool/shoeSizeConverter/logic.ts +8 -0
  113. package/src/tool/shoeSizeConverter/seo.astro +40 -0
  114. package/src/tool/shoeSizeConverter/ui.ts +21 -0
  115. package/src/tool/stainChemistry/bibliography.astro +40 -0
  116. package/src/tool/stainChemistry/component.astro +717 -0
  117. package/src/tool/stainChemistry/i18n/en.ts +303 -0
  118. package/src/tool/stainChemistry/i18n/es.ts +300 -0
  119. package/src/tool/stainChemistry/i18n/fr.ts +310 -0
  120. package/src/tool/stainChemistry/index.ts +34 -0
  121. package/src/tool/stainChemistry/logic.ts +11 -0
  122. package/src/tool/stainChemistry/seo.astro +40 -0
  123. package/src/tool/stainChemistry/ui.ts +51 -0
  124. package/src/tool/yarnCalculator/bibliography.astro +19 -0
  125. package/src/tool/yarnCalculator/component.astro +792 -0
  126. package/src/tool/yarnCalculator/i18n/en.ts +310 -0
  127. package/src/tool/yarnCalculator/i18n/es.ts +310 -0
  128. package/src/tool/yarnCalculator/i18n/fr.ts +310 -0
  129. package/src/tool/yarnCalculator/index.ts +34 -0
  130. package/src/tool/yarnCalculator/logic.ts +84 -0
  131. package/src/tool/yarnCalculator/seo.astro +14 -0
  132. package/src/tool/yarnCalculator/ui.ts +42 -0
  133. package/src/tools.ts +16 -0
  134. package/src/types.ts +72 -0
@@ -0,0 +1,49 @@
1
+ ---
2
+ import {
3
+ SEOTitle,
4
+ SEOCard,
5
+ SEOTip,
6
+ SEOList,
7
+ SEOArticle,
8
+ SEOStats,
9
+ SEOProsCons,
10
+ SEOGlossary,
11
+ } from '@jjlmoya/utils-shared';
12
+ import { sewingPatternScaler } from './index';
13
+ import type { KnownLocale } from '../../types';
14
+
15
+ interface Props {
16
+ locale?: KnownLocale;
17
+ }
18
+
19
+ const { locale = 'es' } = Astro.props;
20
+ const content = await sewingPatternScaler.i18n[locale]?.();
21
+ if (!content) return null;
22
+
23
+ const { seo } = content;
24
+ ---
25
+
26
+ <SEOArticle>
27
+ {seo.map((section: any) => {
28
+ switch (section.type) {
29
+ case 'title':
30
+ return <SEOTitle title={section.text} level={section.level || 2} />;
31
+ case 'paragraph':
32
+ return <p set:html={section.html} />;
33
+ case 'list':
34
+ return <SEOList items={section.items} />;
35
+ case 'card':
36
+ return <SEOCard title={section.title} icon={section.icon}><Fragment set:html={section.html} /></SEOCard>;
37
+ case 'tip':
38
+ return <SEOTip title={section.title}><Fragment set:html={section.html} /></SEOTip>;
39
+ case 'stats':
40
+ return <SEOStats stats={section.items} columns={section.columns} />;
41
+ case 'proscons':
42
+ return <SEOProsCons items={section.items} />;
43
+ case 'glossary':
44
+ return <SEOGlossary items={section.items} />;
45
+ default:
46
+ return null;
47
+ }
48
+ })}
49
+ </SEOArticle>
@@ -0,0 +1,29 @@
1
+ export interface SewingPatternScalerUI {
2
+ labelSystem: string;
3
+ modeStandard: string;
4
+ modeCustom: string;
5
+ labelPatternSize: string;
6
+ labelTargetSize: string;
7
+ labelOriginMeasures: string;
8
+ labelTargetMeasures: string;
9
+ labelEase: string;
10
+ labelChest: string;
11
+ labelWaist: string;
12
+ labelHips: string;
13
+ labelLength: string;
14
+ resBust: string;
15
+ resWaist: string;
16
+ resHips: string;
17
+ resLength: string;
18
+ actionLatAdd: string;
19
+ actionLatRemove: string;
20
+ actionLatSuffix: string;
21
+ actionLatNote: string;
22
+ actionLenExtend: string;
23
+ actionLenShorten: string;
24
+ actionLenNote: string;
25
+ legendOriginal: string;
26
+ legendUpdated: string;
27
+ warnText: string;
28
+ sizePrefix: string;
29
+ }
@@ -0,0 +1,34 @@
1
+ ---
2
+ ---
3
+ <div class="bibliography">
4
+ <h3>References</h3>
5
+ <ul>
6
+ <li><a href="https://en.wikipedia.org/wiki/Shoe_size" target="_blank">Shoe size conversion - Wikipedia</a></li>
7
+ <li><a href="https://www.iso.org/standard/17157.html" target="_blank">ISO 9407: Shoe sizes - Mondopoint system</a></li>
8
+ <li><a href="https://www.zappos.com/c/shoe-size-conversion" target="_blank">Shoe size conversion - Zappos</a></li>
9
+ </ul>
10
+ </div>
11
+ <style>
12
+ .bibliography {
13
+ padding: 1rem 0;
14
+ }
15
+ .bibliography h3 {
16
+ font-size: 1.1rem;
17
+ font-weight: 600;
18
+ margin-bottom: 1rem;
19
+ }
20
+ .bibliography ul {
21
+ list-style: none;
22
+ padding: 0;
23
+ }
24
+ .bibliography li {
25
+ margin-bottom: 0.5rem;
26
+ }
27
+ .bibliography a {
28
+ color: #6366f1;
29
+ text-decoration: none;
30
+ }
31
+ .bibliography a:hover {
32
+ text-decoration: underline;
33
+ }
34
+ </style>
@@ -0,0 +1,437 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { ShoeSizeConverterUI } from './ui';
4
+
5
+ interface Props {
6
+ ui?: Record<string, unknown>;
7
+ }
8
+
9
+ const { ui = {} } = Astro.props;
10
+ const sizeUI = ui as ShoeSizeConverterUI;
11
+ ---
12
+
13
+ <div class="shoe-converter">
14
+ <div class="converter-card">
15
+ <div class="gender-selector">
16
+ <button class="gender-btn gender-btn-men active" data-gender="men" title={sizeUI.genderMenTitle ?? 'Men'}>
17
+ <Icon name="mdi:gender-male" />
18
+ </button>
19
+ <button class="gender-btn gender-btn-women" data-gender="women" title={sizeUI.genderWomenTitle ?? 'Women'}>
20
+ <Icon name="mdi:gender-female" />
21
+ </button>
22
+ </div>
23
+
24
+ <div class="size-section">
25
+ <div class="field-group">
26
+ <label class="field-label" for="origin-region">{sizeUI.regionLabel ?? 'Your Size System'}</label>
27
+ <div class="select-wrap">
28
+ <select id="origin-region" class="region-select">
29
+ <option value="EU">{sizeUI.regionEU ?? 'Europe (EU)'}</option>
30
+ <option value="US">{sizeUI.regionUS ?? 'United States (US)'}</option>
31
+ <option value="UK">{sizeUI.regionUK ?? 'United Kingdom (UK)'}</option>
32
+ <option value="CM">{sizeUI.regionCM ?? 'Mondopoint (CM)'}</option>
33
+ </select>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="size-grid-container">
38
+ <div class="grid-header">
39
+ <h3>{sizeUI.gridTitle ?? 'Select Your Size'}</h3>
40
+ <p>{sizeUI.gridDesc ?? 'Click to see international equivalences'}</p>
41
+ </div>
42
+ <div class="size-grid" id="quick-grid"></div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="results-container">
47
+ <div class="result-card">
48
+ <span class="result-label" id="label-1">-</span>
49
+ <span class="result-value" id="val-1">-</span>
50
+ <span class="result-unit hidden" id="unit-1">{sizeUI.unitCM ?? 'cm'}</span>
51
+ </div>
52
+ <div class="result-card">
53
+ <span class="result-label" id="label-2">-</span>
54
+ <span class="result-value" id="val-2">-</span>
55
+ <span class="result-unit hidden" id="unit-2">{sizeUI.unitCM ?? 'cm'}</span>
56
+ </div>
57
+ <div class="result-card">
58
+ <span class="result-label" id="label-3">-</span>
59
+ <span class="result-value" id="val-3">-</span>
60
+ <span class="result-unit hidden" id="unit-3">{sizeUI.unitCM ?? 'cm'}</span>
61
+ </div>
62
+ </div>
63
+
64
+ <div class="info-banner">
65
+ <Icon name="mdi:information-outline" />
66
+ <p set:html={sizeUI.infoBannerHtml ?? ''} />
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <script define:vars={{ ui }} is:inline>
72
+ window.__shoeSizeUI = ui;
73
+ </script>
74
+
75
+ <script>
76
+ import type { ShoeSizeConverterUI } from './ui';
77
+ import type { SizeMapping, ShoeSizeRegion } from './logic';
78
+
79
+ const rawUI = (window as unknown as { __shoeSizeUI: ShoeSizeConverterUI }).__shoeSizeUI;
80
+
81
+ const dataMap: Record<string, SizeMapping[]> = {
82
+ men: rawUI.menData,
83
+ women: rawUI.womenData,
84
+ };
85
+
86
+ const labels: Record<ShoeSizeRegion, string> = {
87
+ EU: rawUI.labelEU,
88
+ US: rawUI.labelUS,
89
+ UK: rawUI.labelUK,
90
+ CM: rawUI.labelCM,
91
+ };
92
+
93
+ let currentGender = 'men';
94
+ let currentOrigin: ShoeSizeRegion = 'EU';
95
+ let currentSize = '';
96
+
97
+ function getEl(id: string): HTMLElement | null {
98
+ return document.getElementById(id);
99
+ }
100
+
101
+ function setElText(id: string, text: string) {
102
+ const el = getEl(id);
103
+ if (el) el.textContent = text;
104
+ }
105
+
106
+ function updateResults(mapping: SizeMapping) {
107
+ const regions = (['EU', 'US', 'UK', 'CM'] as ShoeSizeRegion[]).filter(
108
+ (r) => r !== currentOrigin,
109
+ );
110
+ regions.forEach((region, i) => {
111
+ const idx = i + 1;
112
+ setElText(`val-${idx}`, mapping[region]);
113
+ setElText(`label-${idx}`, labels[region]);
114
+ const unitEl = getEl(`unit-${idx}`);
115
+ if (unitEl) unitEl.classList.toggle('hidden', region !== 'CM');
116
+ });
117
+ }
118
+
119
+ function makeCell(item: SizeMapping): HTMLDivElement {
120
+ const cellValue = item[currentOrigin];
121
+ const cell = document.createElement('div');
122
+ cell.className = `size-cell${cellValue === currentSize ? ' active' : ''}`;
123
+ cell.textContent = cellValue;
124
+ cell.addEventListener('click', () => {
125
+ currentSize = cellValue;
126
+ updateResults(item);
127
+ renderGrid();
128
+ });
129
+ return cell;
130
+ }
131
+
132
+ function renderGrid() {
133
+ const grid = getEl('quick-grid');
134
+ if (!grid) return;
135
+ const data = dataMap[currentGender];
136
+ grid.innerHTML = '';
137
+ data.forEach((item) => grid.appendChild(makeCell(item)));
138
+ }
139
+
140
+ function initGrid() {
141
+ const data = dataMap[currentGender];
142
+ if (data.length > 0) {
143
+ currentSize = data[0][currentOrigin];
144
+ updateResults(data[0]);
145
+ }
146
+ renderGrid();
147
+ }
148
+
149
+ function init() {
150
+ const genderBtns = document.querySelectorAll('[data-gender]');
151
+ genderBtns.forEach((btn) => {
152
+ btn.addEventListener('click', () => {
153
+ genderBtns.forEach((b) => b.classList.remove('active'));
154
+ btn.classList.add('active');
155
+ currentGender = (btn as HTMLElement).dataset.gender ?? 'men';
156
+ currentSize = '';
157
+ initGrid();
158
+ });
159
+ });
160
+
161
+ const originSelect = getEl('origin-region') as HTMLSelectElement | null;
162
+ originSelect?.addEventListener('change', () => {
163
+ currentOrigin = originSelect.value as ShoeSizeRegion;
164
+ currentSize = '';
165
+ initGrid();
166
+ });
167
+
168
+ initGrid();
169
+ }
170
+
171
+ function startInit() {
172
+ if (document.readyState === 'loading') {
173
+ document.addEventListener('DOMContentLoaded', init);
174
+ } else {
175
+ setTimeout(init, 100);
176
+ }
177
+ document.addEventListener('astro:page-load', init);
178
+ }
179
+
180
+ startInit();
181
+ </script>
182
+
183
+ <style>
184
+ .shoe-converter {
185
+ max-width: 800px;
186
+ margin: 0 auto;
187
+ padding: 1rem 1rem 4rem;
188
+ }
189
+
190
+ .converter-card {
191
+ background: var(--bg-surface);
192
+ border: 1px solid var(--border-color);
193
+ border-radius: 2.5rem;
194
+ padding: 3rem;
195
+ box-shadow: 0 25px 60px rgba(0, 0, 0, 0.05);
196
+ display: flex;
197
+ flex-direction: column;
198
+ gap: 3rem;
199
+ }
200
+
201
+ .theme-dark .converter-card {
202
+ box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3);
203
+ }
204
+
205
+ .gender-selector {
206
+ display: flex;
207
+ justify-content: center;
208
+ gap: 2rem;
209
+ }
210
+
211
+ .gender-btn {
212
+ width: 80px;
213
+ height: 80px;
214
+ border-radius: 2rem;
215
+ border: 2px solid transparent;
216
+ background: var(--bg-page);
217
+ cursor: pointer;
218
+ transition: all 0.3s;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ color: var(--text-muted);
223
+ }
224
+
225
+ .gender-btn svg {
226
+ width: 2.5rem;
227
+ height: 2.5rem;
228
+ }
229
+
230
+ .gender-btn-men.active {
231
+ background: rgba(59, 130, 246, 0.08);
232
+ border-color: #3b82f6;
233
+ color: #3b82f6;
234
+ box-shadow: 0 10px 25px rgba(59, 130, 246, 0.2);
235
+ transform: translateY(-4px);
236
+ }
237
+
238
+ .gender-btn-women.active {
239
+ background: rgba(244, 63, 94, 0.08);
240
+ border-color: #f43f5e;
241
+ color: #f43f5e;
242
+ box-shadow: 0 10px 25px rgba(244, 63, 94, 0.2);
243
+ transform: translateY(-4px);
244
+ }
245
+
246
+ .size-section {
247
+ display: flex;
248
+ flex-direction: column;
249
+ gap: 1.5rem;
250
+ }
251
+
252
+ .field-group {
253
+ display: flex;
254
+ flex-direction: column;
255
+ gap: 0.5rem;
256
+ }
257
+
258
+ .field-label {
259
+ font-size: 0.75rem;
260
+ font-weight: 800;
261
+ color: var(--text-muted);
262
+ text-transform: uppercase;
263
+ letter-spacing: 0.1em;
264
+ }
265
+
266
+ .select-wrap {
267
+ position: relative;
268
+ }
269
+
270
+ .select-wrap::after {
271
+ content: '▾';
272
+ position: absolute;
273
+ right: 1.25rem;
274
+ top: 50%;
275
+ transform: translateY(-50%);
276
+ pointer-events: none;
277
+ color: var(--text-muted);
278
+ font-size: 1rem;
279
+ }
280
+
281
+ .region-select {
282
+ appearance: none;
283
+ width: 100%;
284
+ background: var(--bg-page);
285
+ border: 1.5px solid var(--border-color);
286
+ border-radius: 1rem;
287
+ padding: 0.875rem 2.75rem 0.875rem 1.25rem;
288
+ font-size: 1rem;
289
+ font-weight: 600;
290
+ color: var(--text-main);
291
+ outline: none;
292
+ cursor: pointer;
293
+ transition: border-color 0.2s;
294
+ }
295
+
296
+ .region-select:focus {
297
+ border-color: #3b82f6;
298
+ }
299
+
300
+ .size-grid-container {
301
+ display: flex;
302
+ flex-direction: column;
303
+ gap: 1.5rem;
304
+ }
305
+
306
+ .grid-header {
307
+ text-align: center;
308
+ }
309
+
310
+ .grid-header h3 {
311
+ font-size: 1.25rem;
312
+ font-weight: 800;
313
+ color: var(--text-main);
314
+ margin: 0 0 0.5rem;
315
+ }
316
+
317
+ .grid-header p {
318
+ font-size: 0.875rem;
319
+ color: var(--text-muted);
320
+ margin: 0;
321
+ }
322
+
323
+ .size-grid {
324
+ display: grid;
325
+ grid-template-columns: repeat(auto-fill, minmax(65px, 1fr));
326
+ gap: 0.75rem;
327
+ }
328
+
329
+ :global(.size-cell) {
330
+ aspect-ratio: 1;
331
+ display: flex;
332
+ align-items: center;
333
+ justify-content: center;
334
+ border-radius: 1rem;
335
+ background: var(--bg-surface);
336
+ border: 1px solid var(--border-color);
337
+ font-size: 1.125rem;
338
+ font-weight: 700;
339
+ color: var(--text-muted);
340
+ cursor: pointer;
341
+ transition: all 0.2s;
342
+ }
343
+
344
+ :global(.size-cell:hover) {
345
+ border-color: #3b82f6;
346
+ color: #3b82f6;
347
+ transform: scale(1.05);
348
+ }
349
+
350
+ :global(.size-cell.active) {
351
+ background: #3b82f6;
352
+ border-color: #3b82f6;
353
+ color: white;
354
+ box-shadow: 0 10px 20px rgba(59, 130, 246, 0.25);
355
+ }
356
+
357
+ .results-container {
358
+ display: grid;
359
+ grid-template-columns: repeat(3, 1fr);
360
+ gap: 1.5rem;
361
+ background: var(--bg-page);
362
+ padding: 2rem;
363
+ border-radius: 2rem;
364
+ border: 1px solid var(--border-color);
365
+ }
366
+
367
+ .result-card {
368
+ text-align: center;
369
+ display: flex;
370
+ flex-direction: column;
371
+ gap: 0.5rem;
372
+ }
373
+
374
+ .result-label {
375
+ font-size: 0.75rem;
376
+ font-weight: 800;
377
+ color: var(--text-muted);
378
+ text-transform: uppercase;
379
+ letter-spacing: 0.05em;
380
+ }
381
+
382
+ .result-value {
383
+ font-size: 2.5rem;
384
+ font-weight: 900;
385
+ color: var(--text-main);
386
+ line-height: 1;
387
+ }
388
+
389
+ .result-unit {
390
+ font-size: 0.875rem;
391
+ font-weight: 700;
392
+ color: #3b82f6;
393
+ margin-top: 0.25rem;
394
+ }
395
+
396
+ .result-unit.hidden {
397
+ display: none;
398
+ }
399
+
400
+ .info-banner {
401
+ display: flex;
402
+ align-items: center;
403
+ gap: 1rem;
404
+ padding: 1.25rem;
405
+ background: rgba(3, 105, 161, 0.06);
406
+ border-radius: 1.25rem;
407
+ color: #0369a1;
408
+ font-size: 0.875rem;
409
+ line-height: 1.5;
410
+ }
411
+
412
+ .theme-dark .info-banner {
413
+ background: rgba(3, 105, 161, 0.1);
414
+ color: #7dd3fc;
415
+ }
416
+
417
+ .info-banner svg {
418
+ width: 1.5rem;
419
+ height: 1.5rem;
420
+ flex-shrink: 0;
421
+ }
422
+
423
+ .info-banner p {
424
+ margin: 0;
425
+ }
426
+
427
+ @media (max-width: 640px) {
428
+ .converter-card {
429
+ padding: 1.5rem;
430
+ gap: 2rem;
431
+ }
432
+
433
+ .results-container {
434
+ grid-template-columns: 1fr;
435
+ }
436
+ }
437
+ </style>