@jjlmoya/utils-forensic-science 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 (316) hide show
  1. package/package.json +69 -0
  2. package/scripts/crystal-lattice-catalog-translations.mjs +459 -0
  3. package/scripts/postinstall.mjs +27 -0
  4. package/scripts/sync-crystal-lattice-i18n.mjs +50 -0
  5. package/src/category/i18n/de.ts +55 -0
  6. package/src/category/i18n/en.ts +55 -0
  7. package/src/category/i18n/es.ts +55 -0
  8. package/src/category/i18n/fr.ts +55 -0
  9. package/src/category/i18n/id.ts +55 -0
  10. package/src/category/i18n/it.ts +55 -0
  11. package/src/category/i18n/ja.ts +56 -0
  12. package/src/category/i18n/ko.ts +56 -0
  13. package/src/category/i18n/nl.ts +55 -0
  14. package/src/category/i18n/pl.ts +55 -0
  15. package/src/category/i18n/pt.ts +55 -0
  16. package/src/category/i18n/ru.ts +55 -0
  17. package/src/category/i18n/sv.ts +55 -0
  18. package/src/category/i18n/tr.ts +55 -0
  19. package/src/category/i18n/zh.ts +56 -0
  20. package/src/category/index.ts +48 -0
  21. package/src/category/seo.astro +15 -0
  22. package/src/components/PreviewNavSidebar.astro +116 -0
  23. package/src/components/PreviewToolbar.astro +143 -0
  24. package/src/data.ts +10 -0
  25. package/src/entries.ts +48 -0
  26. package/src/env.d.ts +5 -0
  27. package/src/index.ts +29 -0
  28. package/src/layouts/PreviewLayout.astro +117 -0
  29. package/src/pages/[locale]/[slug].astro +162 -0
  30. package/src/pages/[locale].astro +261 -0
  31. package/src/pages/index.astro +3 -0
  32. package/src/tests/diacritics_density.test.ts +140 -0
  33. package/src/tests/faq_count.test.ts +19 -0
  34. package/src/tests/i18n_coverage.test.ts +36 -0
  35. package/src/tests/inverted_punctuation.test.ts +84 -0
  36. package/src/tests/locale_completeness.test.ts +24 -0
  37. package/src/tests/mocks/astro_mock.js +2 -0
  38. package/src/tests/no_en_dash.test.ts +70 -0
  39. package/src/tests/no_h1_in_components.test.ts +48 -0
  40. package/src/tests/schemas_fulfillment.test.ts +23 -0
  41. package/src/tests/script_density.test.ts +94 -0
  42. package/src/tests/seo_length.test.ts +46 -0
  43. package/src/tests/shared-test-helpers.ts +56 -0
  44. package/src/tests/slug_language_code_format.test.ts +23 -0
  45. package/src/tests/slug_uniqueness.test.ts +81 -0
  46. package/src/tests/title_quality.test.ts +55 -0
  47. package/src/tests/tool_exports.test.ts +34 -0
  48. package/src/tests/tool_validation.test.ts +16 -0
  49. package/src/tests/widmark.test.ts +28 -0
  50. package/src/tool/forensic-age-estimator/bibliography.astro +14 -0
  51. package/src/tool/forensic-age-estimator/bibliography.ts +16 -0
  52. package/src/tool/forensic-age-estimator/component.astro +321 -0
  53. package/src/tool/forensic-age-estimator/dental-skeletal-third-molar-age-estimator.css +634 -0
  54. package/src/tool/forensic-age-estimator/entry.ts +32 -0
  55. package/src/tool/forensic-age-estimator/i18n/de.ts +245 -0
  56. package/src/tool/forensic-age-estimator/i18n/en.ts +245 -0
  57. package/src/tool/forensic-age-estimator/i18n/es.ts +245 -0
  58. package/src/tool/forensic-age-estimator/i18n/fr.ts +245 -0
  59. package/src/tool/forensic-age-estimator/i18n/id.ts +245 -0
  60. package/src/tool/forensic-age-estimator/i18n/it.ts +245 -0
  61. package/src/tool/forensic-age-estimator/i18n/ja.ts +245 -0
  62. package/src/tool/forensic-age-estimator/i18n/ko.ts +245 -0
  63. package/src/tool/forensic-age-estimator/i18n/nl.ts +245 -0
  64. package/src/tool/forensic-age-estimator/i18n/pl.ts +245 -0
  65. package/src/tool/forensic-age-estimator/i18n/pt.ts +245 -0
  66. package/src/tool/forensic-age-estimator/i18n/ru.ts +245 -0
  67. package/src/tool/forensic-age-estimator/i18n/sv.ts +245 -0
  68. package/src/tool/forensic-age-estimator/i18n/tr.ts +245 -0
  69. package/src/tool/forensic-age-estimator/i18n/zh.ts +246 -0
  70. package/src/tool/forensic-age-estimator/index.ts +11 -0
  71. package/src/tool/forensic-age-estimator/logic.ts +81 -0
  72. package/src/tool/forensic-age-estimator/seo.astro +15 -0
  73. package/src/tool/forensic-blood-test-simulator/bibliography.astro +6 -0
  74. package/src/tool/forensic-blood-test-simulator/bibliography.ts +12 -0
  75. package/src/tool/forensic-blood-test-simulator/component.astro +168 -0
  76. package/src/tool/forensic-blood-test-simulator/dom-utils.ts +40 -0
  77. package/src/tool/forensic-blood-test-simulator/entry.ts +32 -0
  78. package/src/tool/forensic-blood-test-simulator/forensic-presumptive-blood-testing-luminol-kastle-meyer-simulator.css +532 -0
  79. package/src/tool/forensic-blood-test-simulator/helpers.ts +16 -0
  80. package/src/tool/forensic-blood-test-simulator/i18n/de.ts +185 -0
  81. package/src/tool/forensic-blood-test-simulator/i18n/en.ts +185 -0
  82. package/src/tool/forensic-blood-test-simulator/i18n/es.ts +185 -0
  83. package/src/tool/forensic-blood-test-simulator/i18n/fr.ts +220 -0
  84. package/src/tool/forensic-blood-test-simulator/i18n/id.ts +220 -0
  85. package/src/tool/forensic-blood-test-simulator/i18n/it.ts +220 -0
  86. package/src/tool/forensic-blood-test-simulator/i18n/ja.ts +220 -0
  87. package/src/tool/forensic-blood-test-simulator/i18n/ko.ts +220 -0
  88. package/src/tool/forensic-blood-test-simulator/i18n/nl.ts +220 -0
  89. package/src/tool/forensic-blood-test-simulator/i18n/pl.ts +220 -0
  90. package/src/tool/forensic-blood-test-simulator/i18n/pt.ts +220 -0
  91. package/src/tool/forensic-blood-test-simulator/i18n/ru.ts +220 -0
  92. package/src/tool/forensic-blood-test-simulator/i18n/sv.ts +220 -0
  93. package/src/tool/forensic-blood-test-simulator/i18n/tr.ts +220 -0
  94. package/src/tool/forensic-blood-test-simulator/i18n/zh.ts +220 -0
  95. package/src/tool/forensic-blood-test-simulator/index.ts +11 -0
  96. package/src/tool/forensic-blood-test-simulator/logic.ts +174 -0
  97. package/src/tool/forensic-blood-test-simulator/script.ts +192 -0
  98. package/src/tool/forensic-blood-test-simulator/seo.astro +15 -0
  99. package/src/tool/forensic-fiber-comparison-microscope/bibliography.astro +6 -0
  100. package/src/tool/forensic-fiber-comparison-microscope/bibliography.ts +16 -0
  101. package/src/tool/forensic-fiber-comparison-microscope/component.astro +146 -0
  102. package/src/tool/forensic-fiber-comparison-microscope/entry.ts +32 -0
  103. package/src/tool/forensic-fiber-comparison-microscope/forensic-fiber-comparison-microscope.css +629 -0
  104. package/src/tool/forensic-fiber-comparison-microscope/i18n/de.ts +250 -0
  105. package/src/tool/forensic-fiber-comparison-microscope/i18n/en.ts +246 -0
  106. package/src/tool/forensic-fiber-comparison-microscope/i18n/es.ts +246 -0
  107. package/src/tool/forensic-fiber-comparison-microscope/i18n/fr.ts +246 -0
  108. package/src/tool/forensic-fiber-comparison-microscope/i18n/id.ts +250 -0
  109. package/src/tool/forensic-fiber-comparison-microscope/i18n/it.ts +246 -0
  110. package/src/tool/forensic-fiber-comparison-microscope/i18n/ja.ts +250 -0
  111. package/src/tool/forensic-fiber-comparison-microscope/i18n/ko.ts +246 -0
  112. package/src/tool/forensic-fiber-comparison-microscope/i18n/nl.ts +246 -0
  113. package/src/tool/forensic-fiber-comparison-microscope/i18n/pl.ts +250 -0
  114. package/src/tool/forensic-fiber-comparison-microscope/i18n/pt.ts +250 -0
  115. package/src/tool/forensic-fiber-comparison-microscope/i18n/ru.ts +250 -0
  116. package/src/tool/forensic-fiber-comparison-microscope/i18n/sv.ts +250 -0
  117. package/src/tool/forensic-fiber-comparison-microscope/i18n/tr.ts +246 -0
  118. package/src/tool/forensic-fiber-comparison-microscope/i18n/zh.ts +250 -0
  119. package/src/tool/forensic-fiber-comparison-microscope/index.ts +11 -0
  120. package/src/tool/forensic-fiber-comparison-microscope/logic.ts +244 -0
  121. package/src/tool/forensic-fiber-comparison-microscope/render.ts +265 -0
  122. package/src/tool/forensic-fiber-comparison-microscope/seo.astro +15 -0
  123. package/src/tool/forensic-fiber-comparison-microscope/view.ts +267 -0
  124. package/src/tool/forensic-glass-becke-line-simulator/bibliography.astro +6 -0
  125. package/src/tool/forensic-glass-becke-line-simulator/bibliography.ts +16 -0
  126. package/src/tool/forensic-glass-becke-line-simulator/component.astro +81 -0
  127. package/src/tool/forensic-glass-becke-line-simulator/entry.ts +32 -0
  128. package/src/tool/forensic-glass-becke-line-simulator/forensic-glass-becke-line-simulator.css +392 -0
  129. package/src/tool/forensic-glass-becke-line-simulator/i18n/de.ts +231 -0
  130. package/src/tool/forensic-glass-becke-line-simulator/i18n/en.ts +231 -0
  131. package/src/tool/forensic-glass-becke-line-simulator/i18n/es.ts +231 -0
  132. package/src/tool/forensic-glass-becke-line-simulator/i18n/fr.ts +231 -0
  133. package/src/tool/forensic-glass-becke-line-simulator/i18n/id.ts +231 -0
  134. package/src/tool/forensic-glass-becke-line-simulator/i18n/it.ts +231 -0
  135. package/src/tool/forensic-glass-becke-line-simulator/i18n/ja.ts +231 -0
  136. package/src/tool/forensic-glass-becke-line-simulator/i18n/ko.ts +231 -0
  137. package/src/tool/forensic-glass-becke-line-simulator/i18n/nl.ts +231 -0
  138. package/src/tool/forensic-glass-becke-line-simulator/i18n/pl.ts +231 -0
  139. package/src/tool/forensic-glass-becke-line-simulator/i18n/pt.ts +231 -0
  140. package/src/tool/forensic-glass-becke-line-simulator/i18n/ru.ts +231 -0
  141. package/src/tool/forensic-glass-becke-line-simulator/i18n/sv.ts +231 -0
  142. package/src/tool/forensic-glass-becke-line-simulator/i18n/tr.ts +231 -0
  143. package/src/tool/forensic-glass-becke-line-simulator/i18n/zh.ts +231 -0
  144. package/src/tool/forensic-glass-becke-line-simulator/index.ts +11 -0
  145. package/src/tool/forensic-glass-becke-line-simulator/logic.ts +100 -0
  146. package/src/tool/forensic-glass-becke-line-simulator/seo.astro +15 -0
  147. package/src/tool/forensic-glass-becke-line-simulator/view.ts +281 -0
  148. package/src/tool/forensic-image-authenticity-analyzer/bibliography.astro +9 -0
  149. package/src/tool/forensic-image-authenticity-analyzer/bibliography.ts +7 -0
  150. package/src/tool/forensic-image-authenticity-analyzer/component.astro +250 -0
  151. package/src/tool/forensic-image-authenticity-analyzer/entry.ts +29 -0
  152. package/src/tool/forensic-image-authenticity-analyzer/forensic-image-metadata-authenticity-analyzer.css +679 -0
  153. package/src/tool/forensic-image-authenticity-analyzer/i18n/de.ts +105 -0
  154. package/src/tool/forensic-image-authenticity-analyzer/i18n/en.ts +105 -0
  155. package/src/tool/forensic-image-authenticity-analyzer/i18n/es.ts +105 -0
  156. package/src/tool/forensic-image-authenticity-analyzer/i18n/fr.ts +105 -0
  157. package/src/tool/forensic-image-authenticity-analyzer/i18n/id.ts +76 -0
  158. package/src/tool/forensic-image-authenticity-analyzer/i18n/it.ts +105 -0
  159. package/src/tool/forensic-image-authenticity-analyzer/i18n/ja.ts +72 -0
  160. package/src/tool/forensic-image-authenticity-analyzer/i18n/ko.ts +72 -0
  161. package/src/tool/forensic-image-authenticity-analyzer/i18n/nl.ts +75 -0
  162. package/src/tool/forensic-image-authenticity-analyzer/i18n/pl.ts +75 -0
  163. package/src/tool/forensic-image-authenticity-analyzer/i18n/pt.ts +105 -0
  164. package/src/tool/forensic-image-authenticity-analyzer/i18n/ru.ts +75 -0
  165. package/src/tool/forensic-image-authenticity-analyzer/i18n/sv.ts +76 -0
  166. package/src/tool/forensic-image-authenticity-analyzer/i18n/tr.ts +76 -0
  167. package/src/tool/forensic-image-authenticity-analyzer/i18n/zh.ts +71 -0
  168. package/src/tool/forensic-image-authenticity-analyzer/index.ts +11 -0
  169. package/src/tool/forensic-image-authenticity-analyzer/logic.ts +283 -0
  170. package/src/tool/forensic-image-authenticity-analyzer/seo.astro +10 -0
  171. package/src/tool/forensic-microcrystal-drug-simulator/bibliography.astro +6 -0
  172. package/src/tool/forensic-microcrystal-drug-simulator/bibliography.ts +12 -0
  173. package/src/tool/forensic-microcrystal-drug-simulator/component.astro +240 -0
  174. package/src/tool/forensic-microcrystal-drug-simulator/entry.ts +32 -0
  175. package/src/tool/forensic-microcrystal-drug-simulator/forensic-microcrystal-drug-simulator.css +430 -0
  176. package/src/tool/forensic-microcrystal-drug-simulator/i18n/de.ts +244 -0
  177. package/src/tool/forensic-microcrystal-drug-simulator/i18n/en.ts +244 -0
  178. package/src/tool/forensic-microcrystal-drug-simulator/i18n/es.ts +244 -0
  179. package/src/tool/forensic-microcrystal-drug-simulator/i18n/fr.ts +244 -0
  180. package/src/tool/forensic-microcrystal-drug-simulator/i18n/id.ts +244 -0
  181. package/src/tool/forensic-microcrystal-drug-simulator/i18n/it.ts +244 -0
  182. package/src/tool/forensic-microcrystal-drug-simulator/i18n/ja.ts +244 -0
  183. package/src/tool/forensic-microcrystal-drug-simulator/i18n/ko.ts +244 -0
  184. package/src/tool/forensic-microcrystal-drug-simulator/i18n/nl.ts +244 -0
  185. package/src/tool/forensic-microcrystal-drug-simulator/i18n/pl.ts +244 -0
  186. package/src/tool/forensic-microcrystal-drug-simulator/i18n/pt.ts +244 -0
  187. package/src/tool/forensic-microcrystal-drug-simulator/i18n/ru.ts +244 -0
  188. package/src/tool/forensic-microcrystal-drug-simulator/i18n/sv.ts +244 -0
  189. package/src/tool/forensic-microcrystal-drug-simulator/i18n/tr.ts +244 -0
  190. package/src/tool/forensic-microcrystal-drug-simulator/i18n/zh.ts +244 -0
  191. package/src/tool/forensic-microcrystal-drug-simulator/index.ts +11 -0
  192. package/src/tool/forensic-microcrystal-drug-simulator/logic.ts +189 -0
  193. package/src/tool/forensic-microcrystal-drug-simulator/seo.astro +15 -0
  194. package/src/tool/forensic-sex-determinator/bibliography.astro +14 -0
  195. package/src/tool/forensic-sex-determinator/bibliography.ts +12 -0
  196. package/src/tool/forensic-sex-determinator/component.astro +463 -0
  197. package/src/tool/forensic-sex-determinator/entry.ts +32 -0
  198. package/src/tool/forensic-sex-determinator/forensic-sex-determinator.css +413 -0
  199. package/src/tool/forensic-sex-determinator/i18n/de.ts +211 -0
  200. package/src/tool/forensic-sex-determinator/i18n/en.ts +211 -0
  201. package/src/tool/forensic-sex-determinator/i18n/es.ts +211 -0
  202. package/src/tool/forensic-sex-determinator/i18n/fr.ts +211 -0
  203. package/src/tool/forensic-sex-determinator/i18n/id.ts +211 -0
  204. package/src/tool/forensic-sex-determinator/i18n/it.ts +211 -0
  205. package/src/tool/forensic-sex-determinator/i18n/ja.ts +211 -0
  206. package/src/tool/forensic-sex-determinator/i18n/ko.ts +211 -0
  207. package/src/tool/forensic-sex-determinator/i18n/nl.ts +211 -0
  208. package/src/tool/forensic-sex-determinator/i18n/pl.ts +211 -0
  209. package/src/tool/forensic-sex-determinator/i18n/pt.ts +211 -0
  210. package/src/tool/forensic-sex-determinator/i18n/ru.ts +211 -0
  211. package/src/tool/forensic-sex-determinator/i18n/sv.ts +211 -0
  212. package/src/tool/forensic-sex-determinator/i18n/tr.ts +211 -0
  213. package/src/tool/forensic-sex-determinator/i18n/zh.ts +211 -0
  214. package/src/tool/forensic-sex-determinator/index.ts +11 -0
  215. package/src/tool/forensic-sex-determinator/logic.ts +89 -0
  216. package/src/tool/forensic-sex-determinator/seo.astro +15 -0
  217. package/src/tool/forensic-stature-estimator/bibliography.astro +14 -0
  218. package/src/tool/forensic-stature-estimator/bibliography.ts +12 -0
  219. package/src/tool/forensic-stature-estimator/component.astro +49 -0
  220. package/src/tool/forensic-stature-estimator/components/EstimationPanel.astro +65 -0
  221. package/src/tool/forensic-stature-estimator/components/OsteometricBoard.astro +39 -0
  222. package/src/tool/forensic-stature-estimator/components/OsteometricSelector.astro +109 -0
  223. package/src/tool/forensic-stature-estimator/dom-utils.ts +71 -0
  224. package/src/tool/forensic-stature-estimator/entry.ts +32 -0
  225. package/src/tool/forensic-stature-estimator/forensic-stature-estimator.css +689 -0
  226. package/src/tool/forensic-stature-estimator/helpers.ts +51 -0
  227. package/src/tool/forensic-stature-estimator/i18n/de.ts +196 -0
  228. package/src/tool/forensic-stature-estimator/i18n/en.ts +196 -0
  229. package/src/tool/forensic-stature-estimator/i18n/es.ts +196 -0
  230. package/src/tool/forensic-stature-estimator/i18n/fr.ts +196 -0
  231. package/src/tool/forensic-stature-estimator/i18n/id.ts +196 -0
  232. package/src/tool/forensic-stature-estimator/i18n/it.ts +196 -0
  233. package/src/tool/forensic-stature-estimator/i18n/ja.ts +196 -0
  234. package/src/tool/forensic-stature-estimator/i18n/ko.ts +196 -0
  235. package/src/tool/forensic-stature-estimator/i18n/nl.ts +196 -0
  236. package/src/tool/forensic-stature-estimator/i18n/pl.ts +196 -0
  237. package/src/tool/forensic-stature-estimator/i18n/pt.ts +196 -0
  238. package/src/tool/forensic-stature-estimator/i18n/ru.ts +196 -0
  239. package/src/tool/forensic-stature-estimator/i18n/sv.ts +196 -0
  240. package/src/tool/forensic-stature-estimator/i18n/tr.ts +196 -0
  241. package/src/tool/forensic-stature-estimator/i18n/zh.ts +196 -0
  242. package/src/tool/forensic-stature-estimator/index.ts +11 -0
  243. package/src/tool/forensic-stature-estimator/logic.ts +119 -0
  244. package/src/tool/forensic-stature-estimator/script.ts +288 -0
  245. package/src/tool/forensic-stature-estimator/seo.astro +15 -0
  246. package/src/tool/forensic-tlc-ink-simulator/bibliography.astro +6 -0
  247. package/src/tool/forensic-tlc-ink-simulator/bibliography.ts +16 -0
  248. package/src/tool/forensic-tlc-ink-simulator/component.astro +245 -0
  249. package/src/tool/forensic-tlc-ink-simulator/entry.ts +32 -0
  250. package/src/tool/forensic-tlc-ink-simulator/forensic-tlc-ink-simulator.css +462 -0
  251. package/src/tool/forensic-tlc-ink-simulator/i18n/de.ts +243 -0
  252. package/src/tool/forensic-tlc-ink-simulator/i18n/en.ts +243 -0
  253. package/src/tool/forensic-tlc-ink-simulator/i18n/es.ts +243 -0
  254. package/src/tool/forensic-tlc-ink-simulator/i18n/fr.ts +243 -0
  255. package/src/tool/forensic-tlc-ink-simulator/i18n/id.ts +243 -0
  256. package/src/tool/forensic-tlc-ink-simulator/i18n/it.ts +243 -0
  257. package/src/tool/forensic-tlc-ink-simulator/i18n/ja.ts +235 -0
  258. package/src/tool/forensic-tlc-ink-simulator/i18n/ko.ts +235 -0
  259. package/src/tool/forensic-tlc-ink-simulator/i18n/nl.ts +243 -0
  260. package/src/tool/forensic-tlc-ink-simulator/i18n/pl.ts +243 -0
  261. package/src/tool/forensic-tlc-ink-simulator/i18n/pt.ts +243 -0
  262. package/src/tool/forensic-tlc-ink-simulator/i18n/ru.ts +243 -0
  263. package/src/tool/forensic-tlc-ink-simulator/i18n/sv.ts +243 -0
  264. package/src/tool/forensic-tlc-ink-simulator/i18n/tr.ts +243 -0
  265. package/src/tool/forensic-tlc-ink-simulator/i18n/zh.ts +235 -0
  266. package/src/tool/forensic-tlc-ink-simulator/index.ts +11 -0
  267. package/src/tool/forensic-tlc-ink-simulator/logic.ts +152 -0
  268. package/src/tool/forensic-tlc-ink-simulator/seo.astro +15 -0
  269. package/src/tool/gsr-dispersion-calculator/bibliography.astro +6 -0
  270. package/src/tool/gsr-dispersion-calculator/bibliography.ts +16 -0
  271. package/src/tool/gsr-dispersion-calculator/component.astro +294 -0
  272. package/src/tool/gsr-dispersion-calculator/entry.ts +32 -0
  273. package/src/tool/gsr-dispersion-calculator/gsr-dispersion-calculator.css +305 -0
  274. package/src/tool/gsr-dispersion-calculator/i18n/de.ts +272 -0
  275. package/src/tool/gsr-dispersion-calculator/i18n/en.ts +272 -0
  276. package/src/tool/gsr-dispersion-calculator/i18n/es.ts +272 -0
  277. package/src/tool/gsr-dispersion-calculator/i18n/fr.ts +272 -0
  278. package/src/tool/gsr-dispersion-calculator/i18n/id.ts +272 -0
  279. package/src/tool/gsr-dispersion-calculator/i18n/it.ts +272 -0
  280. package/src/tool/gsr-dispersion-calculator/i18n/ja.ts +272 -0
  281. package/src/tool/gsr-dispersion-calculator/i18n/ko.ts +272 -0
  282. package/src/tool/gsr-dispersion-calculator/i18n/nl.ts +272 -0
  283. package/src/tool/gsr-dispersion-calculator/i18n/pl.ts +272 -0
  284. package/src/tool/gsr-dispersion-calculator/i18n/pt.ts +272 -0
  285. package/src/tool/gsr-dispersion-calculator/i18n/ru.ts +272 -0
  286. package/src/tool/gsr-dispersion-calculator/i18n/sv.ts +272 -0
  287. package/src/tool/gsr-dispersion-calculator/i18n/tr.ts +272 -0
  288. package/src/tool/gsr-dispersion-calculator/i18n/zh.ts +272 -0
  289. package/src/tool/gsr-dispersion-calculator/index.ts +11 -0
  290. package/src/tool/gsr-dispersion-calculator/logic.ts +148 -0
  291. package/src/tool/gsr-dispersion-calculator/seo.astro +15 -0
  292. package/src/tool/widmark-alcohol-simulator/bibliography.astro +14 -0
  293. package/src/tool/widmark-alcohol-simulator/bibliography.ts +12 -0
  294. package/src/tool/widmark-alcohol-simulator/component.astro +453 -0
  295. package/src/tool/widmark-alcohol-simulator/entry.ts +32 -0
  296. package/src/tool/widmark-alcohol-simulator/i18n/de.ts +193 -0
  297. package/src/tool/widmark-alcohol-simulator/i18n/en.ts +206 -0
  298. package/src/tool/widmark-alcohol-simulator/i18n/es.ts +193 -0
  299. package/src/tool/widmark-alcohol-simulator/i18n/fr.ts +193 -0
  300. package/src/tool/widmark-alcohol-simulator/i18n/id.ts +193 -0
  301. package/src/tool/widmark-alcohol-simulator/i18n/it.ts +193 -0
  302. package/src/tool/widmark-alcohol-simulator/i18n/ja.ts +193 -0
  303. package/src/tool/widmark-alcohol-simulator/i18n/ko.ts +193 -0
  304. package/src/tool/widmark-alcohol-simulator/i18n/nl.ts +193 -0
  305. package/src/tool/widmark-alcohol-simulator/i18n/pl.ts +193 -0
  306. package/src/tool/widmark-alcohol-simulator/i18n/pt.ts +193 -0
  307. package/src/tool/widmark-alcohol-simulator/i18n/ru.ts +193 -0
  308. package/src/tool/widmark-alcohol-simulator/i18n/sv.ts +193 -0
  309. package/src/tool/widmark-alcohol-simulator/i18n/tr.ts +193 -0
  310. package/src/tool/widmark-alcohol-simulator/i18n/zh.ts +193 -0
  311. package/src/tool/widmark-alcohol-simulator/index.ts +11 -0
  312. package/src/tool/widmark-alcohol-simulator/logic.ts +97 -0
  313. package/src/tool/widmark-alcohol-simulator/seo.astro +15 -0
  314. package/src/tool/widmark-alcohol-simulator/widmark-alcohol-simulator.css +386 -0
  315. package/src/tools.ts +27 -0
  316. package/src/types.ts +70 -0
@@ -0,0 +1,12 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'SWGDRUG Recommendations. Scientific Working Group for the Analysis of Seized Drugs.',
6
+ url: 'https://www.swgdrug.org/Documents/SWGDRUG%20Recommendations%20Version%208_FINAL_ForPosting_092919.pdf',
7
+ },
8
+ {
9
+ name: 'United Nations Office on Drugs and Crime. Recommended Methods for the Identification and Analysis of Cocaine in Seized Materials.',
10
+ url: 'https://www.unodc.org/documents/scientific/Cocaine_Manual_Rev_1.pdf',
11
+ }
12
+ ];
@@ -0,0 +1,240 @@
1
+ ---
2
+ import './forensic-microcrystal-drug-simulator.css';
3
+
4
+ interface Props {
5
+ ui: Record<string, string>;
6
+ }
7
+
8
+ const { ui } = Astro.props;
9
+ ---
10
+
11
+ <section class="microcrystal-shell" data-microcrystal-tool>
12
+ <div class="microcrystal-card">
13
+ <div class="microcrystal-stage">
14
+ <div class="microcrystal-toolbar">
15
+ <span>{ui.microscope}</span>
16
+ <strong id="microcrystal-match">-</strong>
17
+ </div>
18
+ <div class="microcrystal-lens" aria-label={ui.canvasAria}>
19
+ <canvas id="microcrystal-canvas" width="760" height="560"></canvas>
20
+ <div class="microcrystal-reticle" aria-hidden="true"></div>
21
+ </div>
22
+ <div class="microcrystal-readout">
23
+ <span id="microcrystal-habit">-</span>
24
+ <span id="microcrystal-cue">-</span>
25
+ </div>
26
+ </div>
27
+
28
+ <aside class="microcrystal-controls">
29
+ <label class="microcrystal-sample-field">
30
+ <span>{ui.sample}</span>
31
+ <span class="microcrystal-select">
32
+ <select id="microcrystal-sample">
33
+ <option value="cocaine">{ui.sampleCocaine}</option>
34
+ <option value="heroin">{ui.sampleHeroin}</option>
35
+ <option value="amphetamine">{ui.sampleAmphetamine}</option>
36
+ <option value="methamphetamine">{ui.sampleMethamphetamine}</option>
37
+ </select>
38
+ </span>
39
+ </label>
40
+
41
+ <div class="microcrystal-reagents" role="group" aria-label={ui.reagent}>
42
+ <button type="button" data-reagent="platinumChloride" data-active="true">
43
+ <span></span>
44
+ {ui.reagentPlatinum}
45
+ </button>
46
+ <button type="button" data-reagent="goldChloride" data-active="false">
47
+ <span></span>
48
+ {ui.reagentGold}
49
+ </button>
50
+ <button type="button" data-reagent="dragendorff" data-active="false">
51
+ <span></span>
52
+ {ui.reagentDragendorff}
53
+ </button>
54
+ </div>
55
+
56
+ <label>
57
+ <span>{ui.polarization}</span>
58
+ <input id="microcrystal-polarization" type="range" min="0" max="100" value="72" />
59
+ </label>
60
+
61
+ <div class="microcrystal-metrics">
62
+ <div>
63
+ <span>{ui.confidence}</span>
64
+ <strong id="microcrystal-confidence">-</strong>
65
+ <i id="microcrystal-confidence-bar"></i>
66
+ </div>
67
+ <div>
68
+ <span>{ui.growth}</span>
69
+ <strong id="microcrystal-growth">-</strong>
70
+ <i id="microcrystal-growth-bar"></i>
71
+ </div>
72
+ <div>
73
+ <span>{ui.birefringence}</span>
74
+ <strong id="microcrystal-birefringence">-</strong>
75
+ <i id="microcrystal-birefringence-bar"></i>
76
+ </div>
77
+ </div>
78
+
79
+ <button class="microcrystal-grow" type="button" id="microcrystal-grow">{ui.regrow}</button>
80
+ <aside class="microcrystal-warning" aria-label={ui.warningLabel}>
81
+ <strong>{ui.warningTitle}</strong>
82
+ <p>{ui.disclaimer}</p>
83
+ </aside>
84
+ </aside>
85
+ </div>
86
+ </section>
87
+
88
+ <script id="microcrystal-ui" type="application/json" set:html={JSON.stringify(ui)}></script>
89
+ <script>
90
+ import { CrystallographyEngine } from './logic';
91
+ import type { CrystalHabit, CrystalReagent, CrystalSeed, DrugSample } from './logic';
92
+
93
+ const engine = new CrystallographyEngine();
94
+ const ui = JSON.parse(document.getElementById('microcrystal-ui')?.textContent || '{}');
95
+ const canvas = document.getElementById('microcrystal-canvas') as HTMLCanvasElement | null;
96
+ const context = canvas?.getContext('2d');
97
+ const sampleSelect = document.getElementById('microcrystal-sample') as HTMLSelectElement | null;
98
+ const polarizationInput = document.getElementById('microcrystal-polarization') as HTMLInputElement | null;
99
+ let reagent: CrystalReagent = 'platinumChloride';
100
+ let growthStart = performance.now();
101
+
102
+ function setText(id: string, value: string): void {
103
+ const element = document.getElementById(id);
104
+ if (element) element.textContent = value;
105
+ }
106
+
107
+ function setMeter(id: string, value: number): void {
108
+ const element = document.getElementById(id);
109
+ if (element) element.style.setProperty('--value', `${Math.max(0, Math.min(100, value))}%`);
110
+ }
111
+
112
+ function habitLabel(habit: CrystalHabit): string {
113
+ return ui[`habit${habit.charAt(0).toUpperCase()}${habit.slice(1)}`] ?? habit;
114
+ }
115
+
116
+ function matchLabel(match: string): string {
117
+ if (match === 'reference-like') return ui.matchReference;
118
+ if (match === 'partial') return ui.matchPartial;
119
+ return ui.matchNonSpecific;
120
+ }
121
+
122
+ function drawBackground(isDark: boolean, polarization: number): void {
123
+ if (!canvas || !context) return;
124
+ context.fillStyle = isDark ? '#10262c' : '#edf4f1';
125
+ context.fillRect(0, 0, canvas.width, canvas.height);
126
+ context.globalAlpha = isDark ? 0.05 + polarization / 900 : 0.025 + polarization / 1400;
127
+ context.strokeStyle = isDark ? '#f5fbff' : '#4f7a74';
128
+ context.lineWidth = isDark ? 1 : 0.8;
129
+ for (let x = -140; x < canvas.width; x += 38) {
130
+ context.beginPath();
131
+ context.moveTo(x, 0);
132
+ context.lineTo(x + 210, canvas.height);
133
+ context.stroke();
134
+ }
135
+ context.globalAlpha = 1;
136
+ }
137
+
138
+ function drawNeedle(seed: CrystalSeed, progress: number, hue: number, polarization: number): void {
139
+ if (!context) return;
140
+ const x = seed.x * 7.6;
141
+ const y = seed.y * 5.6;
142
+ const length = seed.length * progress;
143
+ const glow = 0.35 + polarization / 180;
144
+ context.save();
145
+ context.translate(x, y);
146
+ context.rotate(seed.angle);
147
+ context.strokeStyle = `hsla(${hue}, 88%, ${56 + polarization * 0.18}%, ${glow})`;
148
+ context.lineWidth = seed.width;
149
+ context.beginPath();
150
+ context.moveTo(0, 0);
151
+ context.lineTo(length, 0);
152
+ context.moveTo(length * 0.35, 0);
153
+ context.lineTo(length * 0.72, length * 0.22 * seed.branch);
154
+ context.moveTo(length * 0.42, 0);
155
+ context.lineTo(length * 0.82, -length * 0.2 * seed.branch);
156
+ context.stroke();
157
+ context.restore();
158
+ }
159
+
160
+ function drawRosette(seed: CrystalSeed, progress: number, hue: number, polarization: number): void {
161
+ for (let spoke = 0; spoke < 9; spoke++) {
162
+ drawNeedle({ ...seed, angle: seed.angle + spoke * 0.7, length: seed.length * 0.62 }, progress, hue + spoke * 7, polarization);
163
+ }
164
+ }
165
+
166
+ function drawPlate(seed: CrystalSeed, progress: number, hue: number, polarization: number): void {
167
+ if (!context) return;
168
+ const radius = seed.length * progress * 0.42;
169
+ context.save();
170
+ context.translate(seed.x * 7.6, seed.y * 5.6);
171
+ context.rotate(seed.angle);
172
+ context.fillStyle = `hsla(${hue}, 74%, ${60 + polarization * 0.15}%, 0.38)`;
173
+ context.strokeStyle = `hsla(${hue + 18}, 92%, 74%, 0.72)`;
174
+ context.lineWidth = 1.4;
175
+ context.beginPath();
176
+ for (let side = 0; side < 6; side++) {
177
+ const angle = side * Math.PI / 3;
178
+ const x = Math.cos(angle) * radius;
179
+ const y = Math.sin(angle) * radius * 0.62;
180
+ if (side === 0) context.moveTo(x, y);
181
+ else context.lineTo(x, y);
182
+ }
183
+ context.closePath();
184
+ context.fill();
185
+ context.stroke();
186
+ context.restore();
187
+ }
188
+
189
+ function drawPattern(result: { habit: CrystalHabit; seeds: CrystalSeed[]; hue: number }, progress: number, polarization: number): void {
190
+ const { habit, seeds, hue } = result;
191
+ seeds.forEach((seed, index) => {
192
+ const localProgress = Math.max(0, Math.min(1, progress - index / (seeds.length * 2.5)));
193
+ if (habit === 'rosettes') drawRosette(seed, localProgress, hue, polarization);
194
+ else if (habit === 'plates') drawPlate(seed, localProgress, hue, polarization);
195
+ else if (habit === 'weakScatter') drawPlate({ ...seed, length: seed.length * 0.42 }, localProgress, hue, polarization);
196
+ else drawNeedle(seed, localProgress, hue, polarization);
197
+ });
198
+ }
199
+
200
+ function render(): void {
201
+ if (!canvas || !context || !sampleSelect || !polarizationInput) return;
202
+ const sample = sampleSelect.value as DrugSample;
203
+ const polarization = Number(polarizationInput.value);
204
+ const result = engine.simulate(sample, reagent);
205
+ const progress = Math.min(1, (performance.now() - growthStart) / (1800 - result.growthRate * 9));
206
+ const isDark = document.documentElement.classList.contains('theme-dark');
207
+ drawBackground(isDark, polarization);
208
+ drawPattern(result, progress, polarization);
209
+ setText('microcrystal-match', matchLabel(result.matchLabel));
210
+ setText('microcrystal-habit', habitLabel(result.habit));
211
+ setText('microcrystal-cue', ui[result.cueKey] || '');
212
+ setText('microcrystal-confidence', `${result.confidence}%`);
213
+ setText('microcrystal-growth', `${result.growthRate}%`);
214
+ setText('microcrystal-birefringence', `${result.birefringence}%`);
215
+ setMeter('microcrystal-confidence-bar', result.confidence);
216
+ setMeter('microcrystal-growth-bar', result.growthRate);
217
+ setMeter('microcrystal-birefringence-bar', result.birefringence);
218
+ if (progress < 1) requestAnimationFrame(render);
219
+ }
220
+
221
+ function restart(): void {
222
+ growthStart = performance.now();
223
+ render();
224
+ }
225
+
226
+ sampleSelect?.addEventListener('change', restart);
227
+ polarizationInput?.addEventListener('input', render);
228
+ document.getElementById('microcrystal-grow')?.addEventListener('click', restart);
229
+ document.querySelectorAll<HTMLButtonElement>('[data-reagent]').forEach((button) => {
230
+ button.addEventListener('click', () => {
231
+ reagent = (button.dataset.reagent ?? 'platinumChloride') as CrystalReagent;
232
+ document.querySelectorAll<HTMLButtonElement>('[data-reagent]').forEach((item) => {
233
+ item.dataset.active = item === button ? 'true' : 'false';
234
+ });
235
+ restart();
236
+ });
237
+ });
238
+ new MutationObserver(render).observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
239
+ restart();
240
+ </script>
@@ -0,0 +1,32 @@
1
+ import type { ScienceToolEntry, ToolLocaleContent } from '../../types';
2
+
3
+ export interface MicrocrystalDrugSimulatorUI {
4
+ [key: string]: string;
5
+ }
6
+
7
+ export type MicrocrystalDrugSimulatorLocaleContent = ToolLocaleContent<MicrocrystalDrugSimulatorUI>;
8
+
9
+ export const forensicMicrocrystalDrugSimulator: ScienceToolEntry<MicrocrystalDrugSimulatorUI> = {
10
+ id: 'forensic-microcrystal-drug-simulator',
11
+ icons: {
12
+ bg: 'mdi:microscope',
13
+ fg: 'mdi:crystal-ball',
14
+ },
15
+ i18n: {
16
+ en: () => import('./i18n/en').then((m) => m.content),
17
+ de: () => import('./i18n/de').then((m) => m.content),
18
+ es: () => import('./i18n/es').then((m) => m.content),
19
+ fr: () => import('./i18n/fr').then((m) => m.content),
20
+ id: () => import('./i18n/id').then((m) => m.content),
21
+ it: () => import('./i18n/it').then((m) => m.content),
22
+ ja: () => import('./i18n/ja').then((m) => m.content),
23
+ ko: () => import('./i18n/ko').then((m) => m.content),
24
+ nl: () => import('./i18n/nl').then((m) => m.content),
25
+ pl: () => import('./i18n/pl').then((m) => m.content),
26
+ pt: () => import('./i18n/pt').then((m) => m.content),
27
+ ru: () => import('./i18n/ru').then((m) => m.content),
28
+ sv: () => import('./i18n/sv').then((m) => m.content),
29
+ tr: () => import('./i18n/tr').then((m) => m.content),
30
+ zh: () => import('./i18n/zh').then((m) => m.content),
31
+ },
32
+ };
@@ -0,0 +1,430 @@
1
+ .microcrystal-shell {
2
+ --ink: #25302f;
3
+ --muted: #6d7875;
4
+ --card: #fff;
5
+ --panel: #f4f7f5;
6
+ --control: #fff;
7
+ --control-strong: #fff;
8
+ --border: rgba(37, 48, 47, 0.12);
9
+ --border-soft: rgba(37, 48, 47, 0.08);
10
+ --accent: #4f8f88;
11
+ --accent-soft: #dcebe8;
12
+ --amber: #b88a36;
13
+ --rose: #b56b82;
14
+ --stage: #eef3f1;
15
+ --stage-ink: #25302f;
16
+ --shadow: rgba(37, 48, 47, 0.08);
17
+
18
+ color: var(--ink);
19
+ padding: 0.75rem;
20
+ width: 100%;
21
+ }
22
+
23
+ .microcrystal-card {
24
+ background: var(--card);
25
+ border: 1px solid var(--border);
26
+ border-radius: 8px;
27
+ box-shadow: 0 14px 34px var(--shadow);
28
+ display: grid;
29
+ gap: 0.75rem;
30
+ overflow: hidden;
31
+ padding: 0.75rem;
32
+ }
33
+
34
+ .microcrystal-stage,
35
+ .microcrystal-controls {
36
+ border: 1px solid var(--border);
37
+ border-radius: 8px;
38
+ overflow: hidden;
39
+ }
40
+
41
+ .microcrystal-stage {
42
+ background: var(--stage);
43
+ }
44
+
45
+ .microcrystal-toolbar,
46
+ .microcrystal-readout {
47
+ align-items: center;
48
+ color: var(--stage-ink);
49
+ display: flex;
50
+ gap: 0.75rem;
51
+ justify-content: space-between;
52
+ padding: 0.82rem 0.9rem;
53
+ }
54
+
55
+ .microcrystal-toolbar span,
56
+ .microcrystal-toolbar strong,
57
+ .microcrystal-controls span,
58
+ .microcrystal-metrics span {
59
+ font-size: 0.76rem;
60
+ font-weight: 800;
61
+ text-transform: uppercase;
62
+ }
63
+
64
+ .microcrystal-toolbar strong {
65
+ color: var(--accent);
66
+ }
67
+
68
+ .microcrystal-lens {
69
+ aspect-ratio: 1.25;
70
+ display: grid;
71
+ overflow: hidden;
72
+ position: relative;
73
+ }
74
+
75
+ #microcrystal-canvas,
76
+ .microcrystal-reticle {
77
+ grid-area: 1 / 1;
78
+ }
79
+
80
+ #microcrystal-canvas {
81
+ height: 100%;
82
+ width: 100%;
83
+ }
84
+
85
+ .microcrystal-reticle {
86
+ border: 8px solid rgba(55, 68, 67, 0.16);
87
+ border-radius: 50%;
88
+ margin: 1rem;
89
+ pointer-events: none;
90
+ }
91
+
92
+ .microcrystal-reticle::before,
93
+ .microcrystal-reticle::after {
94
+ background: rgba(55, 68, 67, 0.1);
95
+ content: "";
96
+ left: 50%;
97
+ position: absolute;
98
+ top: 50%;
99
+ transform: translate(-50%, -50%);
100
+ }
101
+
102
+ .microcrystal-reticle::before {
103
+ height: 1px;
104
+ width: 100%;
105
+ }
106
+
107
+ .microcrystal-reticle::after {
108
+ height: 100%;
109
+ width: 1px;
110
+ }
111
+
112
+ .microcrystal-readout {
113
+ align-items: flex-start;
114
+ background: var(--stage);
115
+ border-top: 1px solid var(--border-soft);
116
+ display: grid;
117
+ }
118
+
119
+ .microcrystal-readout span:first-child {
120
+ color: var(--accent);
121
+ font-weight: 900;
122
+ }
123
+
124
+ .microcrystal-readout span:last-child,
125
+ .microcrystal-controls p {
126
+ color: var(--muted);
127
+ font-size: 0.9rem;
128
+ line-height: 1.45;
129
+ margin: 0;
130
+ }
131
+
132
+ .microcrystal-controls {
133
+ background: var(--panel);
134
+ display: grid;
135
+ gap: 0.75rem;
136
+ padding: 0.75rem;
137
+ }
138
+
139
+ .microcrystal-controls label {
140
+ display: grid;
141
+ gap: 0.5rem;
142
+ }
143
+
144
+ .microcrystal-controls input {
145
+ accent-color: var(--accent);
146
+ width: 100%;
147
+ }
148
+
149
+ .microcrystal-sample-field {
150
+ display: grid;
151
+ gap: 0.5rem;
152
+ }
153
+
154
+ .microcrystal-select {
155
+ background: var(--control);
156
+ border: 1px solid var(--border);
157
+ border-radius: 8px;
158
+ display: grid;
159
+ min-height: 58px;
160
+ position: relative;
161
+ }
162
+
163
+ .microcrystal-select::before {
164
+ background: var(--accent-soft);
165
+ border-radius: 999px;
166
+ content: "";
167
+ height: 28px;
168
+ position: absolute;
169
+ right: 0.65rem;
170
+ top: calc(50% - 14px);
171
+ width: 28px;
172
+ }
173
+
174
+ .microcrystal-select::after {
175
+ border-bottom: 2px solid var(--accent);
176
+ border-right: 2px solid var(--accent);
177
+ content: "";
178
+ height: 7px;
179
+ pointer-events: none;
180
+ position: absolute;
181
+ right: 1.24rem;
182
+ top: calc(50% - 5px);
183
+ transform: rotate(45deg);
184
+ width: 7px;
185
+ }
186
+
187
+ .microcrystal-select select {
188
+ appearance: none;
189
+ background: var(--control);
190
+ border: 0;
191
+ color: var(--ink);
192
+ cursor: pointer;
193
+ font-size: inherit;
194
+ font-weight: 800;
195
+ line-height: inherit;
196
+ min-height: 56px;
197
+ padding: 0.75rem 3.1rem 0.75rem 0.85rem;
198
+ width: 100%;
199
+ }
200
+
201
+ .microcrystal-select option {
202
+ background: var(--control);
203
+ color: var(--ink);
204
+ }
205
+
206
+ .microcrystal-select:focus-within {
207
+ border-color: rgba(79, 143, 136, 0.62);
208
+ box-shadow: 0 0 0 3px rgba(79, 143, 136, 0.14);
209
+ }
210
+
211
+ .microcrystal-reagents {
212
+ display: grid;
213
+ gap: 0.5rem;
214
+ }
215
+
216
+ .microcrystal-reagents button,
217
+ .microcrystal-grow {
218
+ align-items: center;
219
+ background: var(--control-strong);
220
+ border: 1px solid var(--border);
221
+ border-radius: 6px;
222
+ color: var(--ink);
223
+ cursor: pointer;
224
+ display: flex;
225
+ font-weight: 800;
226
+ gap: 0.6rem;
227
+ justify-content: flex-start;
228
+ min-height: 52px;
229
+ padding: 0.6rem 0.7rem;
230
+ transition: background 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease;
231
+ }
232
+
233
+ .microcrystal-reagents button[data-active="true"] {
234
+ background: var(--accent-soft);
235
+ border-color: rgba(79, 143, 136, 0.44);
236
+ color: var(--ink);
237
+ transform: translateY(-1px);
238
+ }
239
+
240
+ .microcrystal-reagents span {
241
+ border-radius: 999px;
242
+ display: inline-block;
243
+ height: 18px;
244
+ width: 18px;
245
+ }
246
+
247
+ .microcrystal-reagents button:nth-child(1) span {
248
+ background: var(--accent);
249
+ }
250
+
251
+ .microcrystal-reagents button:nth-child(2) span {
252
+ background: var(--amber);
253
+ }
254
+
255
+ .microcrystal-reagents button:nth-child(3) span {
256
+ background: var(--rose);
257
+ }
258
+
259
+ .microcrystal-metrics {
260
+ display: grid;
261
+ gap: 0.5rem;
262
+ grid-template-columns: repeat(3, minmax(0, 1fr));
263
+ }
264
+
265
+ .microcrystal-metrics div {
266
+ background: var(--control);
267
+ border: 1px solid var(--border-soft);
268
+ border-radius: 8px;
269
+ display: grid;
270
+ gap: 0.45rem;
271
+ min-height: 96px;
272
+ overflow: hidden;
273
+ padding: 0.7rem;
274
+ position: relative;
275
+ }
276
+
277
+ .microcrystal-metrics div::before {
278
+ background: var(--accent-soft);
279
+ border-radius: 999px;
280
+ content: "";
281
+ height: 38px;
282
+ opacity: 0.8;
283
+ position: absolute;
284
+ right: -14px;
285
+ top: -16px;
286
+ width: 38px;
287
+ }
288
+
289
+ .microcrystal-metrics span,
290
+ .microcrystal-metrics strong,
291
+ .microcrystal-metrics i {
292
+ position: relative;
293
+ z-index: 1;
294
+ }
295
+
296
+ .microcrystal-metrics span {
297
+ color: var(--muted);
298
+ }
299
+
300
+ .microcrystal-metrics strong {
301
+ color: var(--ink);
302
+ font-size: 1.55rem;
303
+ line-height: 1;
304
+ }
305
+
306
+ .microcrystal-metrics i {
307
+ --value: 0%;
308
+
309
+ background: rgba(37, 48, 47, 0.09);
310
+ border-radius: 999px;
311
+ display: block;
312
+ height: 7px;
313
+ overflow: hidden;
314
+ }
315
+
316
+ .microcrystal-metrics i::before {
317
+ background: var(--accent);
318
+ border-radius: inherit;
319
+ content: "";
320
+ display: block;
321
+ height: 100%;
322
+ width: var(--value);
323
+ }
324
+
325
+ .microcrystal-metrics div:nth-child(2)::before {
326
+ background: rgba(184, 138, 54, 0.18);
327
+ }
328
+
329
+ .microcrystal-metrics div:nth-child(2) i::before {
330
+ background: var(--amber);
331
+ }
332
+
333
+ .microcrystal-metrics div:nth-child(3)::before {
334
+ background: rgba(181, 107, 130, 0.18);
335
+ }
336
+
337
+ .microcrystal-metrics div:nth-child(3) i::before {
338
+ background: var(--rose);
339
+ }
340
+
341
+ .microcrystal-grow {
342
+ background: var(--accent);
343
+ border-color: var(--accent);
344
+ color: #fff;
345
+ justify-content: center;
346
+ min-height: 56px;
347
+ }
348
+
349
+ .microcrystal-warning {
350
+ background: #fffaf0;
351
+ border: 1px solid rgba(184, 138, 54, 0.28);
352
+ border-left: 5px solid var(--amber);
353
+ border-radius: 8px;
354
+ display: grid;
355
+ gap: 0.35rem;
356
+ padding: 0.75rem 0.85rem;
357
+ }
358
+
359
+ .microcrystal-warning strong {
360
+ color: #7c5b1c;
361
+ font-size: 0.78rem;
362
+ font-weight: 900;
363
+ text-transform: uppercase;
364
+ }
365
+
366
+ .microcrystal-warning p {
367
+ color: #5f594d;
368
+ margin: 0;
369
+ }
370
+
371
+ .theme-dark .microcrystal-shell {
372
+ --ink: #eef8f7;
373
+ --muted: #a5b6b4;
374
+ --card: #10191d;
375
+ --panel: #142429;
376
+ --control: #1b3035;
377
+ --control-strong: #1b3035;
378
+ --border: rgba(238, 248, 247, 0.14);
379
+ --border-soft: rgba(238, 248, 247, 0.09);
380
+ --accent: #45bcc1;
381
+ --accent-soft: #1f3c42;
382
+ --amber: #e1a843;
383
+ --rose: #e06b95;
384
+ --stage: #0d2224;
385
+ --stage-ink: #eef8f7;
386
+ --shadow: rgba(0, 0, 0, 0.34);
387
+ }
388
+
389
+ .theme-dark .microcrystal-card,
390
+ .theme-dark .microcrystal-controls {
391
+ background: var(--card);
392
+ }
393
+
394
+ .theme-dark .microcrystal-select,
395
+ .theme-dark .microcrystal-metrics div {
396
+ background: var(--control);
397
+ }
398
+
399
+ .theme-dark .microcrystal-warning {
400
+ background: #211d12;
401
+ border-color: rgba(225, 168, 67, 0.28);
402
+ border-left-color: var(--amber);
403
+ }
404
+
405
+ .theme-dark .microcrystal-warning strong {
406
+ color: #e8c46a;
407
+ }
408
+
409
+ .theme-dark .microcrystal-warning p {
410
+ color: var(--muted);
411
+ }
412
+
413
+ .theme-dark .microcrystal-reticle {
414
+ border-color: rgba(1, 10, 13, 0.22);
415
+ }
416
+
417
+ .theme-dark .microcrystal-reticle::before,
418
+ .theme-dark .microcrystal-reticle::after {
419
+ background: rgba(255, 255, 255, 0.14);
420
+ }
421
+
422
+ @media (min-width: 900px) {
423
+ .microcrystal-shell {
424
+ padding: 1rem;
425
+ }
426
+
427
+ .microcrystal-card {
428
+ grid-template-columns: minmax(520px, 1fr) minmax(300px, 0.34fr);
429
+ }
430
+ }