@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,14 @@
1
+ ---
2
+ ---
3
+ <ul>
4
+ <li>
5
+ <a href="https://www.craftyarncouncil.com/standards/hooks-and-needles" target="_blank" rel="noopener noreferrer">
6
+ Craft Yarn Council: Needle and Hook Size Chart
7
+ </a>
8
+ </li>
9
+ <li>
10
+ <a href="https://knowledge.bsigroup.com/products/textile-machinery-knitting-machines-number-of-needles-for-circular-knitting-machines-of-large-nominal-diameter" target="_blank" rel="noopener noreferrer">
11
+ British Standards Institution: Textiles - Marking of Knitting Needles
12
+ </a>
13
+ </li>
14
+ </ul>
@@ -0,0 +1,518 @@
1
+ ---
2
+ import type { NeedleConverterUI } from './ui';
3
+
4
+ interface Props {
5
+ ui?: Record<string, unknown>;
6
+ }
7
+
8
+ const { ui = {} } = Astro.props;
9
+ const needleUI = ui as NeedleConverterUI;
10
+ ---
11
+
12
+ <div class="needle-wrapper" id="master-needle">
13
+ <div class="needle-card">
14
+ <div class="card-sidebar">
15
+ <span class="label-tiny">{needleUI.labelSystem}</span>
16
+ <div class="mode-toggle" id="needle-mode">
17
+ <button class="toggle-btn active" data-type="knitting">{needleUI.modeKnitting}</button>
18
+ <button class="toggle-btn" data-type="crochet">{needleUI.modeCrochet}</button>
19
+ </div>
20
+
21
+ <span class="label-tiny">{needleUI.calibratorLabel}</span>
22
+ <div class="calibration-area">
23
+ <div class="hole-selector" id="hole-picker"></div>
24
+ </div>
25
+
26
+ <p class="calibrator-desc">{needleUI.calibratorDesc}</p>
27
+ </div>
28
+
29
+ <div class="card-main">
30
+ <div class="conversion-grid">
31
+ <div class="system-card">
32
+ <span class="val-large" id="res-mm">4.0</span>
33
+ <span class="val-tag">{needleUI.tagMetric}</span>
34
+ </div>
35
+ <div class="system-card">
36
+ <span class="val-large" id="res-us">6</span>
37
+ <span class="val-tag">{needleUI.tagUS}</span>
38
+ </div>
39
+ <div class="system-card">
40
+ <span class="val-large" id="res-uk">8</span>
41
+ <span class="val-tag">{needleUI.tagUK}</span>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="yarn-box">
46
+ <div class="yarn-icon">
47
+ <svg viewBox="0 0 24 24" fill="currentColor" width="28" height="28"><path d="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M16.6,16.89C16.14,17.35 15.39,17.35 14.93,16.89C14.47,16.43 14.47,15.68 14.93,15.22C15.39,14.76 16.14,14.76 16.6,15.22C17.06,15.68 17.06,16.43 16.6,16.89M12,14C10.9,14 10,13.1 10,12C10,10.9 10.9,10 12,10C13.1,10 14,10.9 14,12C14,13.1 13.1,14 12,14M9.07,16.89C8.61,17.35 7.86,17.35 7.4,16.89C6.94,16.43 6.94,15.68 7.4,15.22C7.86,14.76 8.61,14.76 9.07,15.22C9.53,15.68 9.53,16.43 9.07,16.89M12,7.5C14.48,7.5 16.5,9.52 16.5,12C16.5,14.48 14.48,16.5 12,16.5C9.52,16.5 7.5,14.48 7.5,12C7.5,9.52 9.52,7.5 12,7.5Z"/></svg>
48
+ </div>
49
+ <div class="yarn-content">
50
+ <span class="yarn-label">{needleUI.yarnLabel}</span>
51
+ <span class="yarn-name" id="yarn-desc">DK / Light Worsted</span>
52
+ <span class="yarn-wpi" id="yarn-wpi">11-13 WPI</span>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="table-section">
57
+ <span class="label-tiny">{needleUI.tableSectionLabel}</span>
58
+ <div class="needle-table-wrapper">
59
+ <table class="needle-table">
60
+ <thead>
61
+ <tr>
62
+ <th>{needleUI.tableMetric}</th>
63
+ <th>{needleUI.tableUS}</th>
64
+ <th>{needleUI.tableUK}</th>
65
+ <th>{needleUI.tableYarn}</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody id="needle-body"></tbody>
69
+ </table>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <script define:vars={{ ui }} is:inline>
77
+ window.__needleConverterUI = ui;
78
+ </script>
79
+
80
+ <script>
81
+ import type { NeedleConverterUI } from './ui';
82
+ import { NEEDLE_DATA, DEFAULT_IDX } from './logic';
83
+
84
+ declare global {
85
+ interface Window { __needleConverterUI: NeedleConverterUI; }
86
+ }
87
+
88
+ let currentIdx = DEFAULT_IDX;
89
+ let currentType = 'knitting';
90
+
91
+ function setText(id: string, v: string): void {
92
+ const el = document.getElementById(id);
93
+ if (el) el.innerText = v;
94
+ }
95
+
96
+ function buildHoleEl(mm: number, idx: number): HTMLDivElement {
97
+ const hole = document.createElement('div');
98
+ hole.className = `needle-hole${idx === currentIdx ? ' active' : ''}`;
99
+ hole.style.setProperty('--size-px', `${mm * 4}px`);
100
+ const lbl = document.createElement('span');
101
+ lbl.className = 'hole-label';
102
+ lbl.textContent = String(mm);
103
+ hole.appendChild(lbl);
104
+ hole.addEventListener('click', () => { currentIdx = idx; run(); });
105
+ return hole;
106
+ }
107
+
108
+ function renderHoles(): void {
109
+ const picker = document.getElementById('hole-picker');
110
+ if (!picker) return;
111
+ picker.innerHTML = '';
112
+ NEEDLE_DATA.forEach((item, idx) => picker.appendChild(buildHoleEl(item.mm, idx)));
113
+ const active = picker.children[currentIdx] as HTMLElement | undefined;
114
+ if (active) {
115
+ picker.scrollTo({ left: active.offsetLeft - picker.offsetWidth / 2 + active.offsetWidth / 2, behavior: 'smooth' });
116
+ }
117
+ }
118
+
119
+ function buildRowEl(idx: number): HTMLTableRowElement {
120
+ const item = NEEDLE_DATA[idx];
121
+ const tr = document.createElement('tr');
122
+ tr.style.cursor = 'pointer';
123
+ if (idx === currentIdx) tr.classList.add('active');
124
+ const us = currentType === 'knitting' ? item.usK : item.usC;
125
+ tr.innerHTML = `<td style="font-weight:700">${item.mm.toFixed(1)}</td><td>${us}</td><td>${item.uk}</td>` +
126
+ `<td style="font-size:11px;text-transform:uppercase;letter-spacing:0.05em">${item.yarn}</td>`;
127
+ tr.addEventListener('click', () => { currentIdx = idx; run(); });
128
+ return tr;
129
+ }
130
+
131
+ function renderTable(): void {
132
+ const body = document.getElementById('needle-body');
133
+ if (!body) return;
134
+ body.innerHTML = '';
135
+ NEEDLE_DATA.forEach((_, idx) => body.appendChild(buildRowEl(idx)));
136
+ }
137
+
138
+ function updateDisplay(): void {
139
+ const item = NEEDLE_DATA[currentIdx];
140
+ const ui = window.__needleConverterUI;
141
+ setText('res-mm', item.mm.toFixed(1));
142
+ setText('res-us', currentType === 'knitting' ? item.usK : item.usC);
143
+ setText('res-uk', item.uk);
144
+ setText('yarn-desc', item.yarn);
145
+ setText('yarn-wpi', ui.wpiFormat.replace('{wpi}', item.wpi));
146
+ }
147
+
148
+ function run(): void {
149
+ updateDisplay();
150
+ renderHoles();
151
+ renderTable();
152
+ }
153
+
154
+ function attachListeners(): void {
155
+ const btns = document.querySelectorAll<HTMLElement>('#needle-mode button');
156
+ btns.forEach((btn) => {
157
+ btn.addEventListener('click', () => {
158
+ btns.forEach((b) => b.classList.remove('active'));
159
+ btn.classList.add('active');
160
+ currentType = btn.dataset.type || 'knitting';
161
+ run();
162
+ });
163
+ });
164
+ }
165
+
166
+ function init(): void {
167
+ attachListeners();
168
+ run();
169
+ }
170
+
171
+ document.addEventListener('astro:page-load', init);
172
+ init();
173
+ </script>
174
+
175
+ <style>
176
+ .needle-wrapper {
177
+ padding: 1.5rem 0;
178
+ width: 100%;
179
+ }
180
+
181
+ .needle-card {
182
+ --nc-p: #3b82f6;
183
+ --nc-s: #0ea5e9;
184
+
185
+ background: var(--bg-surface);
186
+ color: var(--text-main);
187
+ width: calc(100% - 24px);
188
+ max-width: 1100px;
189
+ margin: 0 auto;
190
+ border-radius: 24px;
191
+ border: 1px solid var(--border-color);
192
+ display: flex;
193
+ flex-direction: column;
194
+ overflow: hidden;
195
+ }
196
+
197
+ @media (min-width: 1024px) {
198
+ .needle-card {
199
+ flex-direction: row;
200
+ }
201
+ }
202
+
203
+ .card-sidebar {
204
+ flex: 0 0 auto;
205
+ padding: 24px;
206
+ width: 100%;
207
+ background: var(--bg-surface);
208
+ }
209
+
210
+ @media (min-width: 1024px) {
211
+ .card-sidebar {
212
+ width: 400px;
213
+ padding: 40px;
214
+ border-right: 1px solid var(--border-color);
215
+ }
216
+ }
217
+
218
+ .card-main {
219
+ flex: 1 1 auto;
220
+ padding: 24px;
221
+ background: var(--bg-surface);
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 32px;
225
+ min-width: 0;
226
+ }
227
+
228
+ @media (min-width: 1024px) {
229
+ .card-main {
230
+ padding: 40px;
231
+ }
232
+ }
233
+
234
+ .label-tiny {
235
+ font-size: 10px;
236
+ font-weight: 700;
237
+ text-transform: uppercase;
238
+ letter-spacing: 0.12em;
239
+ color: var(--text-muted);
240
+ margin-bottom: 12px;
241
+ display: block;
242
+ }
243
+
244
+ .mode-toggle {
245
+ display: flex;
246
+ background: var(--bg-muted);
247
+ padding: 4px;
248
+ border-radius: 12px;
249
+ margin-bottom: 32px;
250
+ }
251
+
252
+ .toggle-btn {
253
+ flex: 1;
254
+ border: none;
255
+ background: transparent;
256
+ padding: 12px;
257
+ font-size: 10px;
258
+ font-weight: 800;
259
+ text-transform: uppercase;
260
+ letter-spacing: 0.05em;
261
+ color: var(--text-muted);
262
+ cursor: pointer;
263
+ border-radius: 8px;
264
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
265
+ }
266
+
267
+ .toggle-btn.active {
268
+ background: var(--bg-surface);
269
+ color: var(--text-main);
270
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
271
+ }
272
+
273
+ .calibration-area {
274
+ background: var(--bg-muted);
275
+ border-radius: 20px;
276
+ padding: 24px;
277
+ border: 1px solid var(--border-color);
278
+ margin-bottom: 24px;
279
+ position: relative;
280
+ overflow: hidden;
281
+ }
282
+
283
+ .hole-selector {
284
+ display: flex;
285
+ align-items: center;
286
+ gap: 24px;
287
+ overflow-x: auto;
288
+ padding: 30px 10px;
289
+ scrollbar-width: none;
290
+ scroll-snap-type: x mandatory;
291
+ }
292
+
293
+ .hole-selector::-webkit-scrollbar {
294
+ display: none;
295
+ }
296
+
297
+ :global(.needle-hole) {
298
+ flex: 0 0 auto;
299
+ width: 64px;
300
+ height: 64px;
301
+ background: var(--bg-surface);
302
+ border: 1.5px solid var(--border-color);
303
+ border-radius: 50%;
304
+ display: flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+ cursor: pointer;
308
+ position: relative;
309
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
310
+ scroll-snap-align: center;
311
+ }
312
+
313
+ :global(.needle-hole::after) {
314
+ content: '';
315
+ position: absolute;
316
+ width: var(--size-px);
317
+ height: var(--size-px);
318
+ background: var(--text-main);
319
+ border-radius: 50%;
320
+ opacity: 0.1;
321
+ transition: transform 0.3s ease;
322
+ }
323
+
324
+ :global(.needle-hole.active) {
325
+ border-color: var(--nc-p);
326
+ background: rgba(59, 130, 246, 0.08);
327
+ transform: scale(1.15);
328
+ box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.15);
329
+ }
330
+
331
+ :global(.needle-hole.active::after) {
332
+ opacity: 1;
333
+ background: var(--nc-p);
334
+ transform: scale(1.05);
335
+ }
336
+
337
+ :global(.hole-label) {
338
+ position: absolute;
339
+ bottom: -28px;
340
+ font-size: 11px;
341
+ font-weight: 800;
342
+ color: var(--text-muted);
343
+ transition: color 0.2s;
344
+ }
345
+
346
+ :global(.needle-hole.active .hole-label) {
347
+ color: var(--nc-p);
348
+ }
349
+
350
+ .calibrator-desc {
351
+ font-size: 12px;
352
+ color: var(--text-muted);
353
+ font-weight: 400;
354
+ line-height: 1.6;
355
+ }
356
+
357
+ .conversion-grid {
358
+ display: grid;
359
+ grid-template-columns: repeat(3, 1fr);
360
+ gap: 16px;
361
+ }
362
+
363
+ .system-card {
364
+ background: var(--bg-muted);
365
+ border-radius: 20px;
366
+ padding: 24px 16px;
367
+ text-align: center;
368
+ border: 1px solid var(--border-color);
369
+ transition: border-color 0.2s;
370
+ }
371
+
372
+ .val-large {
373
+ display: block;
374
+ font-size: 28px;
375
+ font-weight: 800;
376
+ color: var(--text-main);
377
+ line-height: 1;
378
+ margin-bottom: 8px;
379
+ letter-spacing: -0.02em;
380
+ }
381
+
382
+ .val-tag {
383
+ font-size: 10px;
384
+ font-weight: 700;
385
+ text-transform: uppercase;
386
+ color: var(--text-muted);
387
+ letter-spacing: 0.1em;
388
+ }
389
+
390
+ .yarn-box {
391
+ background: #f0f9ff;
392
+ border-left: 4px solid var(--nc-s);
393
+ padding: 28px;
394
+ border-radius: 16px;
395
+ display: flex;
396
+ gap: 20px;
397
+ align-items: center;
398
+ }
399
+
400
+ .theme-dark .yarn-box {
401
+ background: #082f49;
402
+ }
403
+
404
+ .yarn-icon {
405
+ width: 56px;
406
+ height: 56px;
407
+ background: var(--bg-surface);
408
+ border-radius: 16px;
409
+ display: flex;
410
+ align-items: center;
411
+ justify-content: center;
412
+ color: var(--nc-s);
413
+ flex-shrink: 0;
414
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
415
+ }
416
+
417
+ .yarn-content {
418
+ flex: 1;
419
+ }
420
+
421
+ .yarn-label {
422
+ font-size: 9px;
423
+ font-weight: 800;
424
+ color: #0284c7;
425
+ text-transform: uppercase;
426
+ letter-spacing: 0.1em;
427
+ display: block;
428
+ margin-bottom: 4px;
429
+ }
430
+
431
+ .theme-dark .yarn-label {
432
+ color: #7dd3fc;
433
+ }
434
+
435
+ .yarn-name {
436
+ display: block;
437
+ font-size: 17px;
438
+ font-weight: 800;
439
+ color: #075985;
440
+ }
441
+
442
+ .theme-dark .yarn-name {
443
+ color: #bae6fd;
444
+ }
445
+
446
+ .yarn-wpi {
447
+ display: block;
448
+ font-size: 12px;
449
+ color: #0369a1;
450
+ font-weight: 500;
451
+ margin-top: 4px;
452
+ }
453
+
454
+ .theme-dark .yarn-wpi {
455
+ color: #7dd3fc;
456
+ }
457
+
458
+ .table-section {
459
+ border-top: 1px solid var(--border-color);
460
+ padding-top: 40px;
461
+ }
462
+
463
+ .needle-table-wrapper {
464
+ overflow-x: auto;
465
+ border-radius: 16px;
466
+ border: 1px solid var(--border-color);
467
+ }
468
+
469
+ .needle-table {
470
+ width: 100%;
471
+ border-collapse: collapse;
472
+ font-size: 14px;
473
+ text-align: center;
474
+ }
475
+
476
+ .needle-table th {
477
+ background: var(--bg-muted);
478
+ padding: 16px;
479
+ font-size: 10px;
480
+ font-weight: 800;
481
+ text-transform: uppercase;
482
+ color: var(--text-muted);
483
+ border-bottom: 1px solid var(--border-color);
484
+ }
485
+
486
+ .needle-table :global(td) {
487
+ padding: 16px;
488
+ border-bottom: 1px solid var(--border-color);
489
+ color: var(--text-muted);
490
+ transition: all 0.2s;
491
+ }
492
+
493
+ .needle-table :global(tbody tr:nth-child(even) td) {
494
+ background: rgba(0, 0, 0, 0.02);
495
+ }
496
+
497
+ .theme-dark .needle-table :global(tbody tr:nth-child(even) td) {
498
+ background: rgba(255, 255, 255, 0.02);
499
+ }
500
+
501
+ .needle-table :global(tr:hover td) {
502
+ background: var(--bg-muted);
503
+ }
504
+
505
+ .needle-table :global(tr.active td) {
506
+ background: rgba(59, 130, 246, 0.05);
507
+ color: var(--nc-p);
508
+ font-weight: 700;
509
+ }
510
+
511
+ .needle-table :global(tr.active td:first-child) {
512
+ box-shadow: inset 4px 0 0 var(--nc-p);
513
+ }
514
+
515
+ .theme-dark .needle-table :global(tr.active td) {
516
+ background: rgba(59, 130, 246, 0.1);
517
+ }
518
+ </style>