@jjlmoya/utils-astronomy 1.34.1 → 1.36.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 +66 -66
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +4 -2
  4. package/src/index.ts +2 -1
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +4 -3
  7. package/src/tool/astrophotographyExposureStackPlanner/astrophotography-exposure-stacking-planner.css +496 -0
  8. package/src/tool/astrophotographyExposureStackPlanner/bibliography.astro +14 -0
  9. package/src/tool/astrophotographyExposureStackPlanner/bibliography.ts +12 -0
  10. package/src/tool/astrophotographyExposureStackPlanner/component.astro +149 -0
  11. package/src/tool/astrophotographyExposureStackPlanner/controller.ts +125 -0
  12. package/src/tool/astrophotographyExposureStackPlanner/dom-views.ts +41 -0
  13. package/src/tool/astrophotographyExposureStackPlanner/entry.ts +29 -0
  14. package/src/tool/astrophotographyExposureStackPlanner/evaluator.ts +44 -0
  15. package/src/tool/astrophotographyExposureStackPlanner/i18n/de.ts +40 -0
  16. package/src/tool/astrophotographyExposureStackPlanner/i18n/en.ts +232 -0
  17. package/src/tool/astrophotographyExposureStackPlanner/i18n/es.ts +52 -0
  18. package/src/tool/astrophotographyExposureStackPlanner/i18n/fr.ts +40 -0
  19. package/src/tool/astrophotographyExposureStackPlanner/i18n/id.ts +40 -0
  20. package/src/tool/astrophotographyExposureStackPlanner/i18n/it.ts +40 -0
  21. package/src/tool/astrophotographyExposureStackPlanner/i18n/ja.ts +40 -0
  22. package/src/tool/astrophotographyExposureStackPlanner/i18n/ko.ts +40 -0
  23. package/src/tool/astrophotographyExposureStackPlanner/i18n/nl.ts +40 -0
  24. package/src/tool/astrophotographyExposureStackPlanner/i18n/pl.ts +40 -0
  25. package/src/tool/astrophotographyExposureStackPlanner/i18n/pt.ts +40 -0
  26. package/src/tool/astrophotographyExposureStackPlanner/i18n/ru.ts +40 -0
  27. package/src/tool/astrophotographyExposureStackPlanner/i18n/sv.ts +40 -0
  28. package/src/tool/astrophotographyExposureStackPlanner/i18n/tr.ts +40 -0
  29. package/src/tool/astrophotographyExposureStackPlanner/i18n/zh.ts +40 -0
  30. package/src/tool/astrophotographyExposureStackPlanner/index.ts +11 -0
  31. package/src/tool/astrophotographyExposureStackPlanner/localized.ts +59 -0
  32. package/src/tool/astrophotographyExposureStackPlanner/logic.test.ts +74 -0
  33. package/src/tool/astrophotographyExposureStackPlanner/logic.ts +95 -0
  34. package/src/tool/astrophotographyExposureStackPlanner/seo.astro +14 -0
  35. package/src/tool/astrophotographyExposureStackPlanner/storage.ts +22 -0
  36. package/src/tool/astrophotographyExposureStackPlanner/ui.ts +102 -0
  37. package/src/tool/meteorShowerObservingPlanner/bibliography.astro +5 -5
  38. package/src/tool/meteorShowerObservingPlanner/dom-views.ts +38 -16
  39. package/src/tool/meteorShowerObservingPlanner/i18n/de.ts +219 -219
  40. package/src/tool/meteorShowerObservingPlanner/i18n/en.ts +219 -219
  41. package/src/tool/meteorShowerObservingPlanner/i18n/es.ts +219 -219
  42. package/src/tool/meteorShowerObservingPlanner/i18n/fr.ts +219 -219
  43. package/src/tool/meteorShowerObservingPlanner/i18n/id.ts +223 -223
  44. package/src/tool/meteorShowerObservingPlanner/i18n/it.ts +223 -223
  45. package/src/tool/meteorShowerObservingPlanner/i18n/ja.ts +219 -219
  46. package/src/tool/meteorShowerObservingPlanner/i18n/ko.ts +219 -219
  47. package/src/tool/meteorShowerObservingPlanner/i18n/nl.ts +223 -223
  48. package/src/tool/meteorShowerObservingPlanner/i18n/pl.ts +223 -223
  49. package/src/tool/meteorShowerObservingPlanner/i18n/pt.ts +223 -223
  50. package/src/tool/meteorShowerObservingPlanner/i18n/ru.ts +219 -219
  51. package/src/tool/meteorShowerObservingPlanner/i18n/sv.ts +223 -223
  52. package/src/tool/meteorShowerObservingPlanner/i18n/tr.ts +223 -223
  53. package/src/tool/meteorShowerObservingPlanner/i18n/zh.ts +219 -219
  54. package/src/tool/meteorShowerObservingPlanner/meteor-shower-observing-planner.css +16 -9
  55. package/src/tool/meteorShowerObservingPlanner/seo.astro +6 -5
  56. package/src/tool/meteorShowerObservingPlanner/ui.ts +87 -87
  57. package/src/tools.ts +3 -1
package/package.json CHANGED
@@ -1,66 +1,66 @@
1
- {
2
- "name": "@jjlmoya/utils-astronomy",
3
- "version": "1.34.1",
4
- "type": "module",
5
- "main": "./src/index.ts",
6
- "types": "./src/index.ts",
7
- "exports": {
8
- ".": "./src/index.ts",
9
- "./data": "./src/data.ts",
10
- "./entries": "./src/entries.ts",
11
- "./runtime/*": "./src/tool/*/index.ts",
12
- "./category-seo": "./src/category/seo.astro"
13
- },
14
- "files": [
15
- "src",
16
- "scripts"
17
- ],
18
- "publishConfig": {
19
- "access": "public"
20
- },
21
- "scripts": {
22
- "dev": "astro dev",
23
- "start": "astro dev",
24
- "build": "astro build",
25
- "preview": "astro preview",
26
- "astro": "astro",
27
- "lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
28
- "check": "astro check",
29
- "type-check": "astro check",
30
- "test": "vitest run --reporter=verbose --testTimeout=30000",
31
- "preversion": "npm run lint && npm run test && npm run build",
32
- "postversion": "git push && git push --tags",
33
- "patch": "npm version patch",
34
- "minor": "npm version minor",
35
- "major": "npm version major",
36
- "postinstall": "node scripts/postinstall.mjs"
37
- },
38
- "lint-staged": {
39
- "*.{ts,tsx,astro}": [
40
- "eslint --fix"
41
- ]
42
- },
43
- "dependencies": {
44
- "@iconify-json/mdi": "^1.2.3",
45
- "@jjlmoya/prompagate": "^1.1.0",
46
- "@jjlmoya/utils-shared": "1.2.0",
47
- "astro": "^6.1.2",
48
- "astro-icon": "^1.1.0"
49
- },
50
- "devDependencies": {
51
- "@astrojs/check": "^0.9.8",
52
- "eslint": "^9.39.4",
53
- "eslint-plugin-astro": "^1.6.0",
54
- "eslint-plugin-no-comments": "^1.1.10",
55
- "husky": "^9.1.7",
56
- "lint-staged": "^16.4.0",
57
- "postcss-html": "^1.8.1",
58
- "schema-dts": "^1.1.2",
59
- "stylelint": "^17.6.0",
60
- "stylelint-config-standard": "^40.0.0",
61
- "stylelint-declaration-strict-value": "^1.11.1",
62
- "typescript": "^5.4.0",
63
- "typescript-eslint": "^8.58.0",
64
- "vitest": "^4.1.2"
65
- }
66
- }
1
+ {
2
+ "name": "@jjlmoya/utils-astronomy",
3
+ "version": "1.36.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./data": "./src/data.ts",
10
+ "./entries": "./src/entries.ts",
11
+ "./runtime/*": "./src/tool/*/index.ts",
12
+ "./category-seo": "./src/category/seo.astro"
13
+ },
14
+ "files": [
15
+ "src",
16
+ "scripts"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "dev": "astro dev",
23
+ "start": "astro dev",
24
+ "build": "astro build",
25
+ "preview": "astro preview",
26
+ "astro": "astro",
27
+ "lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
28
+ "check": "astro check",
29
+ "type-check": "astro check",
30
+ "test": "vitest run --reporter=verbose --testTimeout=30000",
31
+ "preversion": "npm run lint && npm run test && npm run build",
32
+ "postversion": "git push && git push --tags",
33
+ "patch": "npm version patch",
34
+ "minor": "npm version minor",
35
+ "major": "npm version major",
36
+ "postinstall": "node scripts/postinstall.mjs"
37
+ },
38
+ "lint-staged": {
39
+ "*.{ts,tsx,astro}": [
40
+ "eslint --fix"
41
+ ]
42
+ },
43
+ "dependencies": {
44
+ "@iconify-json/mdi": "^1.2.3",
45
+ "@jjlmoya/prompagate": "^1.1.0",
46
+ "@jjlmoya/utils-shared": "1.2.0",
47
+ "astro": "^6.1.2",
48
+ "astro-icon": "^1.1.0"
49
+ },
50
+ "devDependencies": {
51
+ "@astrojs/check": "^0.9.8",
52
+ "eslint": "^9.39.4",
53
+ "eslint-plugin-astro": "^1.6.0",
54
+ "eslint-plugin-no-comments": "^1.1.10",
55
+ "husky": "^9.1.7",
56
+ "lint-staged": "^16.4.0",
57
+ "postcss-html": "^1.8.1",
58
+ "schema-dts": "^1.1.2",
59
+ "stylelint": "^17.6.0",
60
+ "stylelint-config-standard": "^40.0.0",
61
+ "stylelint-declaration-strict-value": "^1.11.1",
62
+ "typescript": "^5.4.0",
63
+ "typescript-eslint": "^8.58.0",
64
+ "vitest": "^4.1.2"
65
+ }
66
+ }
@@ -6,10 +6,11 @@ import { telescopeResolution } from '../tool/telescopeResolution/entry';
6
6
  import { eyepieceCalculator } from '../tool/smartEyepieceCalculator/entry';
7
7
  import { telescopeExitPupilPlanner } from '../tool/telescopeExitPupilPlanner/entry';
8
8
  import { meteorShowerObservingPlanner } from '../tool/meteorShowerObservingPlanner/entry';
9
+ import { astrophotographyExposureStackPlanner } from '../tool/astrophotographyExposureStackPlanner/entry';
9
10
 
10
11
  export const toolsCategory: AstronomyCategoryEntry = {
11
12
  icon: 'mdi:telescope',
12
- tools: [bortleVisualizer, deepSpaceScope, starExposureCalculator, telescopeResolution, eyepieceCalculator, telescopeExitPupilPlanner, meteorShowerObservingPlanner],
13
+ tools: [bortleVisualizer, deepSpaceScope, starExposureCalculator, telescopeResolution, eyepieceCalculator, telescopeExitPupilPlanner, meteorShowerObservingPlanner, astrophotographyExposureStackPlanner],
13
14
  i18n: {
14
15
  de: () => import('./i18n/de').then((m) => m.content),
15
16
  en: () => import('./i18n/en').then((m) => m.content),
package/src/entries.ts CHANGED
@@ -12,6 +12,8 @@ export { telescopeExitPupilPlanner } from './tool/telescopeExitPupilPlanner/entr
12
12
  export type { TelescopeExitPupilPlannerUI, TelescopeExitPupilPlannerLocaleContent } from './tool/telescopeExitPupilPlanner/entry';
13
13
  export { meteorShowerObservingPlanner } from './tool/meteorShowerObservingPlanner/entry';
14
14
  export type { MeteorShowerObservingPlannerUI, MeteorShowerObservingPlannerLocaleContent } from './tool/meteorShowerObservingPlanner/entry';
15
+ export { astrophotographyExposureStackPlanner } from './tool/astrophotographyExposureStackPlanner/entry';
16
+ export type { ExposurePlannerUI, ExposurePlannerLocaleContent } from './tool/astrophotographyExposureStackPlanner/entry';
15
17
  export { toolsCategory, astronomyCategory } from './category';
16
18
  import { bortleVisualizer } from './tool/bortleVisualizer/entry';
17
19
  import { deepSpaceScope } from './tool/deepSpaceScope/entry';
@@ -20,5 +22,5 @@ import { telescopeResolution } from './tool/telescopeResolution/entry';
20
22
  import { eyepieceCalculator } from './tool/smartEyepieceCalculator/entry';
21
23
  import { telescopeExitPupilPlanner } from './tool/telescopeExitPupilPlanner/entry';
22
24
  import { meteorShowerObservingPlanner } from './tool/meteorShowerObservingPlanner/entry';
23
- export const ALL_ENTRIES = [bortleVisualizer, deepSpaceScope, starExposureCalculator, telescopeResolution, eyepieceCalculator, telescopeExitPupilPlanner, meteorShowerObservingPlanner];
24
-
25
+ import { astrophotographyExposureStackPlanner } from './tool/astrophotographyExposureStackPlanner/entry';
26
+ export const ALL_ENTRIES = [bortleVisualizer, deepSpaceScope, starExposureCalculator, telescopeResolution, eyepieceCalculator, telescopeExitPupilPlanner, meteorShowerObservingPlanner, astrophotographyExposureStackPlanner];
package/src/index.ts CHANGED
@@ -2,7 +2,8 @@ export * from './tool/bortleVisualizer';
2
2
  export * from './tool/deepSpaceScope';
3
3
  export * from './tool/starExposureCalculator';
4
4
  export * from './tool/telescopeResolution';
5
- export * from './tool/meteorShowerObservingPlanner';
5
+ export * from './tool/meteorShowerObservingPlanner';
6
+ export * from './tool/astrophotographyExposureStackPlanner';
6
7
 
7
8
  export { toolsCategory as astronomyCategory } from './category';
8
9
  export const AstronomyCategorySEO = () => import('./category/seo.astro').then((m) => m.default);
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
2
2
  import { ALL_TOOLS } from '../tools';
3
3
 
4
4
  describe('Locale Completeness Validation', () => {
5
- it('all 7 tools registered', () => {
6
- expect(ALL_TOOLS.length).toBe(7);
5
+ it('all 8 tools registered', () => {
6
+ expect(ALL_TOOLS.length).toBe(8);
7
7
  });
8
8
  });
@@ -79,6 +79,7 @@ describe('Tool Validation Suite', () => {
79
79
  'calculadora-oculares',
80
80
  'planificador-pupila-salida-aumentos-telescopio',
81
81
  'planificador-observacion-lluvia-meteoros',
82
+ 'planificador-apilado-exposiciones-astrofotografia',
82
83
  'meteor-shower-observing-planner',
83
84
  ];
84
85
  expect(validSlugs).toContain(content.slug);
@@ -105,12 +106,12 @@ describe('Tool Validation Suite', () => {
105
106
  });
106
107
 
107
108
  describe('Library Registration', () => {
108
- it('should have 7 tools in ALL_TOOLS', () => {
109
- expect(ALL_TOOLS.length).toBe(7);
109
+ it('should have 8 tools in ALL_TOOLS', () => {
110
+ expect(ALL_TOOLS.length).toBe(8);
110
111
  });
111
112
 
112
113
  it('should have all tools in astronomyCategory', () => {
113
- expect(astronomyCategory.tools.length).toBe(7);
114
+ expect(astronomyCategory.tools.length).toBe(8);
114
115
  ALL_TOOLS.forEach(({ entry }) => {
115
116
  const exists = astronomyCategory.tools.some((t: any) => t.id === entry.id);
116
117
  expect(exists).toBe(true);
@@ -0,0 +1,496 @@
1
+ :root {
2
+ --n-base-night: #f4f1ea;
3
+ --n-base-paper: #fffdf8;
4
+ --n-base-ink: #202a2e;
5
+ --n-base-muted: #657276;
6
+ --n-base-line: #d8d5cc;
7
+ --n-base-blue: #2f6677;
8
+ --n-base-gold: #b67b2c;
9
+ --n-base-alert: #b65742;
10
+ --n-base-shadow: #c9c3b8;
11
+ }
12
+
13
+ .theme-dark {
14
+ --n-dark-night: #15191c;
15
+ --n-dark-paper: #20262a;
16
+ --n-dark-ink: #f1eee5;
17
+ --n-dark-muted: #aab4b4;
18
+ --n-dark-line: #3a4447;
19
+ --n-dark-blue: #8fc2ca;
20
+ --n-dark-gold: #e1ad5a;
21
+ --n-dark-alert: #ed987f;
22
+ --n-dark-shadow: #0a0d0e;
23
+ }
24
+
25
+ .astrophotography-exposure-stack-planner {
26
+ --n-night: var(--n-base-night);
27
+ --n-paper: var(--n-base-paper);
28
+ --n-ink: var(--n-base-ink);
29
+ --n-muted: var(--n-base-muted);
30
+ --n-line: var(--n-base-line);
31
+ --n-blue: var(--n-base-blue);
32
+ --n-gold: var(--n-base-gold);
33
+ --n-alert: var(--n-base-alert);
34
+ --n-shadow: var(--n-base-shadow);
35
+
36
+ display: grid;
37
+ grid-template-columns: minmax(260px, 0.8fr) minmax(0, 1.5fr);
38
+ min-height: 690px;
39
+ overflow: hidden;
40
+ border: 1px solid var(--n-line);
41
+ background-color: var(--n-night);
42
+ color: var(--n-ink);
43
+ box-shadow: 12px 12px 0 var(--n-shadow);
44
+ min-width: 0;
45
+ }
46
+
47
+ .theme-dark .astrophotography-exposure-stack-planner {
48
+ --n-night: var(--n-dark-night);
49
+ --n-paper: var(--n-dark-paper);
50
+ --n-ink: var(--n-dark-ink);
51
+ --n-muted: var(--n-dark-muted);
52
+ --n-line: var(--n-dark-line);
53
+ --n-blue: var(--n-dark-blue);
54
+ --n-gold: var(--n-dark-gold);
55
+ --n-alert: var(--n-dark-alert);
56
+ --n-shadow: var(--n-dark-shadow);
57
+ }
58
+
59
+ body:has(.astrophotography-exposure-stack-planner) .utility-header-title {
60
+ font-size: clamp(2.15rem, 6.5vw, 4.5rem);
61
+ }
62
+
63
+ body:has(.astrophotography-exposure-stack-planner) .utility-header-title > span {
64
+ min-width: 0;
65
+ max-width: 100%;
66
+ overflow-wrap: anywhere;
67
+ text-align: center;
68
+ }
69
+
70
+ body:has(.astrophotography-exposure-stack-planner) .utility-header-description {
71
+ width: 100%;
72
+ padding: 0 10px;
73
+ }
74
+
75
+ .exposure-planner-form {
76
+ min-width: 0;
77
+ padding: 30px 26px;
78
+ border-right: 1px solid var(--n-line);
79
+ }
80
+
81
+ .control-rail,
82
+ .camera-fields,
83
+ .choice-fieldset,
84
+ .timing-fields {
85
+ display: grid;
86
+ gap: 16px;
87
+ border: 0;
88
+ margin: 0;
89
+ padding: 0;
90
+ }
91
+
92
+ .control-rail {
93
+ gap: 24px;
94
+ }
95
+
96
+ legend {
97
+ width: 100%;
98
+ margin-bottom: 2px;
99
+ color: var(--n-gold);
100
+ font-size: 0.7rem;
101
+ font-weight: 800;
102
+ letter-spacing: 0.14em;
103
+ text-transform: uppercase;
104
+ }
105
+
106
+ .camera-fields {
107
+ grid-template-columns: 1fr 1fr;
108
+ padding-bottom: 22px;
109
+ border-bottom: 1px solid var(--n-line);
110
+ }
111
+
112
+ .camera-fields legend {
113
+ grid-column: 1 / -1;
114
+ }
115
+
116
+ .timing-fields {
117
+ padding-top: 2px;
118
+ }
119
+
120
+ .field-control {
121
+ display: grid;
122
+ gap: 7px;
123
+ color: var(--n-ink);
124
+ font-size: 0.82rem;
125
+ font-weight: 700;
126
+ }
127
+
128
+ .field-control small {
129
+ color: var(--n-muted);
130
+ font-size: 0.68rem;
131
+ font-weight: 500;
132
+ line-height: 1.35;
133
+ }
134
+
135
+ input {
136
+ width: 100%;
137
+ min-height: 44px;
138
+ padding: 9px 10px;
139
+ border: 1px solid var(--n-line);
140
+ border-radius: 0;
141
+ background-color: var(--n-paper);
142
+ color: var(--n-ink);
143
+ font: inherit;
144
+ font-variant-numeric: tabular-nums;
145
+ }
146
+
147
+ input:focus-visible,
148
+ button:focus-visible,
149
+ summary:focus-visible {
150
+ outline: 3px solid var(--n-gold);
151
+ outline-offset: 2px;
152
+ }
153
+
154
+ .choice-grid {
155
+ min-width: 0;
156
+ display: grid;
157
+ grid-template-columns: 1fr 1fr;
158
+ gap: 7px;
159
+ }
160
+
161
+ .sensor-grid {
162
+ grid-template-columns: 1fr 1fr;
163
+ }
164
+
165
+ button {
166
+ min-width: 0;
167
+ min-height: 40px;
168
+ padding: 8px 9px;
169
+ border: 1px solid var(--n-line);
170
+ border-radius: 0;
171
+ background-color: var(--n-paper);
172
+ color: var(--n-muted);
173
+ cursor: pointer;
174
+ font: inherit;
175
+ font-size: 0.7rem;
176
+ font-weight: 700;
177
+ line-height: 1.2;
178
+ overflow-wrap: anywhere;
179
+ transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease;
180
+ }
181
+
182
+ button:hover,
183
+ button.is-active {
184
+ border-color: var(--n-blue);
185
+ background-color: var(--n-blue);
186
+ color: var(--n-night);
187
+ }
188
+
189
+ .integration-field {
190
+ padding-top: 4px;
191
+ }
192
+
193
+ .exposure-result {
194
+ min-width: 0;
195
+ display: grid;
196
+ align-content: start;
197
+ gap: 24px;
198
+ padding: 30px 34px 28px;
199
+ background-color: var(--n-paper);
200
+ }
201
+
202
+ .result-topline,
203
+ .timeline-header,
204
+ .timeline-labels,
205
+ .result-heading,
206
+ .comparison-row,
207
+ .metric-strip {
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: space-between;
211
+ gap: 16px;
212
+ }
213
+
214
+ .result-kicker {
215
+ color: var(--n-muted);
216
+ font-size: 0.68rem;
217
+ font-weight: 800;
218
+ letter-spacing: 0.16em;
219
+ text-transform: uppercase;
220
+ }
221
+
222
+ .status-pill {
223
+ max-width: 45%;
224
+ overflow-wrap: anywhere;
225
+ padding: 6px 9px;
226
+ border: 1px solid var(--n-gold);
227
+ color: var(--n-gold);
228
+ font-size: 0.66rem;
229
+ font-weight: 800;
230
+ text-transform: uppercase;
231
+ }
232
+
233
+ .status-pill[data-tone="critical"] {
234
+ border-color: var(--n-alert);
235
+ color: var(--n-alert);
236
+ }
237
+
238
+ .result-heading {
239
+ align-items: end;
240
+ padding-bottom: 24px;
241
+ border-bottom: 1px solid var(--n-line);
242
+ }
243
+
244
+ h2,
245
+ h3,
246
+ p {
247
+ margin: 0;
248
+ }
249
+
250
+ h2 {
251
+ max-width: 420px;
252
+ font-size: clamp(1.35rem, 3vw, 2.4rem);
253
+ line-height: 1;
254
+ letter-spacing: -0.04em;
255
+ }
256
+
257
+ .result-heading p {
258
+ max-width: 500px;
259
+ margin-top: 10px;
260
+ color: var(--n-muted);
261
+ font-size: 0.82rem;
262
+ line-height: 1.5;
263
+ }
264
+
265
+ .hero-output {
266
+ display: grid;
267
+ flex: 0 0 auto;
268
+ justify-items: end;
269
+ color: var(--n-blue);
270
+ }
271
+
272
+ .hero-output strong {
273
+ font-size: clamp(2.5rem, 7vw, 5.3rem);
274
+ font-weight: 800;
275
+ letter-spacing: -0.08em;
276
+ line-height: 0.85;
277
+ font-variant-numeric: tabular-nums;
278
+ }
279
+
280
+ .hero-output span {
281
+ margin-top: 8px;
282
+ color: var(--n-muted);
283
+ font-size: 0.72rem;
284
+ font-weight: 800;
285
+ letter-spacing: 0.12em;
286
+ text-transform: uppercase;
287
+ }
288
+
289
+ .timeline-panel {
290
+ display: grid;
291
+ gap: 13px;
292
+ }
293
+
294
+ h3 {
295
+ font-size: 0.82rem;
296
+ letter-spacing: 0.02em;
297
+ }
298
+
299
+ .timeline-header > span {
300
+ color: var(--n-muted);
301
+ font-size: 0.74rem;
302
+ font-variant-numeric: tabular-nums;
303
+ }
304
+
305
+ .timeline {
306
+ min-height: 88px;
307
+ padding: 20px 0;
308
+ border-top: 1px solid var(--n-line);
309
+ border-bottom: 1px solid var(--n-line);
310
+ }
311
+
312
+ .exposure-track {
313
+ display: flex;
314
+ align-items: stretch;
315
+ height: 47px;
316
+ gap: 3px;
317
+ }
318
+
319
+ .exposure-slice {
320
+ position: relative;
321
+ min-width: 7px;
322
+ background-color: var(--n-blue);
323
+ opacity: 0.55;
324
+ }
325
+
326
+ .exposure-slice::after {
327
+ position: absolute;
328
+ right: 2px;
329
+ bottom: 6px;
330
+ left: 2px;
331
+ height: 3px;
332
+ background-color: var(--n-gold);
333
+ content: '';
334
+ }
335
+
336
+ .exposure-slice.is-final {
337
+ background-color: var(--n-gold);
338
+ opacity: 1;
339
+ }
340
+
341
+ .exposure-slice.is-final::after {
342
+ background-color: var(--n-paper);
343
+ }
344
+
345
+ .timeline-labels {
346
+ color: var(--n-muted);
347
+ font-size: 0.68rem;
348
+ }
349
+
350
+ .metric-strip {
351
+ align-items: stretch;
352
+ border-bottom: 1px solid var(--n-line);
353
+ }
354
+
355
+ .metric-strip > div {
356
+ display: grid;
357
+ flex: 1;
358
+ gap: 7px;
359
+ padding: 0 16px 20px 0;
360
+ border-right: 1px solid var(--n-line);
361
+ }
362
+
363
+ .metric-strip > div + div {
364
+ padding-left: 16px;
365
+ }
366
+
367
+ .metric-strip > div:last-child {
368
+ border-right: 0;
369
+ }
370
+
371
+ .metric-strip span,
372
+ .comparison-row span {
373
+ color: var(--n-muted);
374
+ font-size: 0.7rem;
375
+ }
376
+
377
+ .metric-strip strong,
378
+ .comparison-row strong {
379
+ font-size: 1rem;
380
+ font-variant-numeric: tabular-nums;
381
+ }
382
+
383
+ .session-detail,
384
+ .workload-note,
385
+ .sensor-warning {
386
+ color: var(--n-muted);
387
+ font-size: 0.74rem;
388
+ line-height: 1.5;
389
+ }
390
+
391
+ .session-detail {
392
+ margin-top: -15px;
393
+ }
394
+
395
+ .workload-note,
396
+ .sensor-warning {
397
+ padding: 13px 15px;
398
+ border-left: 3px solid var(--n-gold);
399
+ background-color: var(--n-night);
400
+ }
401
+
402
+ .sensor-warning {
403
+ border-left-color: var(--n-alert);
404
+ }
405
+
406
+ .comparison-panel {
407
+ display: grid;
408
+ gap: 11px;
409
+ padding: 17px;
410
+ border: 1px solid var(--n-line);
411
+ }
412
+
413
+ .comparison-panel h3 {
414
+ margin-bottom: 3px;
415
+ color: var(--n-gold);
416
+ font-size: 0.7rem;
417
+ letter-spacing: 0.14em;
418
+ text-transform: uppercase;
419
+ }
420
+
421
+ .comparison-row + .comparison-row {
422
+ padding-top: 10px;
423
+ border-top: 1px solid var(--n-line);
424
+ }
425
+
426
+ .method-details {
427
+ color: var(--n-muted);
428
+ font-size: 0.76rem;
429
+ line-height: 1.55;
430
+ }
431
+
432
+ .method-details summary {
433
+ color: var(--n-ink);
434
+ cursor: pointer;
435
+ font-weight: 800;
436
+ }
437
+
438
+ .method-details p {
439
+ margin-top: 10px;
440
+ }
441
+
442
+ .limits-note {
443
+ padding: 13px 15px;
444
+ border-left: 3px solid var(--n-alert);
445
+ background-color: var(--n-night);
446
+ color: var(--n-muted);
447
+ font-size: 0.74rem;
448
+ line-height: 1.5;
449
+ }
450
+
451
+ .limits-note strong {
452
+ color: var(--n-alert);
453
+ }
454
+
455
+ .limits-note p {
456
+ margin-top: 5px;
457
+ }
458
+
459
+ @media (max-width: 820px) {
460
+ .astrophotography-exposure-stack-planner {
461
+ grid-template-columns: 1fr;
462
+ }
463
+
464
+ .exposure-planner-form {
465
+ border-right: 0;
466
+ border-bottom: 1px solid var(--n-line);
467
+ }
468
+ }
469
+
470
+ @media (max-width: 520px) {
471
+ .exposure-planner-form,
472
+ .exposure-result {
473
+ padding: 23px 18px;
474
+ }
475
+
476
+ .camera-fields {
477
+ grid-template-columns: 1fr;
478
+ }
479
+
480
+ .camera-fields legend {
481
+ grid-column: auto;
482
+ }
483
+
484
+ .result-heading {
485
+ align-items: start;
486
+ flex-direction: column;
487
+ }
488
+
489
+ .result-topline {
490
+ align-items: flex-start;
491
+ }
492
+
493
+ .hero-output {
494
+ justify-items: start;
495
+ }
496
+ }