@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,717 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { StainChemistryUI } from './ui';
4
+
5
+ interface Props {
6
+ ui: StainChemistryUI;
7
+ }
8
+
9
+ const { ui } = Astro.props;
10
+
11
+ function resolveFiberType(family: string, isNoble?: boolean): string {
12
+ if (family === 'synthetic') return 'synthetic';
13
+ if (isNoble) return 'delicate';
14
+ return 'natural';
15
+ }
16
+
17
+ const fibers = Object.entries(ui.fiberData)
18
+ .map(([id, data]) => ({
19
+ id,
20
+ name: data.name,
21
+ fiberType: resolveFiberType(data.family, data.isNoble),
22
+ }))
23
+ .sort((a, b) => a.name.localeCompare(b.name));
24
+ ---
25
+
26
+ <div class="stain-chemistry-container" id="stain-chemistry-root">
27
+ <div class="card-wrapper">
28
+ <div class="selectors-section">
29
+ <div class="selectors-grid">
30
+ <div class="selector-group">
31
+ <label class="selector-label">
32
+ <Icon name="mdi:tshirt-v-outline" class="label-icon" />
33
+ {ui.fiberLabel}
34
+ </label>
35
+ <div class="select-wrapper">
36
+ <select id="fiber-select" class="selector-input">
37
+ <option value="" disabled selected>{ui.selectFiberPlaceholder}</option>
38
+ {fibers.map((fiber) => (
39
+ <option value={fiber.id} data-fiber-type={fiber.fiberType}>{fiber.name}</option>
40
+ ))}
41
+ </select>
42
+ <Icon name="mdi:chevron-down" class="select-chevron" />
43
+ </div>
44
+ </div>
45
+
46
+ <div class="selector-group">
47
+ <label class="selector-label">
48
+ <Icon name="mdi:shimmer" class="label-icon" />
49
+ {ui.stainLabel}
50
+ </label>
51
+ <div class="select-wrapper">
52
+ <select id="stain-select" class="selector-input">
53
+ <option value="" disabled selected>{ui.selectStainPlaceholder}</option>
54
+ {ui.stains.map((stain) => (
55
+ <option value={stain.id}>{stain.name}</option>
56
+ ))}
57
+ </select>
58
+ <Icon name="mdi:chevron-down" class="select-chevron" />
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ <div id="protocol-display" class="protocol-display hidden">
65
+ <div class="protocol-card">
66
+ <div class="protocol-header">
67
+ <div class="protocol-icon-wrap">
68
+ <Icon name="mdi:flask-outline" class="protocol-icon" />
69
+ </div>
70
+ <div>
71
+ <p class="protocol-meta">{ui.molecularDiagnosis}</p>
72
+ <h3 id="protocol-title" class="protocol-title">—</h3>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="protocol-body">
77
+ <div class="protocol-grid">
78
+ <div class="info-card info-card--agent">
79
+ <div class="info-card-icon-wrap agent-icon-wrap">
80
+ <Icon name="mdi:molecule" class="info-card-icon" />
81
+ </div>
82
+ <span class="info-card-meta">{ui.requiredAgent}</span>
83
+ <p id="protocol-agent" class="info-card-value">—</p>
84
+ <p id="protocol-agent-desc" class="info-card-desc"></p>
85
+ </div>
86
+
87
+ <div class="info-card info-card--temp">
88
+ <div class="info-card-icon-wrap temp-icon-wrap">
89
+ <Icon name="mdi:water-thermometer-outline" class="info-card-icon" />
90
+ </div>
91
+ <span class="info-card-meta">{ui.thermalCondition}</span>
92
+ <p id="protocol-temp" class="info-card-temp-value">—</p>
93
+ <span class="info-card-temp-label">{ui.temperature}</span>
94
+ </div>
95
+ </div>
96
+
97
+ <div class="method-card">
98
+ <div class="method-card-overlay">
99
+ <Icon name="mdi:washing-machine" class="method-overlay-icon" />
100
+ </div>
101
+ <div class="method-card-content">
102
+ <div class="method-card-header">
103
+ <div class="method-icon-wrap">
104
+ <Icon name="mdi:format-list-bulleted" class="method-icon" />
105
+ </div>
106
+ <span class="method-meta">{ui.cleaningInstructions}</span>
107
+ </div>
108
+ <div id="protocol-method" class="method-text">—</div>
109
+ </div>
110
+ </div>
111
+
112
+ <div id="protocol-warning" class="warning-card hidden">
113
+ <div class="warning-card-inner">
114
+ <Icon name="mdi:shield-alert-outline" class="warning-icon" />
115
+ <div>
116
+ <p class="warning-label">{ui.criticalWarningLabel}</p>
117
+ <p id="warning-text" class="warning-text"></p>
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ <div class="note-card">
123
+ <div class="note-card-inner">
124
+ <span class="note-badge">!</span>
125
+ <div>
126
+ <p class="note-label">{ui.technicalNote}</p>
127
+ <p class="note-text">{ui.technicalNoteText}</p>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <div id="empty-state" class="empty-state">
136
+ <div class="empty-icon-wrap">
137
+ <Icon name="mdi:hanger" class="empty-icon" />
138
+ </div>
139
+ <h4 class="empty-title">{ui.systemDiagnosis}</h4>
140
+ <p class="empty-desc">{ui.systemDiagnosisDesc}</p>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <script define:vars={{ ui }} is:inline>
146
+ window.__stainUI = ui;
147
+ </script>
148
+
149
+ <script>
150
+ import { getFiberType } from './logic';
151
+ import type { StainChemistryUI, StainChemistryStain, StainChemistryAgent, StainChemistryProtocol } from './ui';
152
+
153
+ type StainUI = StainChemistryUI;
154
+
155
+ interface ProtocolCtx {
156
+ stain: StainChemistryStain;
157
+ agent: StainChemistryAgent;
158
+ protocol: StainChemistryProtocol;
159
+ fiberType: string;
160
+ ui: StainUI;
161
+ }
162
+
163
+ function setText(id: string, value: string) {
164
+ const el = document.getElementById(id);
165
+ if (el) el.textContent = value;
166
+ }
167
+
168
+ function renderWarning({ agent, protocol, fiberType, ui }: ProtocolCtx) {
169
+ const warningDiv = document.getElementById('protocol-warning');
170
+ const warningText = document.getElementById('warning-text');
171
+ if (!warningDiv || !warningText) return;
172
+
173
+ const isDelicatePercarbonate = fiberType === 'delicate' && protocol.agentId === 'percarbonate';
174
+ const warning = isDelicatePercarbonate ? ui.delicatePercarbonateWarning : (agent.warning ?? '');
175
+ warningDiv.classList.toggle('hidden', !warning);
176
+ if (warning) warningText.textContent = warning;
177
+ }
178
+
179
+ function renderProtocol(ctx: ProtocolCtx) {
180
+ const { stain, agent, protocol } = ctx;
181
+ setText('protocol-title', stain.name);
182
+ setText('protocol-agent', agent.name);
183
+ setText('protocol-agent-desc', agent.description);
184
+ setText('protocol-temp', protocol.temperature);
185
+ setText('protocol-method', protocol.method);
186
+ renderWarning(ctx);
187
+ }
188
+
189
+ function resolveProtocol(ui: StainUI, fiberId: string, stainId: string): ProtocolCtx | null {
190
+ const fiberType = getFiberType(fiberId, ui.fiberData);
191
+ const stain = ui.stains.find((s) => s.id === stainId);
192
+ if (!stain) return null;
193
+ const protocol = stain.protocols[fiberType] ?? stain.protocols.natural;
194
+ const agent = ui.agents[protocol.agentId];
195
+ if (!agent) return null;
196
+ return { stain, agent, protocol, fiberType, ui };
197
+ }
198
+
199
+ function updateProtocol() {
200
+ const stainUI = (window as unknown as { __stainUI: StainUI }).__stainUI;
201
+ const fiberSelect = document.getElementById('fiber-select') as HTMLSelectElement;
202
+ const stainSelect = document.getElementById('stain-select') as HTMLSelectElement;
203
+ const display = document.getElementById('protocol-display');
204
+ const emptyState = document.getElementById('empty-state');
205
+
206
+ if (!fiberSelect?.value || !stainSelect?.value || !display || !emptyState) return;
207
+
208
+ const ctx = resolveProtocol(stainUI, fiberSelect.value, stainSelect.value);
209
+ if (!ctx) return;
210
+
211
+ emptyState.classList.add('hidden');
212
+ display.classList.remove('hidden');
213
+ renderProtocol(ctx);
214
+ }
215
+
216
+ function initStainChemistry() {
217
+ const root = document.getElementById('stain-chemistry-root');
218
+ if (!root) return;
219
+
220
+ const fiberSelect = document.getElementById('fiber-select');
221
+ const stainSelect = document.getElementById('stain-select');
222
+
223
+ fiberSelect?.addEventListener('change', updateProtocol);
224
+ stainSelect?.addEventListener('change', updateProtocol);
225
+ }
226
+
227
+ function startInit() {
228
+ if (document.readyState === 'loading') {
229
+ document.addEventListener('DOMContentLoaded', initStainChemistry);
230
+ } else {
231
+ setTimeout(initStainChemistry, 100);
232
+ }
233
+ document.addEventListener('astro:page-load', initStainChemistry);
234
+ }
235
+
236
+ startInit();
237
+ </script>
238
+
239
+ <style>
240
+ .stain-chemistry-container {
241
+ width: 100%;
242
+ padding: 1rem;
243
+ }
244
+
245
+ .card-wrapper {
246
+ background: var(--bg-surface);
247
+ border: 1px solid var(--border-color);
248
+ border-radius: 1.5rem;
249
+ overflow: hidden;
250
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
251
+ }
252
+
253
+
254
+ .selectors-section {
255
+ padding: 2rem;
256
+ border-bottom: 1px solid var(--border-color);
257
+ background: var(--bg-surface);
258
+ }
259
+
260
+ .selectors-grid {
261
+ display: grid;
262
+ grid-template-columns: 1fr 1fr;
263
+ gap: 1.5rem;
264
+ }
265
+
266
+ @media (max-width: 600px) {
267
+ .selectors-grid {
268
+ grid-template-columns: 1fr;
269
+ }
270
+ }
271
+
272
+ .selector-group {
273
+ display: flex;
274
+ flex-direction: column;
275
+ gap: 0.5rem;
276
+ }
277
+
278
+ .selector-label {
279
+ display: flex;
280
+ align-items: center;
281
+ gap: 0.4rem;
282
+ font-size: 0.65rem;
283
+ font-weight: 900;
284
+ color: #6366f1;
285
+ text-transform: uppercase;
286
+ letter-spacing: 0.12em;
287
+ }
288
+
289
+ .label-icon {
290
+ width: 1rem;
291
+ height: 1rem;
292
+ }
293
+
294
+ .select-wrapper {
295
+ position: relative;
296
+ }
297
+
298
+ .selector-input {
299
+ width: 100%;
300
+ height: 3.5rem;
301
+ padding: 0 2.5rem 0 1rem;
302
+ background: var(--bg-page);
303
+ border: 2px solid var(--border-color);
304
+ border-radius: 0.875rem;
305
+ appearance: none;
306
+ font-size: 0.9rem;
307
+ font-weight: 700;
308
+ color: var(--text-main);
309
+ cursor: pointer;
310
+ transition: border-color 0.2s;
311
+ outline: none;
312
+ }
313
+
314
+ .selector-input:focus {
315
+ border-color: #6366f1;
316
+ }
317
+
318
+ .select-chevron {
319
+ position: absolute;
320
+ right: 0.75rem;
321
+ top: 50%;
322
+ transform: translateY(-50%);
323
+ width: 1.25rem;
324
+ height: 1.25rem;
325
+ color: var(--text-muted);
326
+ pointer-events: none;
327
+ }
328
+
329
+
330
+ .protocol-display {
331
+ padding: 1.5rem 2rem 2rem;
332
+ background: var(--bg-page);
333
+ animation: fade-in 0.4s ease;
334
+ }
335
+
336
+ :global(.protocol-display.hidden) {
337
+ display: none;
338
+ }
339
+
340
+ @keyframes fade-in {
341
+ from {
342
+ opacity: 0;
343
+ transform: translateY(6px);
344
+ }
345
+ to {
346
+ opacity: 1;
347
+ transform: translateY(0);
348
+ }
349
+ }
350
+
351
+ .protocol-card {
352
+ background: var(--bg-surface);
353
+ border: 1px solid var(--border-color);
354
+ border-radius: 1.25rem;
355
+ overflow: hidden;
356
+ }
357
+
358
+ .protocol-header {
359
+ display: flex;
360
+ align-items: center;
361
+ gap: 1rem;
362
+ padding: 1.25rem 1.5rem;
363
+ background: var(--bg-page);
364
+ border-bottom: 1px solid var(--border-color);
365
+ }
366
+
367
+ .protocol-icon-wrap {
368
+ width: 2.75rem;
369
+ height: 2.75rem;
370
+ border-radius: 0.75rem;
371
+ background: #6366f1;
372
+ color: #fff;
373
+ display: flex;
374
+ align-items: center;
375
+ justify-content: center;
376
+ flex-shrink: 0;
377
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
378
+ }
379
+
380
+ .protocol-icon {
381
+ width: 1.5rem;
382
+ height: 1.5rem;
383
+ }
384
+
385
+ .protocol-meta {
386
+ font-size: 0.65rem;
387
+ font-weight: 900;
388
+ text-transform: uppercase;
389
+ letter-spacing: 0.12em;
390
+ color: var(--text-muted);
391
+ margin: 0 0 0.2rem;
392
+ }
393
+
394
+ .protocol-title {
395
+ font-size: 1.35rem;
396
+ font-weight: 900;
397
+ color: var(--text-main);
398
+ margin: 0;
399
+ letter-spacing: -0.02em;
400
+ }
401
+
402
+ .protocol-body {
403
+ padding: 1.5rem;
404
+ display: flex;
405
+ flex-direction: column;
406
+ gap: 1rem;
407
+ }
408
+
409
+ .protocol-grid {
410
+ display: grid;
411
+ grid-template-columns: 1fr 1fr;
412
+ gap: 1rem;
413
+ }
414
+
415
+ @media (max-width: 480px) {
416
+ .protocol-grid {
417
+ grid-template-columns: 1fr;
418
+ }
419
+ }
420
+
421
+
422
+ .info-card {
423
+ background: var(--bg-page);
424
+ border: 1px solid var(--border-color);
425
+ border-radius: 1rem;
426
+ padding: 1.25rem;
427
+ display: flex;
428
+ flex-direction: column;
429
+ align-items: center;
430
+ text-align: center;
431
+ gap: 0.4rem;
432
+ transition: border-color 0.2s;
433
+ }
434
+
435
+ .info-card:hover {
436
+ border-color: rgba(99, 102, 241, 0.3);
437
+ }
438
+
439
+ .info-card-icon-wrap {
440
+ width: 3rem;
441
+ height: 3rem;
442
+ border-radius: 50%;
443
+ display: flex;
444
+ align-items: center;
445
+ justify-content: center;
446
+ margin-bottom: 0.5rem;
447
+ }
448
+
449
+ .agent-icon-wrap {
450
+ background: rgba(99, 102, 241, 0.08);
451
+ color: #6366f1;
452
+ }
453
+
454
+ .temp-icon-wrap {
455
+ background: rgba(249, 115, 22, 0.08);
456
+ color: #f97316;
457
+ }
458
+
459
+ .info-card-icon {
460
+ width: 1.5rem;
461
+ height: 1.5rem;
462
+ }
463
+
464
+ .info-card-meta {
465
+ font-size: 0.6rem;
466
+ font-weight: 900;
467
+ text-transform: uppercase;
468
+ letter-spacing: 0.18em;
469
+ color: var(--text-muted);
470
+ }
471
+
472
+ .info-card-value {
473
+ font-size: 1.15rem;
474
+ font-weight: 900;
475
+ color: var(--text-main);
476
+ margin: 0;
477
+ }
478
+
479
+ .info-card-desc {
480
+ font-size: 0.72rem;
481
+ font-weight: 600;
482
+ color: var(--text-muted);
483
+ margin: 0;
484
+ max-width: 14rem;
485
+ line-height: 1.4;
486
+ }
487
+
488
+ .info-card-temp-value {
489
+ font-size: 2rem;
490
+ font-weight: 900;
491
+ color: var(--text-main);
492
+ margin: 0;
493
+ letter-spacing: -0.03em;
494
+ }
495
+
496
+ .info-card-temp-label {
497
+ font-size: 0.6rem;
498
+ font-weight: 700;
499
+ text-transform: uppercase;
500
+ letter-spacing: 0.15em;
501
+ color: var(--text-muted);
502
+ }
503
+
504
+
505
+ .method-card {
506
+ background: #6366f1;
507
+ border-radius: 1rem;
508
+ padding: 1.5rem;
509
+ position: relative;
510
+ overflow: hidden;
511
+ box-shadow: 0 8px 20px rgba(99, 102, 241, 0.2);
512
+ }
513
+
514
+ .method-card-overlay {
515
+ position: absolute;
516
+ top: 0;
517
+ right: 0;
518
+ padding: 1.5rem;
519
+ opacity: 0.08;
520
+ pointer-events: none;
521
+ }
522
+
523
+ .method-overlay-icon {
524
+ width: 5rem;
525
+ height: 5rem;
526
+ color: #fff;
527
+ }
528
+
529
+ .method-card-content {
530
+ position: relative;
531
+ }
532
+
533
+ .method-card-header {
534
+ display: flex;
535
+ align-items: center;
536
+ gap: 0.75rem;
537
+ margin-bottom: 1rem;
538
+ }
539
+
540
+ .method-icon-wrap {
541
+ width: 2rem;
542
+ height: 2rem;
543
+ border-radius: 0.5rem;
544
+ background: rgba(255, 255, 255, 0.2);
545
+ display: flex;
546
+ align-items: center;
547
+ justify-content: center;
548
+ color: #fff;
549
+ }
550
+
551
+ .method-icon {
552
+ width: 1rem;
553
+ height: 1rem;
554
+ }
555
+
556
+ .method-meta {
557
+ font-size: 0.6rem;
558
+ font-weight: 900;
559
+ text-transform: uppercase;
560
+ letter-spacing: 0.18em;
561
+ color: rgba(255, 255, 255, 0.7);
562
+ }
563
+
564
+ .method-text {
565
+ font-size: 1rem;
566
+ font-weight: 700;
567
+ color: #fff;
568
+ line-height: 1.55;
569
+ letter-spacing: -0.01em;
570
+ }
571
+
572
+
573
+ .warning-card {
574
+ border-radius: 1rem;
575
+ background: rgba(239, 68, 68, 0.06);
576
+ border: 1px solid rgba(239, 68, 68, 0.2);
577
+ padding: 1.25rem;
578
+ animation: pulse-warning 4s ease infinite;
579
+ }
580
+
581
+ :global(.warning-card.hidden) {
582
+ display: none;
583
+ }
584
+
585
+ @keyframes pulse-warning {
586
+ 0%, 100% {
587
+ opacity: 1;
588
+ }
589
+ 50% {
590
+ opacity: 0.8;
591
+ }
592
+ }
593
+
594
+ .warning-card-inner {
595
+ display: flex;
596
+ gap: 1rem;
597
+ align-items: flex-start;
598
+ }
599
+
600
+ .warning-icon {
601
+ width: 1.5rem;
602
+ height: 1.5rem;
603
+ color: #ef4444;
604
+ flex-shrink: 0;
605
+ margin-top: 0.1rem;
606
+ }
607
+
608
+ .warning-label {
609
+ font-size: 0.6rem;
610
+ font-weight: 900;
611
+ text-transform: uppercase;
612
+ letter-spacing: 0.15em;
613
+ color: #ef4444;
614
+ margin: 0 0 0.25rem;
615
+ }
616
+
617
+ .warning-text {
618
+ font-size: 0.85rem;
619
+ font-weight: 700;
620
+ color: var(--text-main);
621
+ margin: 0;
622
+ line-height: 1.5;
623
+ }
624
+
625
+
626
+ .note-card {
627
+ background: var(--bg-page);
628
+ border: 1px solid var(--border-color);
629
+ border-radius: 1rem;
630
+ padding: 1.25rem;
631
+ }
632
+
633
+ .note-card-inner {
634
+ display: flex;
635
+ gap: 1rem;
636
+ align-items: flex-start;
637
+ }
638
+
639
+ .note-badge {
640
+ width: 1.5rem;
641
+ height: 1.5rem;
642
+ border-radius: 50%;
643
+ background: var(--text-main);
644
+ color: var(--bg-page);
645
+ display: flex;
646
+ align-items: center;
647
+ justify-content: center;
648
+ font-size: 0.65rem;
649
+ font-weight: 900;
650
+ flex-shrink: 0;
651
+ margin-top: 0.1rem;
652
+ }
653
+
654
+ .note-label {
655
+ font-size: 0.6rem;
656
+ font-weight: 900;
657
+ text-transform: uppercase;
658
+ letter-spacing: 0.15em;
659
+ color: var(--text-muted);
660
+ margin: 0 0 0.25rem;
661
+ }
662
+
663
+ .note-text {
664
+ font-size: 0.8rem;
665
+ font-weight: 600;
666
+ color: var(--text-muted);
667
+ margin: 0;
668
+ line-height: 1.5;
669
+ }
670
+
671
+
672
+ .empty-state {
673
+ padding: 3rem 2rem;
674
+ text-align: center;
675
+ background: var(--bg-page);
676
+ }
677
+
678
+ :global(.empty-state.hidden) {
679
+ display: none;
680
+ }
681
+
682
+ .empty-icon-wrap {
683
+ width: 5rem;
684
+ height: 5rem;
685
+ background: var(--bg-surface);
686
+ border-radius: 50%;
687
+ display: flex;
688
+ align-items: center;
689
+ justify-content: center;
690
+ margin: 0 auto 1.5rem;
691
+ border: 1px solid var(--border-color);
692
+ }
693
+
694
+ .empty-icon {
695
+ width: 2.5rem;
696
+ height: 2.5rem;
697
+ color: var(--border-color);
698
+ }
699
+
700
+ .empty-title {
701
+ font-size: 1.1rem;
702
+ font-weight: 900;
703
+ color: var(--text-main);
704
+ margin: 0 0 0.5rem;
705
+ letter-spacing: -0.02em;
706
+ }
707
+
708
+ .empty-desc {
709
+ font-size: 0.8rem;
710
+ font-weight: 500;
711
+ color: var(--text-muted);
712
+ margin: 0;
713
+ max-width: 18rem;
714
+ margin-inline: auto;
715
+ line-height: 1.5;
716
+ }
717
+ </style>