@jjlmoya/utils-diy 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 (135) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +110 -0
  3. package/src/category/i18n/es.ts +110 -0
  4. package/src/category/i18n/fr.ts +101 -0
  5. package/src/category/index.ts +25 -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 +57 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -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/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +22 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/balusterCalculator/bibliography.astro +14 -0
  25. package/src/tool/balusterCalculator/component.astro +605 -0
  26. package/src/tool/balusterCalculator/i18n/en.ts +264 -0
  27. package/src/tool/balusterCalculator/i18n/es.ts +264 -0
  28. package/src/tool/balusterCalculator/i18n/fr.ts +264 -0
  29. package/src/tool/balusterCalculator/index.ts +33 -0
  30. package/src/tool/balusterCalculator/seo.astro +15 -0
  31. package/src/tool/balusterCalculator/ui.ts +15 -0
  32. package/src/tool/clayCalculator/bibliography.astro +14 -0
  33. package/src/tool/clayCalculator/component.astro +731 -0
  34. package/src/tool/clayCalculator/i18n/en.ts +183 -0
  35. package/src/tool/clayCalculator/i18n/es.ts +183 -0
  36. package/src/tool/clayCalculator/i18n/fr.ts +183 -0
  37. package/src/tool/clayCalculator/index.ts +33 -0
  38. package/src/tool/clayCalculator/seo.astro +15 -0
  39. package/src/tool/clayCalculator/ui.ts +19 -0
  40. package/src/tool/concreteCalculator/bibliography.astro +13 -0
  41. package/src/tool/concreteCalculator/component.astro +1089 -0
  42. package/src/tool/concreteCalculator/i18n/en.ts +201 -0
  43. package/src/tool/concreteCalculator/i18n/es.ts +201 -0
  44. package/src/tool/concreteCalculator/i18n/fr.ts +201 -0
  45. package/src/tool/concreteCalculator/index.ts +28 -0
  46. package/src/tool/concreteCalculator/seo.astro +14 -0
  47. package/src/tool/concreteCalculator/ui.ts +35 -0
  48. package/src/tool/cutOptimizer/bibliography.astro +13 -0
  49. package/src/tool/cutOptimizer/component.astro +825 -0
  50. package/src/tool/cutOptimizer/i18n/en.ts +178 -0
  51. package/src/tool/cutOptimizer/i18n/es.ts +178 -0
  52. package/src/tool/cutOptimizer/i18n/fr.ts +178 -0
  53. package/src/tool/cutOptimizer/index.ts +28 -0
  54. package/src/tool/cutOptimizer/optimizer.ts +199 -0
  55. package/src/tool/cutOptimizer/seo.astro +14 -0
  56. package/src/tool/cutOptimizer/ui.ts +25 -0
  57. package/src/tool/drillCalculator/bibliography.astro +13 -0
  58. package/src/tool/drillCalculator/component.astro +1210 -0
  59. package/src/tool/drillCalculator/engine.ts +63 -0
  60. package/src/tool/drillCalculator/i18n/en.ts +137 -0
  61. package/src/tool/drillCalculator/i18n/es.ts +137 -0
  62. package/src/tool/drillCalculator/i18n/fr.ts +137 -0
  63. package/src/tool/drillCalculator/index.ts +28 -0
  64. package/src/tool/drillCalculator/seo.astro +14 -0
  65. package/src/tool/drillCalculator/ui.ts +41 -0
  66. package/src/tool/drillSharpener/bibliography.astro +13 -0
  67. package/src/tool/drillSharpener/component.astro +712 -0
  68. package/src/tool/drillSharpener/engine.ts +42 -0
  69. package/src/tool/drillSharpener/i18n/en.ts +121 -0
  70. package/src/tool/drillSharpener/i18n/es.ts +121 -0
  71. package/src/tool/drillSharpener/i18n/fr.ts +121 -0
  72. package/src/tool/drillSharpener/index.ts +31 -0
  73. package/src/tool/drillSharpener/seo.astro +14 -0
  74. package/src/tool/drillSharpener/ui.ts +53 -0
  75. package/src/tool/epoxyCalculator/bibliography.astro +14 -0
  76. package/src/tool/epoxyCalculator/component.astro +888 -0
  77. package/src/tool/epoxyCalculator/i18n/en.ts +195 -0
  78. package/src/tool/epoxyCalculator/i18n/es.ts +195 -0
  79. package/src/tool/epoxyCalculator/i18n/fr.ts +195 -0
  80. package/src/tool/epoxyCalculator/index.ts +33 -0
  81. package/src/tool/epoxyCalculator/seo.astro +15 -0
  82. package/src/tool/epoxyCalculator/ui.ts +22 -0
  83. package/src/tool/furnitureFit/bibliography.astro +13 -0
  84. package/src/tool/furnitureFit/component.astro +552 -0
  85. package/src/tool/furnitureFit/engine.ts +51 -0
  86. package/src/tool/furnitureFit/i18n/en.ts +152 -0
  87. package/src/tool/furnitureFit/i18n/es.ts +152 -0
  88. package/src/tool/furnitureFit/i18n/fr.ts +152 -0
  89. package/src/tool/furnitureFit/index.ts +28 -0
  90. package/src/tool/furnitureFit/seo.astro +14 -0
  91. package/src/tool/furnitureFit/ui.ts +21 -0
  92. package/src/tool/mortarCalculator/bibliography.astro +13 -0
  93. package/src/tool/mortarCalculator/component.astro +1139 -0
  94. package/src/tool/mortarCalculator/i18n/en.ts +219 -0
  95. package/src/tool/mortarCalculator/i18n/es.ts +205 -0
  96. package/src/tool/mortarCalculator/i18n/fr.ts +220 -0
  97. package/src/tool/mortarCalculator/index.ts +24 -0
  98. package/src/tool/mortarCalculator/seo.astro +14 -0
  99. package/src/tool/mortarCalculator/ui.ts +37 -0
  100. package/src/tool/passepartoutCalculator/bibliography.astro +13 -0
  101. package/src/tool/passepartoutCalculator/component.astro +741 -0
  102. package/src/tool/passepartoutCalculator/i18n/en.ts +178 -0
  103. package/src/tool/passepartoutCalculator/i18n/es.ts +178 -0
  104. package/src/tool/passepartoutCalculator/i18n/fr.ts +178 -0
  105. package/src/tool/passepartoutCalculator/index.ts +28 -0
  106. package/src/tool/passepartoutCalculator/seo.astro +14 -0
  107. package/src/tool/passepartoutCalculator/ui.ts +17 -0
  108. package/src/tool/stairCalculator/bibliography.astro +52 -0
  109. package/src/tool/stairCalculator/component.astro +766 -0
  110. package/src/tool/stairCalculator/engine.ts +128 -0
  111. package/src/tool/stairCalculator/i18n/en.ts +149 -0
  112. package/src/tool/stairCalculator/i18n/es.ts +149 -0
  113. package/src/tool/stairCalculator/i18n/fr.ts +149 -0
  114. package/src/tool/stairCalculator/index.ts +31 -0
  115. package/src/tool/stairCalculator/seo.astro +211 -0
  116. package/src/tool/stairCalculator/ui.ts +109 -0
  117. package/src/tool/thermalExpansionCalculator/bibliography.astro +13 -0
  118. package/src/tool/thermalExpansionCalculator/component.astro +771 -0
  119. package/src/tool/thermalExpansionCalculator/engine.ts +23 -0
  120. package/src/tool/thermalExpansionCalculator/i18n/en.ts +174 -0
  121. package/src/tool/thermalExpansionCalculator/i18n/es.ts +174 -0
  122. package/src/tool/thermalExpansionCalculator/i18n/fr.ts +174 -0
  123. package/src/tool/thermalExpansionCalculator/index.ts +28 -0
  124. package/src/tool/thermalExpansionCalculator/seo.astro +14 -0
  125. package/src/tool/thermalExpansionCalculator/ui.ts +41 -0
  126. package/src/tool/voltageDropCalculator/bibliography.astro +13 -0
  127. package/src/tool/voltageDropCalculator/component.astro +1022 -0
  128. package/src/tool/voltageDropCalculator/i18n/en.ts +155 -0
  129. package/src/tool/voltageDropCalculator/i18n/es.ts +155 -0
  130. package/src/tool/voltageDropCalculator/i18n/fr.ts +148 -0
  131. package/src/tool/voltageDropCalculator/index.ts +28 -0
  132. package/src/tool/voltageDropCalculator/seo.astro +14 -0
  133. package/src/tool/voltageDropCalculator/ui.ts +22 -0
  134. package/src/tools.ts +16 -0
  135. package/src/types.ts +72 -0
@@ -0,0 +1,1210 @@
1
+ ---
2
+ import type { DrillCalculatorUI } from './ui';
3
+ interface Props { ui?: DrillCalculatorUI; }
4
+ const { ui } = Astro.props;
5
+ const t = (ui ?? {}) as DrillCalculatorUI;
6
+ ---
7
+
8
+ <div class="dc-root" data-dc-root
9
+ data-coolsteel={t.coolSteel}
10
+ data-coolinox={t.coolInox}
11
+ data-coolalu={t.coolAlu}
12
+ data-coolbrass={t.coolBrass}
13
+ data-coolcastiron={t.coolCastIron}
14
+ data-coolwood={t.coolWood}
15
+ data-coolplastic={t.coolPlastic}>
16
+ <div class="dc-card">
17
+ <div class="dc-grid">
18
+ <div class="dc-sidebar">
19
+ <div class="dc-field">
20
+ <label class="dc-field-label">{t.labelMaterial}</label>
21
+ <div class="dc-mat-list" id="dc-mat-list">
22
+ <div class="dc-mat-chip active" data-id="steel" data-fz="0.1">
23
+ <div class="dc-mat-icon">
24
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M2 19.63L13.43 8.2l-.71-.7l1.42-1.43L12 3.89c1.2-1.19 3.09-1.19 4.27 0l3.6 3.61l-1.42 1.41h2.84l.71.71l-3.55 3.59l-.71-.71V9.62l-1.47 1.42l-.71-.71L4.13 21.76z"></path></svg>
25
+ </div>
26
+ <span>{t.matSteel}</span>
27
+ </div>
28
+ <div class="dc-mat-chip" data-id="inox" data-fz="0.05">
29
+ <div class="dc-mat-icon">
30
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12c5.16-1.26 9-6.45 9-12V5z"></path></svg>
31
+ </div>
32
+ <span>{t.matInox}</span>
33
+ </div>
34
+ <div class="dc-mat-chip" data-id="alu" data-fz="0.15">
35
+ <div class="dc-mat-icon">
36
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M23.36 2.26c.59.59.59 1.54 0 2.12l-3.88 3.89l2.12 9.19l-1.41 1.42l-3.88-7.43L9.6 17l.36 2.47l-1.07 1.06l-1.76-3.18l-3.19-1.77L5 14.5l2.5.37L11.37 11L3.94 7.09l1.42-1.41l9.19 2.12l3.89-3.89c.56-.58 1.56-.58 2.12 0"></path></svg>
37
+ </div>
38
+ <span>{t.matAlu}</span>
39
+ </div>
40
+ <div class="dc-mat-chip" data-id="brass" data-fz="0.1">
41
+ <div class="dc-mat-icon">
42
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2"></path></svg>
43
+ </div>
44
+ <span>{t.matBrass}</span>
45
+ </div>
46
+ <div class="dc-mat-chip" data-id="castiron" data-fz="0.2">
47
+ <div class="dc-mat-icon">
48
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18s-.41-.06-.57-.18l-7.9-4.44A.99.99 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18s.41.06.57.18l7.9 4.44c.32.17.53.5.53.88zM12 4.15L6.04 7.5L12 10.85l5.96-3.35zM5 15.91l6 3.38v-6.71L5 9.21zm14 0v-6.7l-6 3.37v6.71z"></path></svg>
49
+ </div>
50
+ <span>{t.matCastIron}</span>
51
+ </div>
52
+ <div class="dc-mat-chip" data-id="wood" data-fz="0.3">
53
+ <div class="dc-mat-icon">
54
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M10 21v-3H3l5-5H5l5-5H7l5-5l5 5h-3l5 5h-3l5 5h-7v3z"></path></svg>
55
+ </div>
56
+ <span>{t.matWood}</span>
57
+ </div>
58
+ <div class="dc-mat-chip" data-id="plastic" data-fz="0.15">
59
+ <div class="dc-mat-icon">
60
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M6 22a3 3 0 0 1-3-3c0-.6.18-1.16.5-1.63L9 7.81V6a1 1 0 0 1-1-1V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1v1.81l5.5 9.56c.32.47.5 1.03.5 1.63a3 3 0 0 1-3 3zm-1-3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1c0-.21-.07-.41-.18-.57l-2.29-3.96L14 17l-5.07-5.07l-3.75 6.5c-.11.16-.18.36-.18.57m8-9a1 1 0 0 0-1 1a1 1 0 0 0 1 1a1 1 0 0 0 1-1a1 1 0 0 0-1-1"></path></svg>
61
+ </div>
62
+ <span>{t.matPlastic}</span>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <div class="dc-field">
68
+ <label class="dc-field-label">{t.labelTool}</label>
69
+ <div class="dc-toggle-group">
70
+ <button class="dc-toggle-btn active" data-type="hss">{t.typeHss}</button>
71
+ <button class="dc-toggle-btn" data-type="carbide">{t.typeWidia}</button>
72
+ </div>
73
+ </div>
74
+
75
+ <div class="dc-field">
76
+ <div class="dc-diameter-header">
77
+ <label class="dc-field-label">{t.labelDiameter}</label>
78
+ <div class="dc-unit-toggle" id="dc-unit-toggle">
79
+ <button class="dc-unit-btn active" data-unit="mm">{t.unitMm}</button>
80
+ <button class="dc-unit-btn" data-unit="in">{t.unitInch}</button>
81
+ </div>
82
+ </div>
83
+ <div class="dc-diameter-display" id="dc-diam-display">10.0 mm</div>
84
+ <div class="dc-slider-box">
85
+ <input type="range" id="dc-diam-slider" class="dc-slider" min="0.5" max="50" step="0.5" value="10" />
86
+ </div>
87
+ </div>
88
+
89
+ <div class="dc-mode-badge">
90
+ <span class="dc-mode-label">{t.modeColumnDrill}</span>
91
+ <div class="dc-toggle-switch" id="dc-limit-toggle"></div>
92
+ </div>
93
+
94
+ <div class="dc-advanced">
95
+ <button class="dc-advanced-trigger" id="dc-adv-trigger">
96
+ <span>{t.labelAdvanced}</span>
97
+ <svg id="dc-adv-chevron" class="dc-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
98
+ <polyline points="6 9 12 15 18 9"></polyline>
99
+ </svg>
100
+ </button>
101
+ <div class="dc-advanced-body" id="dc-adv-body">
102
+ <div class="dc-adv-grid">
103
+ <div>
104
+ <label class="dc-field-label">{t.labelLips}</label>
105
+ <input type="number" id="dc-lips" class="dc-input" value="2" min="1" max="12" />
106
+ </div>
107
+ <div>
108
+ <label class="dc-field-label">{t.labelFz}</label>
109
+ <input type="number" id="dc-fz" class="dc-input" placeholder="0.1" step="0.01" value="" />
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="dc-main">
117
+ <div class="dc-hero">
118
+ <span class="dc-hero-label">{t.resultRpm}</span>
119
+ <div class="dc-rpm-number" id="dc-rpm-out">0</div>
120
+ <span class="dc-hero-unit">{t.rpmUnit}</span>
121
+ <div class="dc-rotation-visual" id="dc-rot-visual"></div>
122
+ <div class="dc-status-row">
123
+ <div id="dc-status-badge" class="hidden dc-badge dc-badge-warning">
124
+ <svg class="dc-badge-icon" viewBox="0 0 24 24" fill="currentColor">
125
+ <circle cx="12" cy="12" r="10"/>
126
+ <path d="M12 7v5M12 16h.01" stroke="white" stroke-width="2" fill="none"/>
127
+ </svg>
128
+ <span>{t.statusLimitActive}</span>
129
+ </div>
130
+ <div id="dc-warning-badge" class="hidden dc-badge dc-badge-alert">
131
+ <svg class="dc-badge-icon" viewBox="0 0 24 24" fill="currentColor">
132
+ <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
133
+ </svg>
134
+ <span>{t.statusOutOfRange}</span>
135
+ </div>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="dc-tiles">
140
+ <div class="dc-tile">
141
+ <div class="dc-tile-icon">
142
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M13.5 4A1.5 1.5 0 0 0 12 5.5A1.5 1.5 0 0 0 13.5 7A1.5 1.5 0 0 0 15 5.5A1.5 1.5 0 0 0 13.5 4m-.36 4.77c-1.19.1-4.44 2.69-4.44 2.69c-.2.15-.14.14.02.42c.16.27.14.29.33.16c.2-.13.53-.34 1.08-.68c2.12-1.36.34 1.78-.57 7.07c-.36 2.62 2 1.27 2.61.87c.6-.39 2.21-1.5 2.37-1.61c.22-.15.06-.27-.11-.52c-.12-.17-.24-.05-.24-.05c-.65.43-1.84 1.33-2 .76c-.19-.57 1.03-4.48 1.7-7.17c.11-.64.41-2.04-.75-1.94"></path></svg>
143
+ </div>
144
+ <div class="dc-tile-content">
145
+ <span class="dc-tile-label">{t.resultCooling}</span>
146
+ <span class="dc-tile-value" id="dc-cool-txt">—</span>
147
+ </div>
148
+ </div>
149
+ <div class="dc-tile">
150
+ <div class="dc-tile-icon">
151
+ <svg width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg>
152
+ </div>
153
+ <div class="dc-tile-content">
154
+ <span class="dc-tile-label">{t.resultFeed}</span>
155
+ <span class="dc-tile-value" id="dc-feed-txt">— mm/min</span>
156
+ </div>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="dc-machine-card">
161
+ <span class="dc-field-label">{t.labelMachine}</span>
162
+ <div class="dc-presets">
163
+ <button class="dc-preset-btn" data-preset="standard">{t.presetStandard}</button>
164
+ <button class="dc-preset-btn" data-preset="cnc">{t.presetCnc}</button>
165
+ </div>
166
+ <div class="dc-manual-input">
167
+ <input type="number" id="dc-speed-in" class="dc-input" placeholder={t.addRpmManual} />
168
+ <button id="dc-add-btn" class="dc-add-btn">+</button>
169
+ </div>
170
+ <div class="dc-chips-area" id="dc-chips-area"></div>
171
+ <div id="dc-closest-box" class="hidden dc-match-box">
172
+ <div class="dc-match-header">
173
+ <span class="dc-match-label">{t.recommendedLabel}</span>
174
+ </div>
175
+ <div class="dc-match-value" id="dc-closest-val">—</div>
176
+ <p class="dc-match-hint">{t.recommendedHint}</p>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <script>
185
+ import { calculateDrill, findClosestSpeed, getSpeedDifference, MATERIALS, type DrillContext } from './engine';
186
+
187
+ interface DcState { material: string; toolType: 'hss' | 'carbide'; diameter: number; unit: 'mm' | 'in'; isLimited: boolean; machineSpeeds: number[]; }
188
+
189
+ const materialCoolMap: Record<string, string> = {
190
+ steel: 'coolSteel', inox: 'coolInox', alu: 'coolAlu', brass: 'coolBrass',
191
+ castiron: 'coolCastIron', wood: 'coolWood', plastic: 'coolPlastic'
192
+ };
193
+
194
+ const presets: Record<string, number[]> = {
195
+ standard: [120, 200, 450, 600, 800, 1100, 1500, 2000, 2500, 3000],
196
+ cnc: [1000, 2000, 4000, 6000, 8000, 12000, 15000, 20000, 24000]
197
+ };
198
+
199
+ const state: DcState = { material: 'steel', toolType: 'hss', diameter: 10, unit: 'mm', isLimited: false, machineSpeeds: [] };
200
+
201
+ function getI18n(key: string): string {
202
+ const root = document.querySelector('[data-dc-root]');
203
+ return (root?.getAttribute(`data-${key}`) as string) || '';
204
+ }
205
+
206
+ function getMaterial() { return MATERIALS.find(m => m.id === state.material)!; }
207
+
208
+ function dcUpdateRpmDisplay(displayRpm: number, idealRpm: number) {
209
+ const rpmEl = document.getElementById('dc-rpm-out');
210
+ if (rpmEl) {
211
+ rpmEl.textContent = displayRpm.toString();
212
+ rpmEl.className = 'dc-rpm-number';
213
+ if (state.isLimited && displayRpm === Math.max(...state.machineSpeeds || [2500])) rpmEl.classList.add('limited');
214
+ else if (idealRpm < 500) rpmEl.classList.add('slow');
215
+ else rpmEl.classList.add('optimal');
216
+ }
217
+ }
218
+
219
+ function dcUpdateVisuals(displayRpm: number) {
220
+ const rotEl = document.getElementById('dc-rot-visual');
221
+ if (rotEl) {
222
+ const dur = Math.max(0.04, 60 / Math.max(1, displayRpm));
223
+ rotEl.style.animationDuration = `${dur}s`;
224
+ rotEl.style.animationPlayState = displayRpm > 0 ? 'running' : 'paused';
225
+ }
226
+ }
227
+
228
+ function dcUpdateClosestSpeed(displayRpm: number) {
229
+ if (state.machineSpeeds.length === 0) {
230
+ document.getElementById('dc-closest-box')?.classList.add('hidden');
231
+ return;
232
+ }
233
+ const closest = findClosestSpeed(displayRpm, state.machineSpeeds);
234
+ const diff = getSpeedDifference(closest, displayRpm);
235
+ const closestVal = document.getElementById('dc-closest-val');
236
+ if (closestVal) {
237
+ closestVal.textContent = `${closest} RPM`;
238
+ closestVal.style.color = diff > 20 ? 'var(--dc-danger, #ef4444)' : 'var(--dc-accent, #3b82f6)';
239
+ }
240
+ document.getElementById('dc-closest-box')?.classList.remove('hidden');
241
+ }
242
+
243
+ function dcGetDiameter(): number {
244
+ let diam = state.diameter;
245
+ if (state.unit === 'in') diam = diam * 25.4;
246
+ return diam;
247
+ }
248
+
249
+ function dcUpdateStatus(warning: string) {
250
+ document.getElementById('dc-status-badge')?.classList.toggle('hidden', warning !== 'limited');
251
+ document.getElementById('dc-warning-badge')?.classList.toggle('hidden', warning !== 'outofrange');
252
+ }
253
+
254
+ function dcUpdateMaterialInfo() {
255
+ const coolEl = document.getElementById('dc-cool-txt');
256
+ if (coolEl) {
257
+ const coolKey = materialCoolMap[state.material] || 'coolSteel';
258
+ coolEl.textContent = getI18n(coolKey);
259
+ }
260
+ }
261
+
262
+ function dcCalculate() {
263
+ const diam = dcGetDiameter();
264
+ const ctx: DrillContext = {
265
+ lips: parseInt((document.getElementById('dc-lips') as HTMLInputElement)?.value || '2'),
266
+ fz: parseFloat((document.getElementById('dc-fz') as HTMLInputElement)?.value || '0') || 0,
267
+ maxRpm: Math.max(...state.machineSpeeds || [2500]),
268
+ isLimited: state.isLimited
269
+ };
270
+
271
+ const result = calculateDrill(diam, state.toolType, getMaterial(), ctx);
272
+ dcUpdateRpmDisplay(result.displayRpm, result.idealRpm);
273
+ dcUpdateVisuals(result.displayRpm);
274
+ dcUpdateStatus(result.warning);
275
+ dcUpdateMaterialInfo();
276
+
277
+ const feedEl = document.getElementById('dc-feed-txt');
278
+ if (feedEl) feedEl.textContent = `${result.feed} mm/min`;
279
+
280
+ dcUpdateClosestSpeed(result.displayRpm);
281
+ }
282
+
283
+ document.querySelectorAll('.dc-mat-chip').forEach(chip => {
284
+ chip.addEventListener('click', () => {
285
+ document.querySelectorAll('.dc-mat-chip').forEach(c => c.classList.remove('active'));
286
+ chip.classList.add('active');
287
+ state.material = chip.getAttribute('data-id') || 'steel';
288
+ (document.getElementById('dc-fz') as HTMLInputElement).value = '';
289
+ dcCalculate();
290
+ });
291
+ });
292
+
293
+ document.querySelectorAll('.dc-toggle-btn').forEach(btn => {
294
+ btn.addEventListener('click', () => {
295
+ document.querySelectorAll('.dc-toggle-btn').forEach(b => b.classList.remove('active'));
296
+ btn.classList.add('active');
297
+ state.toolType = (btn.getAttribute('data-type') as 'hss' | 'carbide') || 'hss';
298
+ dcCalculate();
299
+ });
300
+ });
301
+
302
+ document.querySelectorAll('.dc-unit-btn').forEach(btn => {
303
+ btn.addEventListener('click', () => {
304
+ document.querySelectorAll('.dc-unit-btn').forEach(b => b.classList.remove('active'));
305
+ btn.classList.add('active');
306
+ const newUnit = (btn.getAttribute('data-unit') as 'mm' | 'in') || 'mm';
307
+ const slider = document.getElementById('dc-diam-slider') as HTMLInputElement;
308
+ if (newUnit !== state.unit) {
309
+ let val = parseFloat(slider.value);
310
+ if (newUnit === 'in') {
311
+ val = val / 25.4;
312
+ slider.min = '0.02';
313
+ slider.max = '2';
314
+ slider.step = '0.01';
315
+ } else {
316
+ val = val * 25.4;
317
+ slider.min = '0.5';
318
+ slider.max = '50';
319
+ slider.step = '0.5';
320
+ }
321
+ state.unit = newUnit;
322
+ slider.value = val.toFixed(newUnit === 'in' ? 2 : 1);
323
+ (document.getElementById('dc-diam-display') as HTMLElement).textContent = `${slider.value} ${state.unit}`;
324
+ }
325
+ dcCalculate();
326
+ });
327
+ });
328
+
329
+ const slider = document.getElementById('dc-diam-slider') as HTMLInputElement;
330
+ slider.addEventListener('input', () => {
331
+ state.diameter = parseFloat(slider.value);
332
+ (document.getElementById('dc-diam-display') as HTMLElement).textContent = `${slider.value} ${state.unit}`;
333
+ dcCalculate();
334
+ });
335
+
336
+ [document.getElementById('dc-lips'), document.getElementById('dc-fz')].forEach(el => {
337
+ el?.addEventListener('input', dcCalculate);
338
+ });
339
+
340
+ const limitToggle = document.getElementById('dc-limit-toggle');
341
+ limitToggle?.addEventListener('click', () => {
342
+ state.isLimited = !state.isLimited;
343
+ limitToggle.classList.toggle('active');
344
+ dcCalculate();
345
+ });
346
+
347
+ document.getElementById('dc-adv-trigger')?.addEventListener('click', () => {
348
+ document.getElementById('dc-adv-body')?.classList.toggle('visible');
349
+ document.getElementById('dc-adv-chevron')?.classList.toggle('rotate');
350
+ });
351
+
352
+ document.querySelectorAll('.dc-preset-btn').forEach(btn => {
353
+ btn.addEventListener('click', () => {
354
+ const preset = btn.getAttribute('data-preset') as keyof typeof presets;
355
+ if (preset && presets[preset]) {
356
+ state.machineSpeeds = [...presets[preset]];
357
+ document.querySelectorAll('.dc-preset-btn').forEach(p => p.classList.remove('active'));
358
+ btn.classList.add('active');
359
+ dcRenderChips();
360
+ dcCalculate();
361
+ }
362
+ });
363
+ });
364
+
365
+ document.getElementById('dc-add-btn')?.addEventListener('click', () => {
366
+ const inp = document.getElementById('dc-speed-in') as HTMLInputElement;
367
+ const val = parseInt(inp.value);
368
+ if (!isNaN(val) && val > 0 && !state.machineSpeeds.includes(val)) {
369
+ state.machineSpeeds.push(val);
370
+ state.machineSpeeds.sort((a, b) => a - b);
371
+ dcRenderChips();
372
+ dcCalculate();
373
+ inp.value = '';
374
+ }
375
+ });
376
+
377
+ function dcRenderChips() {
378
+ const area = document.getElementById('dc-chips-area');
379
+ if (!area) return;
380
+ area.innerHTML = '';
381
+ state.machineSpeeds.sort((a, b) => a - b).forEach(v => {
382
+ const div = document.createElement('div');
383
+ div.className = 'dc-chip';
384
+ div.innerHTML = `<span>${v}</span><button type="button">×</button>`;
385
+ div.querySelector('button')?.addEventListener('click', () => {
386
+ state.machineSpeeds = state.machineSpeeds.filter(s => s !== v);
387
+ dcRenderChips();
388
+ dcCalculate();
389
+ });
390
+ area.appendChild(div);
391
+ });
392
+ }
393
+
394
+ document.querySelector('.dc-mat-chip')?.classList.add('active');
395
+ dcCalculate();
396
+ </script>
397
+
398
+ <style>
399
+ :root {
400
+ --dc-primary: #6366f1;
401
+ --dc-primary-dark: #4f46e5;
402
+ --dc-primary-light: #818cf8;
403
+ --dc-accent: #f59e0b;
404
+ --dc-accent-dark: #d97706;
405
+ --dc-accent-light: #fbbf24;
406
+ --dc-danger: #ef4444;
407
+ --dc-success: #10b981;
408
+ --dc-neutral: #374151;
409
+ --dc-border: #e5e7eb;
410
+ }
411
+
412
+ :global(.theme-dark) {
413
+ --dc-primary: #818cf8;
414
+ --dc-primary-dark: #6366f1;
415
+ --dc-accent: #fbbf24;
416
+ --dc-border: rgba(255, 255, 255, 0.1);
417
+ }
418
+
419
+ :global(.hidden) {
420
+ display: none;
421
+ }
422
+
423
+ .dc-root {
424
+ width: 100%;
425
+ max-width: 1200px;
426
+ margin: 0 auto;
427
+ }
428
+
429
+ .dc-card {
430
+ border: 1px solid var(--dc-border);
431
+ border-radius: 32px;
432
+ overflow: hidden;
433
+ background: rgba(255, 255, 255, 0.3);
434
+ }
435
+
436
+ :global(.theme-dark) .dc-card {
437
+ background: rgba(255, 255, 255, 0.01);
438
+ }
439
+
440
+ .dc-grid {
441
+ display: grid;
442
+ grid-template-columns: 1.4fr 1fr;
443
+ min-height: 600px;
444
+ }
445
+
446
+ .dc-sidebar {
447
+ padding: 3rem;
448
+ background: rgba(255, 255, 255, 0.6);
449
+ border-right: 1px solid var(--dc-border);
450
+ display: flex;
451
+ flex-direction: column;
452
+ gap: 2.5rem;
453
+ }
454
+
455
+ :global(.theme-dark) .dc-sidebar {
456
+ background: rgba(255, 255, 255, 0.02);
457
+ }
458
+
459
+ .dc-main {
460
+ padding: 3rem;
461
+ display: flex;
462
+ flex-direction: column;
463
+ gap: 2.5rem;
464
+ background: rgba(255, 255, 255, 0.2);
465
+ }
466
+
467
+ :global(.theme-dark) .dc-main {
468
+ background: rgba(255, 255, 255, 0.005);
469
+ }
470
+
471
+ .dc-field {
472
+ display: flex;
473
+ flex-direction: column;
474
+ gap: 0.6rem;
475
+ }
476
+
477
+ .dc-field-label {
478
+ font-size: 0.85rem;
479
+ font-weight: 700;
480
+ text-transform: uppercase;
481
+ letter-spacing: 0.1em;
482
+ color: #6b7280;
483
+ }
484
+
485
+ :global(.theme-dark) .dc-field-label {
486
+ color: #9ca3af;
487
+ }
488
+
489
+ .dc-mat-list {
490
+ display: grid;
491
+ grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
492
+ gap: 1rem;
493
+ }
494
+
495
+ .dc-mat-chip {
496
+ padding: 1rem;
497
+ border: 2px solid var(--dc-border);
498
+ border-radius: 12px;
499
+ background: white;
500
+ cursor: pointer;
501
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
502
+ font-size: 0.75rem;
503
+ font-weight: 700;
504
+ color: var(--dc-neutral);
505
+ display: flex;
506
+ flex-direction: column;
507
+ align-items: center;
508
+ gap: 0.75rem;
509
+ position: relative;
510
+ }
511
+
512
+ .dc-mat-icon {
513
+ width: 64px;
514
+ height: 64px;
515
+ color: var(--dc-primary);
516
+ flex-shrink: 0;
517
+ display: flex;
518
+ align-items: center;
519
+ justify-content: center;
520
+ font-size: 2.5rem;
521
+ }
522
+
523
+ .dc-mat-chip:hover {
524
+ border-color: var(--dc-primary);
525
+ }
526
+
527
+ :global(.dc-mat-chip.active) {
528
+ background: white;
529
+ border-color: var(--dc-accent);
530
+ transform: rotate(5deg) scale(1.05);
531
+ box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1);
532
+ }
533
+
534
+ :global(.theme-dark) .dc-mat-chip {
535
+ background: rgba(255, 255, 255, 0.05);
536
+ border-color: rgba(255, 255, 255, 0.1);
537
+ color: #e5e7eb;
538
+ }
539
+
540
+ :global(.theme-dark .dc-mat-chip.active) {
541
+ background: rgba(255, 255, 255, 0.1);
542
+ border-color: var(--dc-accent);
543
+ }
544
+
545
+ .dc-toggle-group {
546
+ display: grid;
547
+ grid-template-columns: 1fr 1fr;
548
+ gap: 0.5rem;
549
+ }
550
+
551
+ .dc-toggle-btn {
552
+ padding: 0.8rem 1rem;
553
+ border: 1px solid var(--dc-border);
554
+ border-radius: 8px;
555
+ background: white;
556
+ color: var(--dc-neutral);
557
+ font-weight: 700;
558
+ font-size: 0.75rem;
559
+ cursor: pointer;
560
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
561
+ }
562
+
563
+ .dc-toggle-btn:hover {
564
+ border-color: var(--dc-primary);
565
+ }
566
+
567
+ :global(.dc-toggle-btn.active) {
568
+ background: var(--dc-primary);
569
+ border-color: var(--dc-primary);
570
+ color: white;
571
+ }
572
+
573
+ :global(.theme-dark) .dc-toggle-btn {
574
+ background: rgba(255, 255, 255, 0.05);
575
+ border-color: rgba(255, 255, 255, 0.1);
576
+ color: #e5e7eb;
577
+ }
578
+
579
+ :global(.theme-dark .dc-toggle-btn.active) {
580
+ background: var(--dc-primary);
581
+ border-color: var(--dc-primary);
582
+ color: white;
583
+ box-shadow: 0 0 12px rgba(129, 140, 248, 0.4);
584
+ }
585
+
586
+ .dc-diameter-header {
587
+ display: flex;
588
+ justify-content: space-between;
589
+ align-items: center;
590
+ margin-bottom: 0.5rem;
591
+ }
592
+
593
+ .dc-unit-toggle {
594
+ display: grid;
595
+ grid-template-columns: 1fr 1fr;
596
+ gap: 0.4rem;
597
+ }
598
+
599
+ .dc-unit-btn {
600
+ padding: 0.5rem 0.75rem;
601
+ border: 1px solid var(--dc-border);
602
+ border-radius: 6px;
603
+ background: white;
604
+ color: var(--dc-neutral);
605
+ font-weight: 700;
606
+ font-size: 0.7rem;
607
+ cursor: pointer;
608
+ transition: all 0.2s;
609
+ }
610
+
611
+ :global(.dc-unit-btn.active) {
612
+ background: white;
613
+ border-color: var(--dc-primary);
614
+ color: var(--dc-primary);
615
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.15);
616
+ }
617
+
618
+ :global(.theme-dark) .dc-unit-btn {
619
+ background: rgba(255, 255, 255, 0.05);
620
+ border-color: rgba(255, 255, 255, 0.1);
621
+ color: #e5e7eb;
622
+ }
623
+
624
+ .dc-diameter-display {
625
+ font-size: 1.3rem;
626
+ font-weight: 900;
627
+ color: var(--dc-primary);
628
+ margin-bottom: 0.5rem;
629
+ }
630
+
631
+ :global(.theme-dark) .dc-diameter-display {
632
+ color: var(--dc-primary-light);
633
+ }
634
+
635
+ .dc-slider-box {
636
+ width: 100%;
637
+ }
638
+
639
+ .dc-slider {
640
+ width: 100%;
641
+ height: 8px;
642
+ border-radius: 4px;
643
+ background: var(--dc-border);
644
+ outline: none;
645
+ appearance: none;
646
+ -webkit-appearance: none;
647
+ }
648
+
649
+ .dc-slider::-webkit-slider-thumb {
650
+ appearance: none;
651
+ -webkit-appearance: none;
652
+ width: 24px;
653
+ height: 24px;
654
+ border-radius: 50%;
655
+ background: var(--dc-primary);
656
+ cursor: pointer;
657
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
658
+ }
659
+
660
+ .dc-slider::-moz-range-thumb {
661
+ width: 24px;
662
+ height: 24px;
663
+ border-radius: 50%;
664
+ background: var(--dc-primary);
665
+ cursor: pointer;
666
+ border: none;
667
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
668
+ }
669
+
670
+ .dc-mode-badge {
671
+ display: flex;
672
+ justify-content: space-between;
673
+ align-items: center;
674
+ background: rgba(99, 102, 241, 0.1);
675
+ border: 1px solid rgba(99, 102, 241, 0.2);
676
+ border-radius: 12px;
677
+ padding: 1rem;
678
+ }
679
+
680
+ :global(.theme-dark) .dc-mode-badge {
681
+ background: rgba(99, 102, 241, 0.15);
682
+ border-color: rgba(99, 102, 241, 0.3);
683
+ }
684
+
685
+ .dc-mode-label {
686
+ font-size: 0.75rem;
687
+ font-weight: 800;
688
+ color: var(--dc-primary);
689
+ text-transform: uppercase;
690
+ letter-spacing: 0.05em;
691
+ }
692
+
693
+ :global(.theme-dark) .dc-mode-label {
694
+ color: var(--dc-primary-light);
695
+ }
696
+
697
+ .dc-toggle-switch {
698
+ width: 48px;
699
+ height: 24px;
700
+ border-radius: 12px;
701
+ background: var(--dc-border);
702
+ cursor: pointer;
703
+ transition: background 0.3s cubic-bezier(0.4, 0, 0.2, 1);
704
+ position: relative;
705
+ }
706
+
707
+ :global(.dc-toggle-switch.active) {
708
+ background: var(--dc-primary);
709
+ }
710
+
711
+ .dc-toggle-switch::after {
712
+ content: '';
713
+ position: absolute;
714
+ width: 20px;
715
+ height: 20px;
716
+ border-radius: 50%;
717
+ background: white;
718
+ top: 2px;
719
+ left: 2px;
720
+ transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
721
+ }
722
+
723
+ :global(.dc-toggle-switch.active::after) {
724
+ left: 26px;
725
+ }
726
+
727
+ .dc-advanced {
728
+ margin-top: auto;
729
+ }
730
+ .dc-advanced-trigger {
731
+ width: 100%;
732
+ padding: 0.75rem 1rem;
733
+ background: white;
734
+ border: 2px dashed var(--dc-border);
735
+ border-radius: 12px;
736
+ display: flex;
737
+ justify-content: space-between;
738
+ align-items: center;
739
+ cursor: pointer;
740
+ font-weight: 700;
741
+ color: var(--dc-primary);
742
+ font-size: 0.8rem;
743
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
744
+ }
745
+ .dc-advanced-trigger:hover {
746
+ border-style: solid;
747
+ background: rgba(99, 102, 241, 0.05);
748
+ }
749
+ :global(.theme-dark) .dc-advanced-trigger {
750
+ background: rgba(255, 255, 255, 0.05);
751
+ border-color: rgba(99, 102, 241, 0.3);
752
+ color: var(--dc-primary-light);
753
+ }
754
+
755
+ .dc-chevron {
756
+ width: 16px;
757
+ height: 16px;
758
+ color: inherit;
759
+ transition: transform 0.2s;
760
+ }
761
+
762
+ :global(.dc-chevron.rotate) {
763
+ transform: rotate(180deg);
764
+ }
765
+
766
+ .dc-advanced-body {
767
+ max-height: 0;
768
+ overflow: hidden;
769
+ transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
770
+ margin-top: 0;
771
+ }
772
+ :global(.dc-advanced-body.visible) {
773
+ max-height: 500px;
774
+ margin-top: 1rem;
775
+ }
776
+ .dc-adv-grid {
777
+ display: grid;
778
+ grid-template-columns: 1fr 1fr;
779
+ gap: 1rem;
780
+ }
781
+
782
+ .dc-input {
783
+ width: 100%;
784
+ padding: 1rem;
785
+ border: 1px solid var(--dc-border);
786
+ border-radius: 8px;
787
+ font-weight: 700;
788
+ background: white;
789
+ color: var(--dc-neutral);
790
+ transition: border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
791
+ }
792
+
793
+ .dc-input:focus {
794
+ outline: none;
795
+ border-color: var(--dc-primary);
796
+ }
797
+
798
+ :global(.theme-dark) .dc-input {
799
+ background: rgba(0, 0, 0, 0.25);
800
+ border-color: rgba(255, 255, 255, 0.1);
801
+ color: #f9fafb;
802
+ }
803
+
804
+ .dc-hero {
805
+ text-align: center;
806
+ padding: 3rem;
807
+ background: linear-gradient(135deg, rgba(99, 102, 241, 0.08), rgba(245, 158, 11, 0.05));
808
+ border-radius: 16px;
809
+ }
810
+
811
+ .dc-hero-label {
812
+ font-size: 0.7rem;
813
+ font-weight: 800;
814
+ text-transform: uppercase;
815
+ letter-spacing: 0.15em;
816
+ color: var(--dc-primary);
817
+ }
818
+
819
+ :global(.theme-dark) .dc-hero-label {
820
+ color: var(--dc-primary-light);
821
+ }
822
+
823
+ .dc-rpm-number {
824
+ font-size: 6rem;
825
+ font-weight: 900;
826
+ color: var(--dc-primary);
827
+ line-height: 1;
828
+ margin: 1rem 0;
829
+ }
830
+ :global(.dc-rpm-number.limited) { color: var(--dc-danger); }
831
+ :global(.dc-rpm-number.slow) { color: var(--dc-accent); }
832
+ :global(.dc-rpm-number.optimal) { color: var(--dc-success); }
833
+ :global(.theme-dark .dc-rpm-number) { color: var(--dc-primary-light); }
834
+ :global(.theme-dark .dc-rpm-number.slow) { color: var(--dc-accent-light); }
835
+
836
+ .dc-hero-unit {
837
+ font-size: 0.85rem;
838
+ font-weight: 800;
839
+ color: #6b7280;
840
+ text-transform: uppercase;
841
+ letter-spacing: 0.05em;
842
+ }
843
+
844
+ .dc-rotation-visual {
845
+ width: 80px;
846
+ height: 80px;
847
+ border-radius: 50%;
848
+ border: 4px solid;
849
+ border-top-color: var(--dc-primary);
850
+ border-right-color: var(--dc-accent);
851
+ border-bottom-color: var(--dc-primary);
852
+ border-left-color: var(--dc-accent);
853
+ margin: 1rem auto;
854
+ animation: dc-spin 2s linear infinite;
855
+ animation-play-state: paused;
856
+ }
857
+
858
+ @keyframes dc-spin {
859
+ from {
860
+ transform: rotate(0deg);
861
+ }
862
+ to {
863
+ transform: rotate(360deg);
864
+ }
865
+ }
866
+
867
+ .dc-status-row {
868
+ min-height: 32px;
869
+ display: flex;
870
+ justify-content: center;
871
+ margin-top: 0.5rem;
872
+ gap: 0.5rem;
873
+ }
874
+
875
+ :global(.dc-badge) {
876
+ display: inline-flex;
877
+ align-items: center;
878
+ gap: 0.5rem;
879
+ padding: 0.5rem 1rem;
880
+ border-radius: 20px;
881
+ font-size: 0.7rem;
882
+ font-weight: 700;
883
+ }
884
+
885
+ :global(.dc-badge-warning) {
886
+ background: rgba(99, 102, 241, 0.15);
887
+ color: var(--dc-primary);
888
+ }
889
+
890
+ :global(.theme-dark .dc-badge-warning) {
891
+ background: rgba(99, 102, 241, 0.25);
892
+ }
893
+
894
+ :global(.dc-badge-alert) {
895
+ background: rgba(239, 68, 68, 0.15);
896
+ color: var(--dc-danger);
897
+ }
898
+
899
+ :global(.theme-dark .dc-badge-alert) {
900
+ background: rgba(239, 68, 68, 0.25);
901
+ }
902
+
903
+ :global(.dc-badge-icon) {
904
+ width: 16px;
905
+ height: 16px;
906
+ display: flex;
907
+ align-items: center;
908
+ justify-content: center;
909
+ flex-shrink: 0;
910
+ color: inherit;
911
+ }
912
+
913
+ :global(.dc-badge-icon svg) {
914
+ width: 100%;
915
+ height: 100%;
916
+ stroke: currentcolor;
917
+ fill: currentcolor;
918
+ }
919
+
920
+ :global(.dc-badge-icon path) {
921
+ stroke: currentcolor;
922
+ fill: currentcolor;
923
+ }
924
+
925
+ .dc-tiles {
926
+ display: grid;
927
+ grid-template-columns: 1fr;
928
+ gap: 1rem;
929
+ }
930
+
931
+ .dc-tile {
932
+ padding: 1.5rem;
933
+ background: rgba(255, 255, 255, 0.5);
934
+ border: 1px solid rgba(255, 255, 255, 0.7);
935
+ border-radius: 12px;
936
+ display: flex;
937
+ flex-direction: row;
938
+ align-items: flex-start;
939
+ gap: 1rem;
940
+ transition: all 0.2s;
941
+ }
942
+
943
+ .dc-tile:hover {
944
+ background: rgba(255, 255, 255, 0.7);
945
+ border-color: var(--dc-border);
946
+ }
947
+
948
+ :global(.theme-dark) .dc-tile {
949
+ background: rgba(255, 255, 255, 0.04);
950
+ border-color: rgba(255, 255, 255, 0.06);
951
+ }
952
+
953
+ .dc-tile-icon {
954
+ width: 56px;
955
+ height: 56px;
956
+ color: var(--dc-primary);
957
+ flex-shrink: 0;
958
+ display: flex;
959
+ align-items: center;
960
+ justify-content: center;
961
+ font-size: 2rem;
962
+ }
963
+
964
+ :global(.theme-dark) .dc-tile-icon {
965
+ color: var(--dc-primary-light);
966
+ }
967
+
968
+ .dc-tile-content {
969
+ display: flex;
970
+ flex-direction: column;
971
+ gap: 0.4rem;
972
+ }
973
+
974
+ .dc-tile-label {
975
+ font-size: 0.7rem;
976
+ font-weight: 700;
977
+ text-transform: uppercase;
978
+ color: #6b7280;
979
+ letter-spacing: 0.05em;
980
+ }
981
+
982
+ :global(.theme-dark) .dc-tile-label {
983
+ color: #9ca3af;
984
+ }
985
+
986
+ .dc-tile-value {
987
+ font-size: 1.3rem;
988
+ font-weight: 900;
989
+ color: var(--dc-primary);
990
+ }
991
+
992
+ :global(.theme-dark) .dc-tile-value {
993
+ color: var(--dc-primary-light);
994
+ }
995
+
996
+ .dc-machine-card {
997
+ background: rgba(255, 255, 255, 0.4);
998
+ border: 1px solid rgba(255, 255, 255, 0.6);
999
+ border-radius: 12px;
1000
+ padding: 1.5rem;
1001
+ display: flex;
1002
+ flex-direction: column;
1003
+ gap: 1rem;
1004
+ }
1005
+
1006
+ :global(.theme-dark) .dc-machine-card {
1007
+ background: rgba(255, 255, 255, 0.03);
1008
+ border-color: rgba(255, 255, 255, 0.06);
1009
+ }
1010
+
1011
+ .dc-presets {
1012
+ display: grid;
1013
+ grid-template-columns: 1fr 1fr;
1014
+ gap: 0;
1015
+ border-radius: 16px 16px 0 0;
1016
+ overflow: hidden;
1017
+ border: 1px solid var(--dc-border);
1018
+ }
1019
+ .dc-preset-btn {
1020
+ padding: 0.8rem 1rem;
1021
+ border: none;
1022
+ border-right: 1px solid var(--dc-border);
1023
+ background: white;
1024
+ color: var(--dc-neutral);
1025
+ font-weight: 700;
1026
+ font-size: 0.75rem;
1027
+ cursor: pointer;
1028
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
1029
+ }
1030
+ :global(.dc-preset-btn:last-child) {
1031
+ border-right: none;
1032
+ }
1033
+
1034
+ .dc-preset-btn:hover {
1035
+ background: rgba(99, 102, 241, 0.05);
1036
+ }
1037
+
1038
+ :global(.dc-preset-btn.active) {
1039
+ background: var(--dc-primary);
1040
+ color: white;
1041
+ }
1042
+
1043
+ :global(.theme-dark) .dc-preset-btn {
1044
+ background: rgba(255, 255, 255, 0.05);
1045
+ border-right-color: rgba(255, 255, 255, 0.1);
1046
+ color: #e5e7eb;
1047
+ }
1048
+
1049
+ .dc-manual-input {
1050
+ display: flex;
1051
+ gap: 0;
1052
+ border: 1px solid var(--dc-border);
1053
+ border-radius: 8px;
1054
+ overflow: hidden;
1055
+ background: white;
1056
+ margin-top: 0.75rem;
1057
+ }
1058
+ .dc-manual-input .dc-input {
1059
+ flex: 1;
1060
+ border: none;
1061
+ border-radius: 0;
1062
+ padding: 0.8rem 1rem;
1063
+ font-size: 0.9rem;
1064
+ }
1065
+
1066
+ .dc-input::placeholder {
1067
+ color: #9ca3af;
1068
+ }
1069
+
1070
+ .dc-manual-input .dc-input:focus {
1071
+ background: rgba(99, 102, 241, 0.02);
1072
+ }
1073
+
1074
+ :global(.theme-dark) .dc-manual-input .dc-input {
1075
+ background: rgba(255, 255, 255, 0.08);
1076
+ color: #f9fafb;
1077
+ border-color: rgba(255, 255, 255, 0.1);
1078
+ }
1079
+ .dc-add-btn {
1080
+ padding: 0.8rem 1.2rem;
1081
+ background: var(--dc-primary);
1082
+ color: white;
1083
+ border: none;
1084
+ font-weight: 900;
1085
+ cursor: pointer;
1086
+ transition: all 0.2s;
1087
+ display: flex;
1088
+ align-items: center;
1089
+ justify-content: center;
1090
+ font-size: 1.2rem;
1091
+ }
1092
+ .dc-add-btn:hover {
1093
+ background: var(--dc-primary-dark);
1094
+ }
1095
+
1096
+ :global(.dc-chips-area) {
1097
+ display: flex;
1098
+ flex-wrap: wrap;
1099
+ gap: 0.5rem;
1100
+ min-height: 0;
1101
+ }
1102
+ :global(.dc-chip) {
1103
+ display: inline-flex;
1104
+ align-items: center;
1105
+ gap: 0.5rem;
1106
+ background: rgba(99, 102, 241, 0.1);
1107
+ color: var(--dc-primary);
1108
+ padding: 0.5rem 0.8rem;
1109
+ border-radius: 16px;
1110
+ font-size: 0.75rem;
1111
+ font-weight: 700;
1112
+ }
1113
+ :global(.theme-dark .dc-chip) {
1114
+ background: rgba(99, 102, 241, 0.15);
1115
+ color: var(--dc-primary-light);
1116
+ }
1117
+ :global(.dc-chip button) {
1118
+ background: none;
1119
+ border: none;
1120
+ color: inherit;
1121
+ cursor: pointer;
1122
+ font-weight: 900;
1123
+ padding: 0;
1124
+ width: 16px;
1125
+ height: 16px;
1126
+ display: flex;
1127
+ align-items: center;
1128
+ justify-content: center;
1129
+ transition: color 0.2s;
1130
+ }
1131
+ :global(.dc-chip button:hover) { color: var(--dc-danger); }
1132
+
1133
+ :global(.dc-match-box) {
1134
+ background: rgba(99, 102, 241, 0.08);
1135
+ border: 1px solid rgba(99, 102, 241, 0.2);
1136
+ border-radius: 12px;
1137
+ padding: 1.2rem;
1138
+ text-align: center;
1139
+ animation: slide-in-up 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1140
+ }
1141
+
1142
+ @keyframes slide-in-up {
1143
+ from {
1144
+ opacity: 0;
1145
+ transform: translateY(20px);
1146
+ }
1147
+ to {
1148
+ opacity: 1;
1149
+ transform: translateY(0);
1150
+ }
1151
+ }
1152
+ :global(.dc-match-header) {
1153
+ font-size: 0.75rem;
1154
+ font-weight: 800;
1155
+ text-transform: uppercase;
1156
+ color: var(--dc-primary);
1157
+ margin-bottom: 0.6rem;
1158
+ letter-spacing: 0.05em;
1159
+ }
1160
+
1161
+ :global(.theme-dark .dc-match-header) {
1162
+ color: var(--dc-primary-light);
1163
+ }
1164
+
1165
+ :global(.dc-match-value) {
1166
+ font-size: 1.5rem;
1167
+ font-weight: 900;
1168
+ color: var(--dc-primary);
1169
+ }
1170
+
1171
+ :global(.theme-dark .dc-match-value) {
1172
+ color: var(--dc-primary-light);
1173
+ }
1174
+
1175
+ :global(.dc-match-hint) {
1176
+ font-size: 0.65rem;
1177
+ color: #6b7280;
1178
+ margin: 0.6rem 0 0;
1179
+ }
1180
+
1181
+ @media (max-width: 900px) {
1182
+ .dc-grid {
1183
+ grid-template-columns: 1fr;
1184
+ }
1185
+
1186
+ .dc-sidebar {
1187
+ border-right: none;
1188
+ border-bottom: 1px solid var(--dc-border);
1189
+ padding: 2rem;
1190
+ gap: 2rem;
1191
+ }
1192
+
1193
+ .dc-main {
1194
+ padding: 2rem;
1195
+ gap: 2rem;
1196
+ }
1197
+
1198
+ .dc-tiles {
1199
+ grid-template-columns: 1fr;
1200
+ }
1201
+
1202
+ .dc-mat-list {
1203
+ grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
1204
+ }
1205
+
1206
+ .dc-rpm-number {
1207
+ font-size: 3.5rem;
1208
+ }
1209
+ }
1210
+ </style>