@found-in-space/skykit 0.2.0-alpha.1 → 0.2.0-dev.20260527.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 (42) hide show
  1. package/README.md +143 -6
  2. package/examples/custom-object-layer/custom-object-layer.js +1 -24
  3. package/examples/xr-free-roam/index.html +62 -4
  4. package/examples/xr-free-roam/xr-free-roam.css +249 -18
  5. package/examples/xr-free-roam/xr-free-roam.js +644 -217
  6. package/package.json +31 -5
  7. package/src/__tests__/skykit-anchored-images.test.js +32 -4
  8. package/src/__tests__/skykit-browser.test.js +217 -0
  9. package/src/__tests__/skykit-data.test.js +131 -0
  10. package/src/__tests__/skykit-parallax.test.js +4 -4
  11. package/src/__tests__/skykit-touch-os.test.js +71 -0
  12. package/src/__tests__/skykit-xr.test.js +123 -2
  13. package/src/__tests__/skykit.test.js +138 -1
  14. package/src/anchored-images.js +14 -15
  15. package/src/browser-addons.d.ts +16 -0
  16. package/src/browser-addons.js +155 -0
  17. package/src/browser-constellations.d.ts +13 -0
  18. package/src/browser-constellations.js +387 -0
  19. package/src/browser-journey.d.ts +8 -0
  20. package/src/browser-journey.js +240 -0
  21. package/src/browser.d.ts +98 -0
  22. package/src/browser.js +215 -13
  23. package/src/data.d.ts +133 -0
  24. package/src/data.js +447 -0
  25. package/src/embed.d.ts +5 -0
  26. package/src/embed.js +52 -2
  27. package/src/hr-diagram.js +23 -5
  28. package/src/index.d.ts +32 -7
  29. package/src/plugins.js +87 -43
  30. package/src/story.d.ts +57 -0
  31. package/src/story.js +396 -0
  32. package/src/three-shim.d.ts +32 -0
  33. package/src/touch-os.d.ts +70 -0
  34. package/src/touch-os.js +275 -0
  35. package/src/utils.js +96 -6
  36. package/src/viewer-entry.d.ts +10 -0
  37. package/src/viewer-entry.js +4 -0
  38. package/src/viewer.js +110 -12
  39. package/src/xr/plugins.js +224 -13
  40. package/src/xr/session.js +60 -14
  41. package/src/xr.d.ts +22 -0
  42. package/src/xr.js +1 -0
package/README.md CHANGED
@@ -20,6 +20,20 @@ strategies. SkyKit passes strategies through to provider sessions; it does not
20
20
  redefine planning, inspect strategy kinds, or hide loader registries behind
21
21
  string names.
22
22
 
23
+ ## Website Use-Cases
24
+
25
+ The beginner website entries are use-case bounded:
26
+
27
+ | Use-case | Website owner says | Public entry |
28
+ | --- | --- | --- |
29
+ | Viewer | "Put stars on my page and let me customize the scene." | `embed.js`, `viewer.js` |
30
+ | Data | "Give me star data so I can render, list, map, or game it myself." | `data.js` |
31
+ | Story | "Let me tell a curated story through space." | `story.js` |
32
+
33
+ `embed.js` is the no-code viewer entry. It is not a separate use-case.
34
+ `viewer.js` is the JavaScript-customizable viewer entry. `data.js` is renderer
35
+ independent. `story.js` is authored chapters plus a viewer.
36
+
23
37
  ## Paste into a static page or CMS
24
38
 
25
39
  For the beginner path, use the auto-booting embed. Paste this into a static HTML
@@ -36,7 +50,7 @@ page or a CMS custom HTML block:
36
50
 
37
51
  <script
38
52
  type="module"
39
- src="https://esm.sh/@found-in-space/skykit/embed?bundle"
53
+ src="https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/embed?bundle&deps=three@0.170.0"
40
54
  ></script>
41
55
  ```
42
56
 
@@ -55,19 +69,64 @@ Optional attributes keep small tweaks HTML-only:
55
69
  data-skykit-magnitude="7"
56
70
  data-skykit-speed="4"
57
71
  data-skykit-exposure="2600"
72
+ data-skykit-look-at="ra=4.496h, dec=16.948"
73
+ data-skykit-mouse-mode="strafe"
58
74
  style="width: 100%; height: 520px; background: #02040b"
59
75
  ></div>
60
76
  ```
61
77
 
78
+ `data-skykit-look-at` accepts RA/Dec text such as
79
+ `ra=4.496h, dec=16.948`, decimal degrees such as `67.447,16.948`, or a
80
+ parsec-space `x,y,z` target for exact generated coordinates. `data-skykit-mouse-mode`
81
+ defaults to `grab`; use `look` or `strafe` for the first-person mouse-look
82
+ direction, or `none` to disable mouse drag controls.
83
+
62
84
  The host dispatches `skykit-browser-ready` with `{ browser, viewer }` in
63
85
  `event.detail` after startup, and `skykit-browser-error` if startup fails. The
64
- embed does not install a global object, so pages can host multiple viewers.
86
+ embed also installs a small `Skykit` global for noob-path scripts:
87
+
88
+ ```js
89
+ const browser = await Skykit.whenReady();
90
+ ```
65
91
 
66
- Pin the CDN URL to a released SkyKit version when publishing long-lived pages,
67
- for example
92
+ Pages can host multiple viewers. Pass a selector or element to choose one:
93
+
94
+ ```js
95
+ const browser = await Skykit.whenReady('#orion-viewer');
96
+ ```
97
+
98
+ Pin the package CDN URL to a released SkyKit version when publishing long-lived
99
+ pages, for example
68
100
  `https://esm.sh/@found-in-space/skykit@x.y.z/embed?bundle&deps=three@0.170.0`.
69
101
 
70
- ## Create a browser from JavaScript
102
+ Optional first-party capabilities stay out of the initial browser until they are
103
+ requested. This keeps the one-script noob path while avoiding bundle bloat.
104
+
105
+ ```html
106
+ <div
107
+ data-skykit-browser
108
+ data-skykit-constellations="western"
109
+ data-skykit-constellation-art="off"
110
+ style="width:100%;height:520px;background:#02040b"
111
+ ></div>
112
+ ```
113
+
114
+ For small scripted interactions, use the browser handle:
115
+
116
+ ```html
117
+ <script type="module">
118
+ const browser = await Skykit.whenReady();
119
+
120
+ document.querySelector('#orion').addEventListener('click', () => {
121
+ browser.journey.transitionTo({
122
+ lookAt: 'ra=5.919h, dec=7.407',
123
+ durationSecs: 3,
124
+ });
125
+ });
126
+ </script>
127
+ ```
128
+
129
+ ## Create a Viewer from JavaScript
71
130
 
72
131
  If your site has a module script, npm, or a bundler, call the helper directly:
73
132
 
@@ -76,7 +135,7 @@ If your site has a module script, npm, or a bundler, call the helper directly:
76
135
  <pre id="status">Loading stars...</pre>
77
136
 
78
137
  <script type="module">
79
- import { createSkykitBrowser } from '@found-in-space/skykit/browser';
138
+ import { createSkykitBrowser } from 'https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/viewer?bundle&deps=three@0.170.0';
80
139
 
81
140
  await createSkykitBrowser({
82
141
  host: '#viewer',
@@ -91,10 +150,66 @@ The helper still returns the pieces when a lesson wants to grow:
91
150
  const sky = await createSkykitBrowser('#viewer');
92
151
 
93
152
  sky.viewer.requestViewState({ observerPc: { x: 4, y: 0, z: -8 } });
153
+ sky.addObject(marker, {
154
+ positionPc: { x: 17.574, y: 42.316, z: 13.963 },
155
+ });
94
156
  sky.loop.stop();
95
157
  await sky.dispose();
96
158
  ```
97
159
 
160
+ For npm or bundlers, use the same beginner entry:
161
+
162
+ ```js
163
+ import { THREE, createSkykitBrowser } from '@found-in-space/skykit/viewer';
164
+ ```
165
+
166
+ ## Use Star Data Without a Viewer
167
+
168
+ Use `data.js` when SkyKit should supply rows and your app should own rendering:
169
+
170
+ ```js
171
+ import { loadStarRows } from 'https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/data?bundle';
172
+
173
+ const stars = await loadStarRows({
174
+ limitingMagnitude: 6.5,
175
+ maxStars: 100,
176
+ sortBy: 'apparentMagnitude',
177
+ });
178
+
179
+ console.table(stars);
180
+ ```
181
+
182
+ For games and maps, load a local volume and hand rows to Canvas, PixiJS,
183
+ Phaser, SVG, or your own renderer:
184
+
185
+ ```js
186
+ const stars = await loadStarRows({
187
+ centerPc: { x: 0, y: 0, z: 0 },
188
+ radiusPc: 50,
189
+ maxStars: 2000,
190
+ });
191
+ ```
192
+
193
+ ## Create a Guided Story
194
+
195
+ Use `story.js` when the page is an authored article or tour:
196
+
197
+ ```html
198
+ <div data-skykit-story style="height:600px;background:#02040b">
199
+ <section data-skykit-chapter data-title="The Sun" data-target-pc="0,0,0">
200
+ We start at the Sun.
201
+ </section>
202
+ <section data-skykit-chapter data-title="The Hyades" data-target-pc="17.574,42.316,13.963">
203
+ Now jump to the Hyades cluster.
204
+ </section>
205
+ </div>
206
+
207
+ <script
208
+ type="module"
209
+ src="https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/story?bundle&deps=three@0.170.0"
210
+ ></script>
211
+ ```
212
+
98
213
  Use the lower-level factories when a lesson is teaching composition or replacing
99
214
  a part of the stack:
100
215
 
@@ -263,6 +378,28 @@ For a slightly more playful example, see `examples/plugin-lab.js`. It builds
263
378
  app-owned Three objects and action-driven annotations from the same public hooks
264
379
  a learner would use.
265
380
 
381
+ The pasteable browser embed has a smaller add-on convention for noob pages:
382
+
383
+ ```js
384
+ Skykit.registerBrowserAddon({
385
+ id: 'lesson:marker',
386
+ install({ browser, THREE }) {
387
+ const marker = new THREE.Mesh(
388
+ new THREE.SphereGeometry(0.02),
389
+ new THREE.MeshBasicMaterial({ color: 0xffcc00 }),
390
+ );
391
+ const handle = browser.addObject(marker, {
392
+ positionPc: { x: 17.574, y: 42.316, z: 13.963 },
393
+ });
394
+ return () => handle.dispose();
395
+ },
396
+ });
397
+ ```
398
+
399
+ See `docs/skykit-browser-plugins.md` for the browser add-on spec,
400
+ `Skykit.whenReady()`, first-party constellation support, and the
401
+ `browser.journey` API.
402
+
266
403
  Browser lessons:
267
404
 
268
405
  - `examples/free-roam-lesson/` composes streamed stars, keyboard navigation,
@@ -145,7 +145,7 @@ function createInitialViewState() {
145
145
  observerPc: { x: 0, y: 0, z: 0 },
146
146
  coordinateUnitsPerParsec: UNITS_PER_PARSEC,
147
147
  limitingMagnitude: 7.5,
148
- orientationIcrs: lookAtFromOriginWithNorthUp(HYADES_CENTER_PC),
148
+ lookAt: { targetPc: HYADES_CENTER_PC },
149
149
  };
150
150
  }
151
151
 
@@ -332,29 +332,6 @@ function toRenderPosition(pointPc) {
332
332
  );
333
333
  }
334
334
 
335
- function lookAtFromOriginWithNorthUp(targetPc) {
336
- const forward = new THREE.Vector3(targetPc.x, targetPc.y, targetPc.z).normalize();
337
- const north = new THREE.Vector3(0, 0, 1);
338
- let up = north.clone().sub(forward.clone().multiplyScalar(north.dot(forward)));
339
- if (up.lengthSq() < 1e-8) {
340
- up = new THREE.Vector3(0, 1, 0);
341
- }
342
- up.normalize();
343
-
344
- const backward = forward.clone().negate();
345
- const right = up.clone().cross(backward).normalize();
346
- up = backward.clone().cross(right).normalize();
347
-
348
- const matrix = new THREE.Matrix4().makeBasis(right, up, backward);
349
- const quaternion = new THREE.Quaternion().setFromRotationMatrix(matrix);
350
- return {
351
- x: quaternion.x,
352
- y: quaternion.y,
353
- z: quaternion.z,
354
- w: quaternion.w,
355
- };
356
- }
357
-
358
335
  function directionFromRaDec(raDeg, decDeg) {
359
336
  const ra = THREE.MathUtils.degToRad(raDeg);
360
337
  const dec = THREE.MathUtils.degToRad(decDeg);
@@ -10,10 +10,68 @@
10
10
  <body>
11
11
  <main class="xr-free-roam-shell">
12
12
  <div class="viewer-shell" data-viewer></div>
13
- <div class="xr-actions" aria-label="XR actions">
14
- <button type="button" data-action="enter-xr">Enter VR</button>
15
- <button type="button" data-action="exit-xr">Exit VR</button>
16
- </div>
13
+ <section class="xr-preflight" data-preflight aria-labelledby="xr-preflight-title">
14
+ <div class="xr-preflight-copy">
15
+ <p class="xr-kicker">SkyKit XR</p>
16
+ <h1 id="xr-preflight-title">Free Roam Preflight</h1>
17
+ <p>
18
+ Choose the rendering defaults for this headset run, then enter the immersive session.
19
+ </p>
20
+ <dl class="xr-status-list">
21
+ <div>
22
+ <dt>WebXR</dt>
23
+ <dd data-xr-supported>Checking</dd>
24
+ </div>
25
+ <div>
26
+ <dt>Scale</dt>
27
+ <dd data-setting-value="worldScaleLog10">1 m/pc</dd>
28
+ </div>
29
+ <div>
30
+ <dt>Stars</dt>
31
+ <dd data-setting-value="limitingMagnitude">Mag 7.5</dd>
32
+ </div>
33
+ </dl>
34
+ </div>
35
+
36
+ <form class="xr-settings" data-xr-settings>
37
+ <fieldset>
38
+ <legend>Rendering</legend>
39
+ <label>
40
+ <span>Magnitude limit</span>
41
+ <output data-setting-value="limitingMagnitude">7.5</output>
42
+ <input data-setting="limitingMagnitude" type="range" min="4" max="10" step="0.1" value="7.5" />
43
+ </label>
44
+ <label>
45
+ <span>Exposure</span>
46
+ <output data-setting-value="exposureLog10">100,000</output>
47
+ <input data-setting="exposureLog10" type="range" min="3.5" max="5.5" step="0.05" value="5" />
48
+ </label>
49
+ <label>
50
+ <span>World scale</span>
51
+ <output data-setting-value="worldScaleLog10">1 m/pc</output>
52
+ <input data-setting="worldScaleLog10" type="range" min="-3" max="0" step="0.05" value="0" />
53
+ </label>
54
+ </fieldset>
55
+
56
+ <fieldset>
57
+ <legend>Session Flags</legend>
58
+ <label class="xr-toggle">
59
+ <input data-setting="nearFloor" type="checkbox" checked />
60
+ <span>Near-star readability floor</span>
61
+ </label>
62
+ <label class="xr-toggle">
63
+ <input data-setting="constellationArt" type="checkbox" checked />
64
+ <span>Constellation art</span>
65
+ </label>
66
+ </fieldset>
67
+
68
+ <div class="xr-actions" aria-label="XR actions">
69
+ <button class="xr-primary-action" type="button" data-action="enter-xr">Enter VR</button>
70
+ <button type="button" data-action="exit-xr" hidden disabled>Exit VR</button>
71
+ </div>
72
+ <p class="xr-session-status" data-session-status>Idle</p>
73
+ </form>
74
+ </section>
17
75
  </main>
18
76
  <script type="module" src="./xr-free-roam.js"></script>
19
77
  </body>
@@ -2,9 +2,20 @@ html,
2
2
  body {
3
3
  margin: 0;
4
4
  width: 100%;
5
+ min-width: 320px;
5
6
  height: 100%;
6
7
  overflow: hidden;
7
- background: #020712;
8
+ background: #05070b;
9
+ color: #f4efe2;
10
+ font: 16px/1.45 "Avenir Next", Avenir, "Segoe UI", sans-serif;
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ [hidden] {
18
+ display: none !important;
8
19
  }
9
20
 
10
21
  .xr-free-roam-shell,
@@ -13,39 +24,259 @@ body {
13
24
  inset: 0;
14
25
  }
15
26
 
27
+ .xr-free-roam-shell {
28
+ background:
29
+ linear-gradient(90deg, rgba(5, 7, 11, 0.96) 0%, rgba(5, 7, 11, 0.84) 46%, rgba(5, 7, 11, 0.58) 100%),
30
+ #05070b;
31
+ }
32
+
33
+ .viewer-shell {
34
+ z-index: 0;
35
+ opacity: 0.34;
36
+ filter: saturate(0.78) contrast(1.08);
37
+ pointer-events: none;
38
+ transition: opacity 0.18s ease, filter 0.18s ease;
39
+ }
40
+
41
+ .xr-free-roam-shell.is-presenting .viewer-shell {
42
+ opacity: 1;
43
+ filter: none;
44
+ pointer-events: auto;
45
+ }
46
+
16
47
  .viewer-shell canvas {
17
48
  display: block;
18
49
  width: 100%;
19
50
  height: 100%;
20
- cursor: grab;
51
+ cursor: default;
21
52
  touch-action: none;
22
53
  user-select: none;
23
54
  }
24
55
 
25
- .viewer-shell canvas:active {
26
- cursor: grabbing;
56
+ .xr-preflight {
57
+ position: fixed;
58
+ inset: 0;
59
+ z-index: 10;
60
+ display: grid;
61
+ grid-template-columns: minmax(0, 1fr) minmax(320px, 430px);
62
+ gap: clamp(24px, 5vw, 72px);
63
+ align-items: center;
64
+ width: 100%;
65
+ height: 100%;
66
+ padding: clamp(24px, 6vh, 72px) clamp(18px, 7vw, 92px);
67
+ overflow: auto;
68
+ }
69
+
70
+ .xr-free-roam-shell.is-presenting .xr-preflight {
71
+ display: none;
72
+ }
73
+
74
+ .xr-preflight-copy {
75
+ max-width: 720px;
76
+ }
77
+
78
+ .xr-kicker {
79
+ margin: 0 0 10px;
80
+ color: #6fd7c3;
81
+ font-size: 0.82rem;
82
+ font-weight: 700;
83
+ text-transform: uppercase;
84
+ }
85
+
86
+ .xr-preflight h1 {
87
+ margin: 0;
88
+ max-width: 11ch;
89
+ color: #fff9e8;
90
+ font-size: clamp(2.6rem, 6vh, 5.6rem);
91
+ line-height: 0.96;
92
+ font-weight: 760;
93
+ }
94
+
95
+ .xr-preflight-copy > p:not(.xr-kicker) {
96
+ max-width: 46rem;
97
+ margin: 22px 0 0;
98
+ color: #cfc7b8;
99
+ font-size: 1.08rem;
100
+ }
101
+
102
+ .xr-status-list {
103
+ display: grid;
104
+ grid-template-columns: repeat(3, minmax(0, 1fr));
105
+ gap: 1px;
106
+ max-width: 620px;
107
+ margin: 34px 0 0;
108
+ padding: 1px;
109
+ background: rgba(244, 239, 226, 0.14);
110
+ border-radius: 8px;
111
+ overflow: hidden;
112
+ }
113
+
114
+ .xr-status-list div {
115
+ min-width: 0;
116
+ padding: 13px 14px;
117
+ background: rgba(15, 18, 18, 0.88);
118
+ }
119
+
120
+ .xr-status-list dt {
121
+ color: #a7b2ac;
122
+ font-size: 0.76rem;
123
+ font-weight: 700;
124
+ text-transform: uppercase;
125
+ }
126
+
127
+ .xr-status-list dd {
128
+ margin: 4px 0 0;
129
+ color: #fff6dd;
130
+ font-weight: 700;
131
+ overflow-wrap: anywhere;
132
+ }
133
+
134
+ .xr-settings {
135
+ display: grid;
136
+ gap: 14px;
137
+ width: min(100%, 430px);
138
+ padding: 18px;
139
+ border: 1px solid rgba(244, 239, 226, 0.18);
140
+ border-radius: 8px;
141
+ background: rgba(14, 17, 16, 0.88);
142
+ box-shadow: 0 24px 80px rgba(0, 0, 0, 0.28);
143
+ }
144
+
145
+ .xr-settings fieldset {
146
+ display: grid;
147
+ gap: 14px;
148
+ min-width: 0;
149
+ margin: 0;
150
+ padding: 0 0 16px;
151
+ border: 0;
152
+ border-bottom: 1px solid rgba(244, 239, 226, 0.13);
153
+ }
154
+
155
+ .xr-settings fieldset:last-of-type {
156
+ padding-bottom: 2px;
157
+ border-bottom: 0;
158
+ }
159
+
160
+ .xr-settings legend {
161
+ padding: 0;
162
+ color: #d9b779;
163
+ font-size: 0.82rem;
164
+ font-weight: 760;
165
+ text-transform: uppercase;
166
+ }
167
+
168
+ .xr-settings label:not(.xr-toggle) {
169
+ display: grid;
170
+ grid-template-columns: 1fr auto;
171
+ gap: 8px 14px;
172
+ align-items: center;
173
+ color: #f4efe2;
174
+ font-weight: 650;
175
+ }
176
+
177
+ .xr-settings output {
178
+ color: #9ddbd1;
179
+ font-variant-numeric: tabular-nums;
180
+ font-weight: 760;
181
+ text-align: right;
182
+ }
183
+
184
+ .xr-settings input[type="range"] {
185
+ grid-column: 1 / -1;
186
+ width: 100%;
187
+ accent-color: #d9b779;
188
+ }
189
+
190
+ .xr-toggle {
191
+ display: grid;
192
+ grid-template-columns: 22px 1fr;
193
+ gap: 10px;
194
+ align-items: center;
195
+ min-height: 32px;
196
+ color: #e8dfcf;
197
+ font-weight: 650;
198
+ }
199
+
200
+ .xr-toggle input {
201
+ width: 18px;
202
+ height: 18px;
203
+ accent-color: #6fd7c3;
27
204
  }
28
205
 
29
206
  .xr-actions {
30
- position: fixed;
31
- top: 16px;
32
- right: 16px;
33
- z-index: 10;
34
207
  display: flex;
35
- gap: 8px;
208
+ flex-wrap: wrap;
209
+ gap: 10px;
36
210
  }
37
211
 
38
212
  .xr-actions button {
39
- min-height: 38px;
40
- padding: 0 14px;
41
- border: 1px solid rgba(145, 220, 255, 0.35);
213
+ min-height: 42px;
214
+ padding: 0 16px;
215
+ border: 1px solid rgba(244, 239, 226, 0.24);
42
216
  border-radius: 8px;
43
- background: rgba(5, 13, 26, 0.76);
44
- color: #eef8ff;
45
- font: 600 0.9rem/1 "Avenir Next", Avenir, "Segoe UI", sans-serif;
217
+ background: rgba(244, 239, 226, 0.08);
218
+ color: #fff9e8;
219
+ font: inherit;
220
+ font-weight: 760;
221
+ }
222
+
223
+ .xr-actions button:hover:not(:disabled) {
224
+ border-color: rgba(244, 239, 226, 0.72);
225
+ background: rgba(244, 239, 226, 0.13);
226
+ }
227
+
228
+ .xr-actions button:disabled {
229
+ cursor: not-allowed;
230
+ opacity: 0.55;
231
+ }
232
+
233
+ .xr-actions .xr-primary-action {
234
+ min-width: 144px;
235
+ border-color: rgba(217, 183, 121, 0.72);
236
+ background: #d9b779;
237
+ color: #101313;
238
+ }
239
+
240
+ .xr-actions .xr-primary-action:hover:not(:disabled) {
241
+ border-color: #ffe0a6;
242
+ background: #e8c987;
46
243
  }
47
244
 
48
- .xr-actions button:hover {
49
- border-color: rgba(145, 220, 255, 0.8);
50
- background: rgba(10, 25, 44, 0.9);
245
+ .xr-session-status {
246
+ min-height: 1.4em;
247
+ margin: 0;
248
+ color: #a7b2ac;
249
+ font-size: 0.88rem;
250
+ }
251
+
252
+ @media (max-width: 820px) {
253
+ .xr-preflight {
254
+ grid-template-columns: 1fr;
255
+ align-content: start;
256
+ }
257
+
258
+ .xr-preflight h1 {
259
+ max-width: 13ch;
260
+ font-size: clamp(2.3rem, 12vw, 4.4rem);
261
+ }
262
+
263
+ .xr-status-list {
264
+ grid-template-columns: 1fr;
265
+ }
266
+
267
+ .xr-settings {
268
+ width: 100%;
269
+ }
270
+ }
271
+
272
+ @media (max-height: 640px) and (min-width: 821px) {
273
+ .xr-preflight {
274
+ align-items: start;
275
+ padding-top: 28px;
276
+ padding-bottom: 28px;
277
+ }
278
+
279
+ .xr-preflight h1 {
280
+ font-size: 3.4rem;
281
+ }
51
282
  }