@found-in-space/skykit 0.2.0-alpha.1 → 0.2.0-alpha.20260528

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 (40) hide show
  1. package/README.md +142 -11
  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 +675 -274
  6. package/package.json +22 -6
  7. package/src/__tests__/skykit-anchored-images.test.js +32 -4
  8. package/src/__tests__/skykit-browser.test.js +267 -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 +179 -2
  13. package/src/__tests__/skykit.test.js +142 -506
  14. package/src/actions.js +0 -8
  15. package/src/anchored-images.js +14 -15
  16. package/src/browser-addons.d.ts +16 -0
  17. package/src/browser-addons.js +155 -0
  18. package/src/browser-constellations.d.ts +13 -0
  19. package/src/browser-constellations.js +387 -0
  20. package/src/browser.d.ts +81 -0
  21. package/src/browser.js +192 -13
  22. package/src/data.d.ts +133 -0
  23. package/src/data.js +447 -0
  24. package/src/embed.d.ts +5 -0
  25. package/src/embed.js +53 -2
  26. package/src/hr-diagram.js +23 -5
  27. package/src/index.d.ts +21 -73
  28. package/src/index.js +0 -1
  29. package/src/plugins.js +22 -708
  30. package/src/three-shim.d.ts +32 -0
  31. package/src/touch-os.d.ts +70 -0
  32. package/src/touch-os.js +275 -0
  33. package/src/utils.js +96 -6
  34. package/src/viewer-entry.d.ts +10 -0
  35. package/src/viewer-entry.js +4 -0
  36. package/src/viewer.js +110 -12
  37. package/src/xr/plugins.js +298 -13
  38. package/src/xr/session.js +60 -14
  39. package/src/xr.d.ts +40 -0
  40. package/src/xr.js +2 -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
+
32
+ `embed.js` is the no-code viewer entry. It is not a separate use-case.
33
+ `viewer.js` is the JavaScript-customizable viewer entry. `data.js` is renderer
34
+ independent. Authored chapters stay in website or lesson code and call SkyKit
35
+ navigation actions directly.
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,73 @@ 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"
74
+ data-skykit-persistent-cache="off"
58
75
  style="width: 100%; height: 520px; background: #02040b"
59
76
  ></div>
60
77
  ```
61
78
 
79
+ `data-skykit-look-at` accepts RA/Dec text such as
80
+ `ra=4.496h, dec=16.948`, decimal degrees such as `67.447,16.948`, or a
81
+ parsec-space `x,y,z` target for exact generated coordinates. `data-skykit-mouse-mode`
82
+ defaults to `grab`; use `look` or `strafe` for the first-person mouse-look
83
+ direction, or `none` to disable mouse drag controls. Persistent browser Cache API
84
+ storage is enabled by default for octree ranges; set
85
+ `data-skykit-persistent-cache="off"` to keep caching session-only.
86
+
62
87
  The host dispatches `skykit-browser-ready` with `{ browser, viewer }` in
63
88
  `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.
89
+ embed also installs a small `Skykit` global for noob-path scripts:
65
90
 
66
- Pin the CDN URL to a released SkyKit version when publishing long-lived pages,
67
- for example
91
+ ```js
92
+ const browser = await Skykit.whenReady();
93
+ ```
94
+
95
+ Pages can host multiple viewers. Pass a selector or element to choose one:
96
+
97
+ ```js
98
+ const browser = await Skykit.whenReady('#orion-viewer');
99
+ ```
100
+
101
+ Pin the package CDN URL to a released SkyKit version when publishing long-lived
102
+ pages, for example
68
103
  `https://esm.sh/@found-in-space/skykit@x.y.z/embed?bundle&deps=three@0.170.0`.
69
104
 
70
- ## Create a browser from JavaScript
105
+ Optional first-party capabilities stay out of the initial browser until they are
106
+ requested. This keeps the one-script noob path while avoiding bundle bloat.
107
+
108
+ ```html
109
+ <div
110
+ data-skykit-browser
111
+ data-skykit-constellations="western"
112
+ data-skykit-constellation-art="off"
113
+ style="width:100%;height:520px;background:#02040b"
114
+ ></div>
115
+ ```
116
+
117
+ For small scripted interactions, use the browser handle:
118
+
119
+ ```html
120
+ <script type="module">
121
+ import {
122
+ SKYKIT_ACTIONS,
123
+ createSkykitNavigationPlugin,
124
+ } from 'https://esm.sh/@found-in-space/skykit';
125
+
126
+ const browser = await Skykit.whenReady();
127
+ await browser.install(createSkykitNavigationPlugin());
128
+
129
+ document.querySelector('#orion').addEventListener('click', () => {
130
+ browser.viewer.actions.invoke(SKYKIT_ACTIONS.navigation.transitionTo, {
131
+ view: { lookAt: 'ra=5.919h, dec=7.407' },
132
+ movement: { durationSecs: 3 },
133
+ });
134
+ });
135
+ </script>
136
+ ```
137
+
138
+ ## Create a Viewer from JavaScript
71
139
 
72
140
  If your site has a module script, npm, or a bundler, call the helper directly:
73
141
 
@@ -76,7 +144,7 @@ If your site has a module script, npm, or a bundler, call the helper directly:
76
144
  <pre id="status">Loading stars...</pre>
77
145
 
78
146
  <script type="module">
79
- import { createSkykitBrowser } from '@found-in-space/skykit/browser';
147
+ import { createSkykitBrowser } from 'https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/viewer?bundle&deps=three@0.170.0';
80
148
 
81
149
  await createSkykitBrowser({
82
150
  host: '#viewer',
@@ -91,10 +159,52 @@ The helper still returns the pieces when a lesson wants to grow:
91
159
  const sky = await createSkykitBrowser('#viewer');
92
160
 
93
161
  sky.viewer.requestViewState({ observerPc: { x: 4, y: 0, z: -8 } });
162
+ sky.addObject(marker, {
163
+ positionPc: { x: 17.574, y: 42.316, z: 13.963 },
164
+ });
94
165
  sky.loop.stop();
95
166
  await sky.dispose();
96
167
  ```
97
168
 
169
+ For npm or bundlers, use the same beginner entry:
170
+
171
+ ```js
172
+ import { THREE, createSkykitBrowser } from '@found-in-space/skykit/viewer';
173
+ ```
174
+
175
+ ## Use Star Data Without a Viewer
176
+
177
+ Use `data.js` when SkyKit should supply rows and your app should own rendering:
178
+
179
+ ```js
180
+ import { loadStarRows } from 'https://esm.sh/@found-in-space/skykit@0.2.0-alpha.2/data?bundle';
181
+
182
+ const stars = await loadStarRows({
183
+ limitingMagnitude: 6.5,
184
+ maxStars: 100,
185
+ sortBy: 'apparentMagnitude',
186
+ });
187
+
188
+ console.table(stars);
189
+ ```
190
+
191
+ For games and maps, load a local volume and hand rows to Canvas, PixiJS,
192
+ Phaser, SVG, or your own renderer:
193
+
194
+ ```js
195
+ const stars = await loadStarRows({
196
+ centerPc: { x: 0, y: 0, z: 0 },
197
+ radiusPc: 50,
198
+ maxStars: 2000,
199
+ });
200
+ ```
201
+
202
+ ## Author Chapters
203
+
204
+ Keep named chapters in the website or lesson script. Each chapter can call
205
+ navigation actions such as `skykit:navigation.transitionTo` and
206
+ `skykit:navigation.orbit` from its own `goTo(id)` dispatcher.
207
+
98
208
  Use the lower-level factories when a lesson is teaching composition or replacing
99
209
  a part of the stack:
100
210
 
@@ -208,7 +318,7 @@ names, not renderer or loader factory names:
208
318
  SKYKIT_ACTIONS.ship.moveForward; // "skykit:ship.move.forward"
209
319
  SKYKIT_CONTROLS.observer.parallaxOffset; // "skykit:observer.control.parallaxOffset"
210
320
  SKYKIT_ACTIONS.viewer.reset; // "skykit:viewer.reset"
211
- SKYKIT_ACTIONS.journey.goToChapter; // "skykit:journey.goToChapter"
321
+ SKYKIT_ACTIONS.navigation.transitionTo; // "skykit:navigation.transitionTo"
212
322
  ```
213
323
 
214
324
  Plugins can add their own namespaces:
@@ -221,13 +331,13 @@ const firePlugin = (ctx) => {
221
331
  };
222
332
  ```
223
333
 
224
- DOM buttons, touch surfaces, keyboard bindings, XR controls, journeys, and debug
225
- tools can all call the same action:
334
+ DOM buttons, touch surfaces, keyboard bindings, XR controls, app-owned chapters,
335
+ and debug tools can all call the same action:
226
336
 
227
337
  ```js
228
338
  button.addEventListener('click', () => {
229
- viewer.actions.invoke(SKYKIT_ACTIONS.journey.goToChapter, {
230
- chapterId: 'hyades-arrival',
339
+ viewer.actions.invoke('website:chapter.goTo', {
340
+ id: 'hyades-arrival',
231
341
  });
232
342
  });
233
343
  ```
@@ -263,6 +373,27 @@ For a slightly more playful example, see `examples/plugin-lab.js`. It builds
263
373
  app-owned Three objects and action-driven annotations from the same public hooks
264
374
  a learner would use.
265
375
 
376
+ The pasteable browser embed has a smaller add-on convention for noob pages:
377
+
378
+ ```js
379
+ Skykit.registerBrowserAddon({
380
+ id: 'lesson:marker',
381
+ install({ browser, THREE }) {
382
+ const marker = new THREE.Mesh(
383
+ new THREE.SphereGeometry(0.02),
384
+ new THREE.MeshBasicMaterial({ color: 0xffcc00 }),
385
+ );
386
+ const handle = browser.addObject(marker, {
387
+ positionPc: { x: 17.574, y: 42.316, z: 13.963 },
388
+ });
389
+ return () => handle.dispose();
390
+ },
391
+ });
392
+ ```
393
+
394
+ See `docs/skykit-browser-plugins.md` for the browser add-on spec,
395
+ `Skykit.whenReady()`, and first-party constellation support.
396
+
266
397
  Browser lessons:
267
398
 
268
399
  - `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
  }