Spaces:
Configuration error
Configuration error
| @import "tailwindcss"; | |
| @import "@nuxt/ui"; | |
| html, | |
| body, | |
| #cesiumContainer { | |
| width: 100%; | |
| height: 100%; | |
| margin: 0; | |
| padding: 0; | |
| position: fixed; | |
| overflow: hidden; | |
| font-family: sans-serif; | |
| color: #edffff; | |
| background-color: black; | |
| } | |
| body { | |
| -webkit-touch-callout: none; | |
| user-select: none; | |
| } | |
| /* Hides #app until Vue mounts. The `dark` class index.html sets alongside it forces | |
| Nuxt UI onto its dark palette, since Vue mode has no color-mode switch and | |
| defaults to light while this app is a dark globe ui throughout. */ | |
| [v-cloak] { | |
| display: none; | |
| } | |
| /* The safe area, named once. `viewport-fit=cover` in index.html is what makes these | |
| report anything: without it iOS keeps them 0 even where the page already reaches | |
| under the home indicator. The top is 0 on every iOS layout, and is carried for an | |
| Android install, which is edge to edge. */ | |
| :root { | |
| --safe-top: env(safe-area-inset-top, 0px); | |
| --safe-right: env(safe-area-inset-right, 0px); | |
| --safe-bottom: env(safe-area-inset-bottom, 0px); | |
| --safe-left: env(safe-area-inset-left, 0px); | |
| /* Cesium's credit line in the bottom-left corner: the inset lifts it off the home | |
| indicator, and the screen's rounded corner then cuts into the left. The same inset | |
| scales how much of 12px that costs, so a square screen keeps Cesium's own 5/3. */ | |
| --credit-corner-bottom: calc(3px + var(--safe-bottom)); | |
| --credit-corner-left: calc(5px + var(--safe-left) + min(12px, var(--safe-bottom))); | |
| /* With no deck to clear, the corner. `body.clock-deck` moves the line up. */ | |
| --credit-bottom: var(--credit-corner-bottom); | |
| --credit-left: var(--credit-corner-left); | |
| } | |
| /* Above the sky view's HUD (z-4) and the entity info panel (z-5), so the control | |
| that leaves the sky view stays reachable while it is up. Sideways the notch is | |
| beside these rows, and without the inset they slide under it. */ | |
| #toolbarLeft { | |
| position: absolute; | |
| top: calc(5px + var(--safe-top)); | |
| left: calc(2px + var(--safe-left)); | |
| z-index: 6; | |
| } | |
| #toolbarRight { | |
| position: absolute; | |
| top: calc(5px + var(--safe-top)); | |
| right: calc(2px + var(--safe-right)); | |
| z-index: 6; | |
| } | |
| /* Cesium styles these buttons for a mouse, and on iOS a tap leaves `:hover` and | |
| `:focus` behind on whatever it touched last. The lit state is bound to `menu` in | |
| Satvis.vue instead; these put the pointer states back to base. */ | |
| #toolbarLeft .cesium-button:not(.toolbarButton--open):focus, | |
| #toolbarRight .cesium-button:not(.toolbarButton--open):focus { | |
| border-color: #444; | |
| } | |
| /* Only where a pointer can actually rest on a button. Elsewhere :hover is whatever | |
| was tapped last. */ | |
| @media (hover: none) { | |
| #toolbarLeft .cesium-button:not(.toolbarButton--open):hover, | |
| #toolbarRight .cesium-button:not(.toolbarButton--open):hover { | |
| background: #303336; | |
| border-color: #444; | |
| color: #edffff; | |
| fill: #edffff; | |
| box-shadow: none; | |
| } | |
| } | |
| /* The propagation panel deliberately reuses Satvis's compact toolbar language | |
| instead of creating a competing application shell. */ | |
| .propagation-panel { | |
| width: min(330px, calc(100vw - 20px)); | |
| max-height: min(76vh, 760px); | |
| overflow: auto; | |
| padding-bottom: 10px; | |
| } | |
| .propagation-panel__intro, | |
| .propagation-panel__note, | |
| .propagation-panel__status { | |
| margin: 7px 12px 10px; | |
| color: #bfd0d8; | |
| font-size: 12px; | |
| line-height: 1.35; | |
| } | |
| .propagation-panel__note { | |
| color: #9eacb4; | |
| font-size: 11px; | |
| } | |
| .propagation-panel__status { | |
| min-height: 32px; | |
| color: #82e6f4; | |
| } | |
| .propagation-panel__status--error { | |
| color: #ffac99; | |
| } | |
| .propagation-panel__grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 8px; | |
| padding: 0 12px 12px; | |
| } | |
| .propagation-panel__grid label { | |
| display: grid; | |
| gap: 4px; | |
| color: #d8e4e9; | |
| font-size: 11px; | |
| } | |
| .propagation-panel__grid span { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| color: #92acb9; | |
| } | |
| .propagation-panel__grid input { | |
| box-sizing: border-box; | |
| width: 100%; | |
| min-width: 0; | |
| border: 1px solid #58676f; | |
| border-radius: 3px; | |
| background: #1d2428; | |
| color: #e7fbff; | |
| font: inherit; | |
| padding: 5px 6px; | |
| } | |
| .propagation-panel__details { | |
| margin: 8px 12px 12px; | |
| color: #d8e4e9; | |
| font-size: 12px; | |
| } | |
| .propagation-panel__details summary { | |
| cursor: pointer; | |
| margin-bottom: 10px; | |
| } | |
| .propagation-panel__details .propagation-panel__grid { | |
| padding: 0; | |
| } | |
| .propagation-panel__run { | |
| display: block; | |
| width: calc(100% - 24px); | |
| margin: 12px; | |
| border: 1px solid #5cd8ec; | |
| border-radius: 4px; | |
| background: #176274; | |
| color: #f2ffff; | |
| cursor: pointer; | |
| font-size: 13px; | |
| font-weight: 600; | |
| padding: 9px 10px; | |
| } | |
| .propagation-panel__run:hover, | |
| .propagation-panel__run:focus-visible { | |
| background: #23899e; | |
| } | |
| /* Cesium's `outline: none` leaves keyboard focus with nothing to show for it. */ | |
| #toolbarLeft .cesium-button:focus-visible, | |
| #toolbarRight .cesium-button:focus-visible { | |
| outline: 2px solid #7fd2ea; | |
| outline-offset: 2px; | |
| } | |
| /* The button whose panel is open. The shadow is cleared so it looks the same whether | |
| or not a pointer also happens to be resting on it. */ | |
| #toolbarLeft .toolbarButton--open, | |
| #toolbarRight .toolbarButton--open { | |
| background: #3d4348; | |
| border-color: #7fd2ea; | |
| color: #7fd2ea; | |
| fill: #7fd2ea; | |
| box-shadow: none; | |
| } | |
| .toolbarSwitches { | |
| display: flex; | |
| flex-direction: column; | |
| margin: 2px 3px; | |
| /* Carries the group titles, which have no surface of their own — the rows do, | |
| so the panel is what has to hold them legible against a bright globe. */ | |
| background-color: #303336d9; | |
| border-radius: 8px; | |
| } | |
| /* The satellite catalog panel is a dense text list; a more opaque surface | |
| keeps globe labels from bleeding through the rows. */ | |
| .toolbarSwitches--catalog { | |
| background-color: #303336e6; | |
| } | |
| .toolbarTitle { | |
| text-align: center; | |
| height: 24px; | |
| line-height: 24px; | |
| margin-top: 5px; | |
| } | |
| .toolbarContent { | |
| margin: 5px; | |
| } | |
| /* A control whose selection a surface model has taken over, so it is no longer | |
| describing what is on screen. Dimming says only that; whether the row is still | |
| the user's to change is a separate question the markup answers per group (a | |
| hidden globe leaves the choice theirs, an imposed terrain does not). | |
| Opacity only, deliberately. A `grayscale` filter here read as a broken panel | |
| rather than an inert one: the red/green dot is the only state a row carries, | |
| and draining its colour left a column of switches saying nothing at all. */ | |
| .toolbarSwitch--inert { | |
| opacity: 0.3; | |
| } | |
| .toolbarTitle--inert { | |
| opacity: 0.5; | |
| } | |
| /* Why nothing happened, for a surface model that cannot apply in this view mode. | |
| Zero width with a full-width minimum so the sentence wraps inside the panel | |
| instead of widening it: these panels are sized by their content, and the | |
| longest row is what should set that width. */ | |
| .toolbarNote { | |
| width: 0; | |
| min-width: 100%; | |
| box-sizing: border-box; | |
| padding: 2px 8px 3px 8px; | |
| font-size: 11px; | |
| line-height: 14px; | |
| opacity: 0.6; | |
| } | |
| .toolbarSwitch { | |
| /* Lighter than the panel it sits on, not the same colour. These were both | |
| #303336, which was invisible while the panel was translucent enough to show | |
| the globe through the gaps — and once the panel was made opaque, the rows and | |
| their surface merged into one flat slab with no edges at all. */ | |
| background-color: #464b50; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| display: inline-block; | |
| height: 24px; | |
| line-height: 24px; | |
| margin: 1px 0px 1px 0px; | |
| opacity: 0.85; | |
| padding-left: 50px; | |
| position: relative; | |
| } | |
| .toolbarSwitch input { | |
| display: none; | |
| } | |
| /* Waiting on the device — a location fix, or the orientation sensor answering a | |
| probe. Not clickable, so a second tap cannot queue a second request while the | |
| first is still out. */ | |
| .toolbarSwitch:has(input:disabled) { | |
| cursor: default; | |
| } | |
| /* Sits exactly where the slider's dot does, and is positioned rather than inline | |
| so that showing it costs no width: these panels are sized by their content, so | |
| anything that grows a row reflows the whole menu. That is why the wait is not | |
| said in the label. */ | |
| .toolbarSpinner { | |
| position: absolute; | |
| left: 4px; | |
| bottom: 4px; | |
| box-sizing: border-box; | |
| height: 16px; | |
| width: 16px; | |
| border: 2px solid #ffffff40; | |
| border-top-color: #ffffff; | |
| border-radius: 50%; | |
| animation: toolbarSpin 0.8s linear infinite; | |
| } | |
| @keyframes toolbarSpin { | |
| to { | |
| transform: rotate(1turn); | |
| } | |
| } | |
| /* Still a ring rather than a dot, so it reads as busy without moving. */ | |
| @media (prefers-reduced-motion: reduce) { | |
| .toolbarSpinner { | |
| animation: none; | |
| } | |
| } | |
| .slider::before { | |
| position: absolute; | |
| content: ""; | |
| height: 16px; | |
| width: 16px; | |
| left: 4px; | |
| bottom: 4px; | |
| border-radius: 50%; | |
| background-color: red; | |
| transition: 0.4s; | |
| } | |
| input:checked + .slider::before { | |
| background-color: green; | |
| transform: translateX(20px); | |
| } | |
| .cesium-toolbar-button { | |
| font-size: 22px; | |
| color: #ffffff; | |
| } | |
| /* Center the Nuxt UI <svg> in every app toolbar button. Applies to both <a> and | |
| <button>; without flex the block-level Iconify SVGs hug the button's left | |
| edge. */ | |
| #toolbarLeft .cesium-toolbar-button, | |
| #toolbarRight .cesium-toolbar-button { | |
| align-items: center; | |
| justify-content: center; | |
| display: inline-flex; | |
| } | |
| /* Cesium's ion logo at 80%. Its guide allows placement and presentation to be | |
| adjusted while the logo stays prominent, and the line's width is what the deck's | |
| breakpoints are measured against. | |
| https://cesium.com/learn/ion/content-usage-and-attribution-guide/ */ | |
| .cesium-credit-logoContainer img { | |
| height: 22px; | |
| width: auto; | |
| } | |
| /* The clock deck shares the bottom of the screen with Cesium's credit line: how much | |
| room the deck claims, and where the credits go around it. */ | |
| body.clock-deck { | |
| /* Every offset below is in terms of this, or a notch grows the deck past the line. */ | |
| --clock-deck-safe: max(6px, var(--safe-bottom)); | |
| /* What the deck occupies, for the sky view's cards. Constant across the fold. */ | |
| --clock-deck-height: calc(88px + var(--clock-deck-safe)); | |
| /* Clear of the deck: the default, and where a phone puts them. Far enough up that | |
| the corner is no concern, so the left goes back to Cesium's own. */ | |
| --credit-bottom: calc(var(--clock-deck-height) + 4px); | |
| --credit-left: calc(5px + var(--safe-left)); | |
| } | |
| /* In the control row beside the surface: 28 px of credit centred in its 46. */ | |
| body.clock-deck[data-clock-deck="beside"] { | |
| --credit-bottom: calc(51px + var(--clock-deck-safe)); | |
| } | |
| /* Folded, the reserved scale row is more room than the credit line needs, which | |
| leaves the rounded corner as the only thing to clear. */ | |
| body.clock-deck[data-clock-deck="folded"] { | |
| --credit-bottom: var(--credit-corner-bottom); | |
| --credit-left: var(--credit-corner-left); | |
| } | |
| /* `!important` because Cesium rewrites this element's `bottom` inline on every | |
| resize. Unscoped: the corner is the viewport's, deck or no deck. */ | |
| .cesium-viewer-bottom { | |
| bottom: var(--credit-bottom) ; | |
| left: var(--credit-left) ; | |
| } | |
| /* The width cap stops its full-row container swallowing taps meant for the deck. */ | |
| body.clock-deck .cesium-viewer-bottom { | |
| right: auto ; | |
| width: max-content ; | |
| } | |
| /* Below the cap the scale row covers the corner Cesium puts the fullscreen button in. | |
| Hidden, not unbuilt, so it returns with the gutter. `!important` for the reason | |
| above. */ | |
| @media (max-width: 999px) { | |
| body.clock-deck .cesium-viewer-fullscreenContainer { | |
| display: none ; | |
| } | |
| } | |
| /* Room for a phone-width deck and the line side by side: 560 plus twice the 206 px | |
| box it ends at, plus air. The box, not its 196 px of content. Both with the logo at | |
| 22 px; 236 and 226 at Cesium's own 28. */ | |
| @media (min-width: 1000px) { | |
| /* On `:root`, not `body.clock-deck`: the class arrives in the deck's `onMounted`, | |
| and the timeline measures its own width in that same tick. */ | |
| :root { | |
| --clock-deck-max: 560px; | |
| } | |
| /* Cesium's own corner, for the two cases that hold the line above the deck: the | |
| deck caps and centres at this width and stops sharing the row. */ | |
| body.clock-deck[data-clock-deck="clear"], | |
| body.clock-deck[data-clock-deck="beside"] { | |
| --credit-bottom: var(--credit-corner-bottom); | |
| --credit-left: var(--credit-corner-left); | |
| } | |
| } | |