@jjlmoya/utils-astronomy 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 (57) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +57 -0
  3. package/src/category/i18n/es.ts +57 -0
  4. package/src/category/i18n/fr.ts +58 -0
  5. package/src/category/index.ts +16 -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 +19 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +22 -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 +24 -0
  17. package/src/tests/mocks/astro_mock.js +2 -0
  18. package/src/tests/seo_length.test.ts +57 -0
  19. package/src/tests/tool_validation.test.ts +145 -0
  20. package/src/tool/bortleVisualizer/bibliography.astro +14 -0
  21. package/src/tool/bortleVisualizer/component.astro +491 -0
  22. package/src/tool/bortleVisualizer/i18n/en.ts +153 -0
  23. package/src/tool/bortleVisualizer/i18n/es.ts +161 -0
  24. package/src/tool/bortleVisualizer/i18n/fr.ts +153 -0
  25. package/src/tool/bortleVisualizer/index.ts +41 -0
  26. package/src/tool/bortleVisualizer/logic.ts +118 -0
  27. package/src/tool/bortleVisualizer/seo.astro +61 -0
  28. package/src/tool/bortleVisualizer/style.css +5 -0
  29. package/src/tool/deepSpaceScope/bibliography.astro +14 -0
  30. package/src/tool/deepSpaceScope/component.astro +849 -0
  31. package/src/tool/deepSpaceScope/i18n/en.ts +157 -0
  32. package/src/tool/deepSpaceScope/i18n/es.ts +157 -0
  33. package/src/tool/deepSpaceScope/i18n/fr.ts +157 -0
  34. package/src/tool/deepSpaceScope/index.ts +48 -0
  35. package/src/tool/deepSpaceScope/logic.ts +41 -0
  36. package/src/tool/deepSpaceScope/seo.astro +61 -0
  37. package/src/tool/deepSpaceScope/style.css +5 -0
  38. package/src/tool/starExposureCalculator/bibliography.astro +14 -0
  39. package/src/tool/starExposureCalculator/component.astro +562 -0
  40. package/src/tool/starExposureCalculator/i18n/en.ts +163 -0
  41. package/src/tool/starExposureCalculator/i18n/es.ts +163 -0
  42. package/src/tool/starExposureCalculator/i18n/fr.ts +158 -0
  43. package/src/tool/starExposureCalculator/index.ts +53 -0
  44. package/src/tool/starExposureCalculator/logic.ts +49 -0
  45. package/src/tool/starExposureCalculator/seo.astro +61 -0
  46. package/src/tool/starExposureCalculator/style.css +5 -0
  47. package/src/tool/telescopeResolution/bibliography.astro +14 -0
  48. package/src/tool/telescopeResolution/component.astro +556 -0
  49. package/src/tool/telescopeResolution/i18n/en.ts +168 -0
  50. package/src/tool/telescopeResolution/i18n/es.ts +163 -0
  51. package/src/tool/telescopeResolution/i18n/fr.ts +168 -0
  52. package/src/tool/telescopeResolution/index.ts +52 -0
  53. package/src/tool/telescopeResolution/logic.ts +39 -0
  54. package/src/tool/telescopeResolution/seo.astro +61 -0
  55. package/src/tool/telescopeResolution/style.css +5 -0
  56. package/src/tools.ts +19 -0
  57. package/src/types.ts +71 -0
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { telescopeResolution } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await telescopeResolution.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,556 @@
1
+ ---
2
+ import type { TelescopeResolutionUI } from './index';
3
+ import type { KnownLocale } from '../../types';
4
+
5
+ interface Props {
6
+ ui: TelescopeResolutionUI;
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ ---
12
+
13
+ <div class="tscope-wrapper">
14
+ <div class="tscope-grid">
15
+ <div class="tscope-controls">
16
+ <div class="tscope-control-group">
17
+ <label class="tscope-control-label">{ui.apertureLabel}</label>
18
+ <div class="tscope-stepper">
19
+ <button class="tscope-step-btn" data-action="dec" data-target="aperture">-</button>
20
+ <input type="number" id="aperture-input" value="150" min="1" max="2000" class="tscope-stepper-input" />
21
+ <button class="tscope-step-btn" data-action="inc" data-target="aperture">+</button>
22
+ </div>
23
+ </div>
24
+
25
+ <div class="tscope-control-group">
26
+ <label class="tscope-control-label">{ui.unitLabel}</label>
27
+ <div class="tscope-select-wrapper">
28
+ <select id="unit-selector" class="tscope-select">
29
+ <option value="mm">{ui.mmUnit}</option>
30
+ <option value="in">{ui.inUnit}</option>
31
+ </select>
32
+ <svg class="tscope-select-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
33
+ <polyline points="6 9 12 15 18 9"></polyline>
34
+ </svg>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="tscope-control-group">
39
+ <label class="tscope-control-label">{ui.presetsLabel}</label>
40
+ <div class="tscope-select-wrapper">
41
+ <select id="preset-selector" class="tscope-select">
42
+ <option value="">{ui.presetsPlaceholder}</option>
43
+ <option value="70">70mm (Refractor - Iniciación)</option>
44
+ <option value="114">114mm (4.5") Newtonian</option>
45
+ <option value="150">150mm (6") Dobson</option>
46
+ <option value="200">200mm (8") Dobson</option>
47
+ <option value="254">254mm (10") Dobson</option>
48
+ <option value="304">304mm (12") Profesional</option>
49
+ </select>
50
+ <svg class="tscope-select-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
51
+ <polyline points="6 9 12 15 18 9"></polyline>
52
+ </svg>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="tscope-control-group">
57
+ <label class="tscope-control-label">
58
+ {ui.seeingLabel}: <span id="seeing-value">1.0</span>"
59
+ </label>
60
+ <input type="range" id="seeing-input" min="0.5" max="3.0" step="0.1" value="1.0" class="tscope-range" aria-label={ui.seeingLabel} />
61
+ </div>
62
+ </div>
63
+
64
+ <div class="tscope-results">
65
+ <div class="tscope-main-result">
66
+ <h4 class="tscope-result-title">{ui.dawesLabel}</h4>
67
+ <div class="tscope-resolution-value">
68
+ <span id="dawes-result" class="tscope-resolution-number">0.77</span>
69
+ <span class="tscope-resolution-unit">{ui.arcsecUnit}</span>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="tscope-comp-grid">
74
+ <div class="tscope-comp-card">
75
+ <h4 class="tscope-comp-title">{ui.rayleighLabel}</h4>
76
+ <span class="tscope-comp-value" id="rayleigh-result">0.92</span>"
77
+ </div>
78
+ <div class="tscope-comp-card">
79
+ <h4 class="tscope-comp-title">{ui.maxMagLabel}</h4>
80
+ <span class="tscope-comp-value" id="max-mag-result">300</span>x
81
+ </div>
82
+ </div>
83
+
84
+ <div id="seeing-alert" class="tscope-seeing-alert">
85
+ {ui.seeingAlertPrefix} (1.0") {ui.seeingAlertSuffix} (0.77").
86
+ </div>
87
+
88
+ <div class="tscope-visualization">
89
+ <label class="tscope-vis-label">{ui.simLabel}</label>
90
+ <div class="tscope-sim-container">
91
+ <div class="tscope-binary-system" id="binary-system">
92
+ <div class="tscope-star" id="star-1"></div>
93
+ <div class="tscope-star" id="star-2"></div>
94
+ </div>
95
+ </div>
96
+ <p class="tscope-sim-explanation">{ui.simExplanation}</p>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <script>
103
+ import { calculateResolution, inchesToMm, mmToInches } from './logic';
104
+
105
+ class ResolutionApp {
106
+ apertureInput: HTMLInputElement;
107
+ unitSelector: HTMLSelectElement;
108
+ presetSelector: HTMLSelectElement;
109
+ seeingInput: HTMLInputElement;
110
+ dawesDisplay: HTMLElement;
111
+ rayleighDisplay: HTMLElement;
112
+ maxMagDisplay: HTMLElement;
113
+ seeingDisplay: HTMLElement;
114
+ seeingAlert: HTMLElement;
115
+ star1: HTMLElement;
116
+ star2: HTMLElement;
117
+ stepBtns: NodeListOf<HTMLButtonElement>;
118
+
119
+ constructor() {
120
+ this.apertureInput = document.getElementById('aperture-input') as HTMLInputElement;
121
+ this.unitSelector = document.getElementById('unit-selector') as HTMLSelectElement;
122
+ this.presetSelector = document.getElementById('preset-selector') as HTMLSelectElement;
123
+ this.seeingInput = document.getElementById('seeing-input') as HTMLInputElement;
124
+ this.dawesDisplay = document.getElementById('dawes-result') as HTMLElement;
125
+ this.rayleighDisplay = document.getElementById('rayleigh-result') as HTMLElement;
126
+ this.maxMagDisplay = document.getElementById('max-mag-result') as HTMLElement;
127
+ this.seeingDisplay = document.getElementById('seeing-value') as HTMLElement;
128
+ this.seeingAlert = document.getElementById('seeing-alert') as HTMLElement;
129
+ this.star1 = document.getElementById('star-1') as HTMLElement;
130
+ this.star2 = document.getElementById('star-2') as HTMLElement;
131
+ this.stepBtns = document.querySelectorAll('.tscope-step-btn');
132
+ this.init();
133
+ }
134
+
135
+ bindStepButtons() {
136
+ this.stepBtns.forEach((btn) => {
137
+ btn.addEventListener('click', () => {
138
+ const action = btn.dataset.action;
139
+ let val = parseFloat(this.apertureInput.value);
140
+ if (action === 'inc') val += this.unitSelector.value === 'mm' ? 10 : 0.5;
141
+ if (action === 'dec') val -= this.unitSelector.value === 'mm' ? 10 : 0.5;
142
+ this.apertureInput.value = Math.max(1, val).toString();
143
+ this.calculate();
144
+ });
145
+ });
146
+ }
147
+
148
+ bindUnitSelector() {
149
+ this.unitSelector.addEventListener('change', () => {
150
+ const val = parseFloat(this.apertureInput.value);
151
+ if (this.unitSelector.value === 'in') {
152
+ this.apertureInput.value = mmToInches(val).toFixed(1);
153
+ } else {
154
+ this.apertureInput.value = inchesToMm(val).toFixed(0);
155
+ }
156
+ this.calculate();
157
+ });
158
+ }
159
+
160
+ init() {
161
+ this.bindStepButtons();
162
+ this.apertureInput.addEventListener('input', () => this.calculate());
163
+ this.bindUnitSelector();
164
+ this.presetSelector.addEventListener('change', () => {
165
+ const val = this.presetSelector.value;
166
+ if (val) {
167
+ this.unitSelector.value = 'mm';
168
+ this.apertureInput.value = val;
169
+ this.calculate();
170
+ }
171
+ });
172
+ this.seeingInput.addEventListener('input', () => {
173
+ this.seeingDisplay.textContent = this.seeingInput.value;
174
+ this.calculate();
175
+ });
176
+ this.calculate();
177
+ }
178
+
179
+ calculate() {
180
+ let apertureMm = parseFloat(this.apertureInput.value) || 1;
181
+ if (this.unitSelector.value === 'in') {
182
+ apertureMm = inchesToMm(apertureMm);
183
+ }
184
+ const seeing = parseFloat(this.seeingInput.value);
185
+ const result = calculateResolution(apertureMm, seeing);
186
+
187
+ this.dawesDisplay.textContent = result.dawes.toFixed(2);
188
+ this.rayleighDisplay.textContent = result.rayleigh.toFixed(2);
189
+ this.maxMagDisplay.textContent = Math.round(result.maxMagnification).toString();
190
+
191
+ if (result.seeingLimited) {
192
+ this.seeingAlert.classList.add('visible');
193
+ this.seeingAlert.textContent = `Atmosfera limitada: El seeing de hoy (${seeing}") impedirá que veas el potencial de tu telescopio (${result.dawes.toFixed(2)}").`;
194
+ } else {
195
+ this.seeingAlert.classList.remove('visible');
196
+ }
197
+
198
+ this.updateSimulation(result.dawes);
199
+ }
200
+
201
+ updateSimulation(dawes: number) {
202
+ const baseBlur = Math.max(0, dawes * 3);
203
+ const separation = Math.max(8, 30 - dawes * 15);
204
+
205
+ if (this.star1 && this.star2) {
206
+ this.star1.style.setProperty('--star-blur', `${baseBlur}px`);
207
+ this.star2.style.setProperty('--star-blur', `${baseBlur}px`);
208
+ const container = document.getElementById('binary-system');
209
+ if (container) {
210
+ container.style.gap = `${separation}px`;
211
+ }
212
+ const scale = Math.max(0.5, 2.5 - dawes / 1.5);
213
+ this.star1.style.transform = `scale(${scale})`;
214
+ this.star2.style.transform = `scale(${scale})`;
215
+ }
216
+ }
217
+ }
218
+
219
+ new ResolutionApp();
220
+ </script>
221
+
222
+ <style>
223
+ .tscope-wrapper {
224
+ width: 100%;
225
+ max-width: 56rem;
226
+ margin: 0 auto;
227
+ padding: 2.5rem;
228
+ background: var(--bg-surface);
229
+ border: 1px solid var(--border-base);
230
+ border-radius: 2rem;
231
+ box-shadow: 0 40px 80px -20px var(--shadow-base);
232
+ position: relative;
233
+ overflow: hidden;
234
+ }
235
+
236
+ .tscope-grid {
237
+ display: grid;
238
+ grid-template-columns: 1fr;
239
+ gap: 2.5rem;
240
+ position: relative;
241
+ z-index: 1;
242
+ }
243
+
244
+ @media (min-width: 850px) {
245
+ .tscope-grid {
246
+ grid-template-columns: 1.1fr 1fr;
247
+ gap: 3.5rem;
248
+ }
249
+ }
250
+
251
+ .tscope-controls {
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: 2rem;
255
+ }
256
+
257
+ .tscope-control-group {
258
+ display: flex;
259
+ flex-direction: column;
260
+ gap: 0.85rem;
261
+ }
262
+
263
+ .tscope-control-label {
264
+ font-size: 0.85rem;
265
+ font-weight: 700;
266
+ color: var(--text-muted, #94a3b8);
267
+ text-transform: uppercase;
268
+ letter-spacing: 0.05em;
269
+ display: flex;
270
+ justify-content: space-between;
271
+ }
272
+
273
+ .tscope-stepper {
274
+ display: flex;
275
+ align-items: center;
276
+ background: var(--bg-muted);
277
+ border: 1px solid var(--border-base);
278
+ border-radius: 1rem;
279
+ overflow: hidden;
280
+ transition: border-color 0.3s ease;
281
+ }
282
+
283
+ .tscope-stepper:focus-within {
284
+ border-color: var(--color-purple, #a855f7);
285
+ }
286
+
287
+ .tscope-step-btn {
288
+ width: 4rem;
289
+ height: 4rem;
290
+ border: none;
291
+ background: var(--bg-muted);
292
+ color: var(--color-purple, #a855f7);
293
+ font-size: 1.8rem;
294
+ cursor: pointer;
295
+ transition: all 0.2s ease;
296
+ display: flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ }
300
+
301
+ .tscope-step-btn:hover {
302
+ background: var(--bg-page);
303
+ color: var(--color-purple, #a855f7);
304
+ }
305
+
306
+ .tscope-stepper-input {
307
+ flex: 1;
308
+ background: transparent;
309
+ border: none;
310
+ color: var(--text-base);
311
+ text-align: center;
312
+ font-size: 1.5rem;
313
+ font-weight: 700;
314
+ outline: none;
315
+ width: 100%;
316
+ }
317
+
318
+ .tscope-select-wrapper {
319
+ position: relative;
320
+ width: 100%;
321
+ }
322
+
323
+ .tscope-select-arrow {
324
+ position: absolute;
325
+ right: 0.875rem;
326
+ top: 50%;
327
+ transform: translateY(-50%);
328
+ width: 1rem;
329
+ height: 1rem;
330
+ color: var(--color-purple, #a855f7);
331
+ pointer-events: none;
332
+ }
333
+
334
+ .tscope-select {
335
+ appearance: none;
336
+ -webkit-appearance: none;
337
+ width: 100%;
338
+ background: var(--bg-muted);
339
+ border: 1px solid var(--border-base);
340
+ color: var(--text-base);
341
+ padding: 1rem 3rem 1rem 1.25rem;
342
+ border-radius: 1rem;
343
+ font-size: 0.95rem;
344
+ font-weight: 600;
345
+ outline: none;
346
+ cursor: pointer;
347
+ transition: border-color 0.3s ease;
348
+ }
349
+
350
+ .tscope-select:hover {
351
+ border-color: var(--color-purple, #a855f7);
352
+ }
353
+
354
+ .tscope-select:focus {
355
+ border-color: var(--color-purple, #a855f7);
356
+ }
357
+
358
+ .tscope-select option {
359
+ background: var(--bg-surface);
360
+ color: var(--text-base);
361
+ }
362
+
363
+ .tscope-range {
364
+ width: 100%;
365
+ appearance: none;
366
+ background: var(--bg-muted);
367
+ height: 10px;
368
+ border-radius: 20px;
369
+ outline: none;
370
+ cursor: pointer;
371
+ border: 1px solid var(--border-base);
372
+ accent-color: var(--color-purple, #a855f7);
373
+ }
374
+
375
+ .tscope-results {
376
+ display: flex;
377
+ flex-direction: column;
378
+ gap: 2.5rem;
379
+ padding: 0.5rem;
380
+ }
381
+
382
+ .tscope-main-result {
383
+ text-align: center;
384
+ background: var(--bg-muted);
385
+ padding: 3rem 2rem;
386
+ border-radius: 2rem;
387
+ border: 1px solid var(--border-base);
388
+ }
389
+
390
+ .tscope-result-title {
391
+ font-size: 0.9rem;
392
+ color: var(--color-purple, #a855f7);
393
+ text-transform: uppercase;
394
+ letter-spacing: 0.2em;
395
+ margin: 0 0 2rem;
396
+ }
397
+
398
+ .tscope-resolution-value {
399
+ display: flex;
400
+ flex-direction: column;
401
+ align-items: center;
402
+ gap: 0.5rem;
403
+ }
404
+
405
+ .tscope-resolution-number {
406
+ font-size: 6rem;
407
+ font-weight: 900;
408
+ line-height: 0.9;
409
+ background: linear-gradient(to bottom, var(--text-base), var(--color-purple, #a855f7));
410
+ -webkit-background-clip: text;
411
+ -webkit-text-fill-color: transparent;
412
+ background-clip: text;
413
+ filter: drop-shadow(0 10px 20px rgba(168, 85, 247, 0.4));
414
+ }
415
+
416
+ .tscope-resolution-unit {
417
+ font-size: 1.15rem;
418
+ color: var(--text-muted, #94a3b8);
419
+ font-weight: 600;
420
+ text-transform: lowercase;
421
+ opacity: 0.8;
422
+ }
423
+
424
+ .tscope-comp-grid {
425
+ display: grid;
426
+ grid-template-columns: 1fr 1fr;
427
+ gap: 1.5rem;
428
+ }
429
+
430
+ .tscope-comp-card {
431
+ background: var(--bg-muted);
432
+ padding: 1.25rem;
433
+ border-radius: 1.25rem;
434
+ text-align: center;
435
+ border: 1px solid var(--border-base);
436
+ transition: transform 0.3s ease, border-color 0.2s ease;
437
+ }
438
+
439
+ .tscope-comp-card:hover {
440
+ transform: translateY(-5px);
441
+ border-color: var(--color-purple, #a855f7);
442
+ }
443
+
444
+ .tscope-comp-title {
445
+ font-size: 0.75rem;
446
+ color: var(--text-muted, #64748b);
447
+ margin: 0 0 0.75rem;
448
+ text-transform: uppercase;
449
+ letter-spacing: 0.1em;
450
+ }
451
+
452
+ .tscope-comp-value {
453
+ font-size: 1.75rem;
454
+ font-weight: 800;
455
+ color: var(--color-pink, #ec4899);
456
+ }
457
+
458
+ .tscope-seeing-alert {
459
+ padding: 1.25rem;
460
+ background: var(--bg-muted);
461
+ border-left: 4px solid var(--color-warning, #f59e0b);
462
+ border-radius: 0.75rem;
463
+ font-size: 0.85rem;
464
+ color: var(--text-muted);
465
+ line-height: 1.6;
466
+ display: none;
467
+ }
468
+
469
+ .tscope-seeing-alert.visible {
470
+ display: block;
471
+ animation: tscope-slide-in 0.4s ease-out;
472
+ }
473
+
474
+ @keyframes tscope-slide-in {
475
+ from {
476
+ opacity: 0;
477
+ transform: translateX(-10px);
478
+ }
479
+ to {
480
+ opacity: 1;
481
+ transform: translateX(0);
482
+ }
483
+ }
484
+
485
+ .tscope-visualization {
486
+ background: var(--bg-muted);
487
+ padding: 2rem;
488
+ border-radius: 1.5rem;
489
+ border: 1px solid var(--border-base);
490
+ }
491
+
492
+ .tscope-vis-label {
493
+ display: block;
494
+ font-size: 0.75rem;
495
+ font-weight: 800;
496
+ color: var(--text-muted, #475569);
497
+ text-transform: uppercase;
498
+ letter-spacing: 0.15em;
499
+ margin-bottom: 2rem;
500
+ text-align: center;
501
+ }
502
+
503
+ .tscope-sim-container {
504
+ height: 140px;
505
+ background: radial-gradient(circle at center, #0f172a 0%, #000 100%);
506
+ border-radius: 1rem;
507
+ display: flex;
508
+ align-items: center;
509
+ justify-content: center;
510
+ position: relative;
511
+ border: 1px solid rgba(168, 85, 247, 0.1);
512
+ }
513
+
514
+ .tscope-binary-system {
515
+ display: flex;
516
+ align-items: center;
517
+ position: relative;
518
+ padding: 2rem;
519
+ }
520
+
521
+ .tscope-star {
522
+ width: 8px;
523
+ height: 8px;
524
+ background: white;
525
+ border-radius: 50%;
526
+ filter: blur(var(--star-blur, 0)) drop-shadow(0 0 10px #fff) drop-shadow(0 0 20px var(--color-purple, #a855f7));
527
+ transition: all 0.5s ease;
528
+ position: relative;
529
+ z-index: 2;
530
+ }
531
+
532
+ .tscope-star::after {
533
+ content: '';
534
+ position: absolute;
535
+ top: 50%;
536
+ left: 50%;
537
+ transform: translate(-50%, -50%);
538
+ width: 200%;
539
+ height: 200%;
540
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
541
+ pointer-events: none;
542
+ }
543
+
544
+ .tscope-sim-explanation {
545
+ margin-top: 1.5rem;
546
+ font-size: 0.8rem;
547
+ color: var(--text-muted);
548
+ text-align: center;
549
+ line-height: 1.6;
550
+ letter-spacing: 0.02em;
551
+ padding: 1rem;
552
+ background: var(--bg-page);
553
+ border-radius: 0.75rem;
554
+ border: 1px dashed var(--border-base);
555
+ }
556
+ </style>