/* Black Nova — observatory UI.
   Brand: two colours only. #000000 plus the current star's colour (--star). Archivo (wdth 125, wght 800) for
   display, IBM Plex Mono 500 for labels. --star animates between colourways via @property. */

@property --star {
  syntax: '<color>';
  inherits: true;
  initial-value: #EEE8DE;
}

:root {
  --star: #EEE8DE;
  --ink: #000000;
  --t-dim: color-mix(in srgb, var(--star) 64%, transparent);
  --t-faint: color-mix(in srgb, var(--star) 38%, transparent);
  --hair: color-mix(in srgb, var(--star) 22%, transparent);
  --display: 'Archivo', 'Arial Black', system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Menlo, monospace;
  --gutter: clamp(16px, 3.2vw, 48px);
  --ease: cubic-bezier(.16, .84, .24, 1);
  color-scheme: dark;
  transition: --star 1.6s var(--ease);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: #000;
  color: var(--star);
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  font-family: var(--display);
  font-stretch: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  -webkit-tap-highlight-color: transparent;
}

button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
:focus-visible { outline: 1px solid var(--star); outline-offset: 4px; }

.mono {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: .14em;
  text-transform: uppercase;
}

#gl {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  display: block;
  touch-action: none;
  cursor: grab;
  opacity: 0;
  transition: opacity .6s var(--ease);
}
#gl.is-dragging { cursor: grabbing; }
/* the live scene fades in over the shell once its shaders are compiled (main.js boot) */
body.scene-in #gl { opacity: 1; }

/* The shell: the mark drawn by the page until the live scene is in, laid out from the same camera maths (main.js
   placeShell). Under the canvas, so the scene simply fades in over it; the light half is the same colour both ways. */
.shell { position: fixed; inset: 0; pointer-events: none; overflow: hidden; }
.shell i { position: absolute; left: 0; top: 0; display: block; }
#shell-light { bottom: 0; width: 0; background: var(--star); }
#shell-dot { border-radius: 50%; background: #000; }
#shell-star {
  border-radius: 50%;
  opacity: 0;
  /* matched to the rendered Sun on the landing: a solid disc to ~1.5 radii (the bright limb blooms), a quick
     falloff, and a faint glow gone by 3 radii */
  background: radial-gradient(circle closest-side, #fffaf2 0, var(--star) 40%, var(--star) 50%,
    color-mix(in srgb, var(--star) 22%, transparent) 58%, color-mix(in srgb, var(--star) 6%, transparent) 75%, transparent 100%);
}

/* ---------------------------------------------------------------- HUD */

.hud {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}
.hud > * { pointer-events: auto; }
.hud .transit { pointer-events: none; }

/* one soft scrim along the bottom, under the star's name, the controls and the rail (UI, not the logo) */
.hud::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: min(46vh, 380px);
  background: linear-gradient(0deg, rgba(0,0,0,.62), rgba(0,0,0,.24) 50%, transparent);
  pointer-events: none;
  z-index: -1;
  transition: opacity 1s var(--ease);
}

.top {
  position: absolute;
  top: var(--gutter);
  left: var(--gutter);
  right: var(--gutter);
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
}
.top > * { pointer-events: auto; }

.brand { display: block; width: clamp(150px, 13vw, 210px); line-height: 0; }
.brand svg { width: 100%; height: auto; display: block; }
/* Brand rule: only #EEE8DE changes; #000000 never does. */
.brand [fill="#EEE8DE"] { fill: var(--star); }
.brand [stroke="#EEE8DE"] { stroke: var(--star); }

/* quality: three stops (L, M, H) and Auto, said quietly; it comes up under the pointer */
.qual {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 10px;
  letter-spacing: .26em;
  color: var(--t-faint);
  transition: color .4s var(--ease);
}
.qual:hover, .qual:focus-within { color: var(--t-dim); }
.qual-k { cursor: pointer; }
.qual b { display: inline-block; min-width: 1.2ch; text-align: center; font-weight: 500; color: var(--t-dim); }
.qual-auto { letter-spacing: inherit; padding: 6px 0; transition: color .4s var(--ease); }
.qual-auto[aria-pressed="true"] { color: var(--star); }
.qual-auto:focus-visible { outline-offset: 2px; }
.qual input {
  -webkit-appearance: none;
  appearance: none;
  width: 64px;
  height: 16px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  --p: 50%;
}
.qual input::-webkit-slider-runnable-track {
  height: 1px;
  background: linear-gradient(90deg, var(--t-dim) var(--p), var(--hair) var(--p));
}
.qual input::-moz-range-track { height: 1px; background: var(--hair); }
.qual input::-moz-range-progress { height: 1px; background: var(--t-dim); }
.qual input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 7px; height: 7px;
  margin-top: -3px;
  border-radius: 50%;
  background: var(--t-dim);
}
.qual input::-moz-range-thumb { width: 7px; height: 7px; border: 0; border-radius: 50%; background: var(--t-dim); }
.qual input:focus-visible { outline: none; }
.qual input:focus-visible::-webkit-slider-thumb { background: var(--star); box-shadow: 0 0 0 3px #000, 0 0 0 4px var(--star); }

/* ---------------------------------------------------------------- the star */

/* The star, said simply: its place in the catalogue, its name, what it is and how far (its facts are the Star
   Tour's). In flight the overlay (.transit) takes its place, the destination's name set the same way, so the name
   stays where it is as the panel comes back. */
.panel, .transit {
  position: absolute;
  left: var(--gutter);
  bottom: calc(var(--gutter) + 100px);
  width: min(1000px, 54vw);
}
/* Leaving for a flight, the panel sinks and blurs away; coming back it only fades in, already in place, as the
   flight overlay (the same name in the same spot) fades out: on arrival the name doesn't move at all. */
.panel { transition: opacity .7s var(--ease); }
.panel.is-out { opacity: 0; transform: translateY(12px); filter: blur(6px);
  transition: opacity .7s var(--ease), transform .9s var(--ease), filter .7s var(--ease); }
.kicker { margin: 0 0 14px; color: var(--t-faint); font-size: 10px; letter-spacing: .32em; }
.name, .t-name {
  margin: 0;
  font-family: var(--display);
  font-stretch: 125%;
  font-weight: 800;
  font-size: clamp(40px, 4.8vw, 92px);
  line-height: .88;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--star);
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}
.p-sub, .t-dist { margin: 18px 0 0; color: var(--t-dim); font-size: 10px; letter-spacing: .26em; }
.p-sub { display: flex; align-items: center; flex-wrap: wrap; row-gap: 6px; }
.p-sub .sep { display: inline-block; width: 28px; height: 1px; margin: 0 14px; background: var(--t-faint); }

.transit { opacity: 0; transform: translateY(12px); transition: opacity .6s var(--ease), transform 0s linear .6s; }
.transit.is-on { opacity: 1; transform: none; transition: opacity .6s var(--ease), transform .8s var(--ease); }
.t-dist { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- gravity and the views */

.bhmod {
  position: absolute;
  right: var(--gutter);
  bottom: calc(var(--gutter) + 88px);
  width: min(264px, 24vw);
  display: grid;
  gap: 20px;
  transition: opacity .6s var(--ease);
}
.grav label { display: flex; justify-content: space-between; font-size: 10px; letter-spacing: .26em; color: var(--t-dim); cursor: pointer; }
.grav b { font-weight: 500; color: var(--star); }
/* the slider: a hairline the star's colour fills, and on it a small black hole whose glow grows with its pull
   (--g, 0 to 1: hud.js setGravityUI) */
.grav input {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 24px;
  margin: 6px 0 0;
  background: transparent;
  cursor: pointer;
  --p: 0%;
  --g: 0;
}
.grav input::-webkit-slider-runnable-track {
  height: 1px;
  background: linear-gradient(90deg, var(--star) var(--p), var(--hair) var(--p));
}
.grav input::-moz-range-track { height: 1px; background: var(--hair); }
.grav input::-moz-range-progress { height: 1px; background: var(--star); }
.grav input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 11px; height: 11px;
  margin-top: -5px;
  border-radius: 50%;
  background: #000;
  box-shadow: 0 0 0 1px var(--star), 0 0 calc(2px + var(--g) * 14px) calc(var(--g) * 3px) color-mix(in srgb, var(--star) 75%, transparent);
}
.grav input::-moz-range-thumb {
  width: 11px; height: 11px;
  border: 0;
  border-radius: 50%;
  background: #000;
  box-shadow: 0 0 0 1px var(--star), 0 0 calc(2px + var(--g) * 14px) calc(var(--g) * 3px) color-mix(in srgb, var(--star) 75%, transparent);
}
.grav input:focus-visible { outline: none; }
.grav input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 1px var(--star), 0 0 0 4px #000, 0 0 0 5px var(--t-dim); }

/* the views: three words; the one you're in is lit, with a point of light under it */
.views { display: flex; justify-content: space-between; }
.views button {
  position: relative;
  padding: 6px 0 12px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--t-faint);
  transition: color .4s var(--ease);
}
.views button::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  width: 4px; height: 4px;
  margin-left: calc(-2px - .13em);   /* under the letters (the tracking leaves a space after the last one) */
  border-radius: 50%;
  background: var(--star);
  box-shadow: 0 0 8px var(--star);
  opacity: 0;
  transform: scale(.4);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
.views button:hover { color: var(--t-dim); }
.views button[aria-pressed="true"] { color: var(--star); }
.views button[aria-pressed="true"]::after { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- logo view */

/* The banner's own strokes and wordmarks, aligned to the live black hole and star. Opacities come from JS:
   --words (BLACK / NOVA) and --strokes (figure-eight). */
.logo-overlay {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  --words: 0; --strokes: 0;
}
.logo-overlay svg { position: absolute; left: 0; top: 0; transform-origin: 0 0; overflow: visible; }
.logo-overlay [fill="#EEE8DE"] { fill: var(--star); }
.logo-overlay [stroke="#EEE8DE"] { stroke: var(--star); }
.logo-overlay [stop-color="#EEE8DE"] { stop-color: var(--star); }
.logo-overlay path[stroke], .logo-overlay .pen-glows { opacity: var(--strokes); }
.logo-overlay path:not([stroke]) { opacity: var(--words); }
/* the figure-eight: nothing of it until it starts drawing itself; while it draws, pens draw copies of its two
   loops (main.js: drawFigure); the banner's own strokes stand in again, untouched, the moment it's complete */
.logo-svg.undrawn path[stroke], .logo-svg.drawing path[stroke]:not(.pen),
.logo-svg:not(.drawing) .pen, .logo-svg:not(.drawing) .pen-glows { visibility: hidden; }

/* ---------------------------------------------------------------- landing (the mark) */

/* a black veil the site fades through (the Star Tour's ending, and the landing assembling again after it) */
.veil { position: fixed; inset: 0; z-index: 2; background: #000; opacity: 0; pointer-events: none; }

/* the whole layer blends (a child's blend mode would stop at this stacking context), so the hint reads on both halves */
.landing { position: fixed; inset: 0; z-index: 3; pointer-events: none; mix-blend-mode: difference; }
.bh-hotspot {
  position: absolute;
  left: 0; top: 0;
  width: 64px; height: 64px;
  margin: -32px 0 0 -32px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
}
.bh-hotspot:focus-visible { opacity: 1; outline: 1px solid var(--star); outline-offset: 6px; }
body.is-landing .bh-hotspot { pointer-events: auto; cursor: none; }

/* The landing draws its own cursor (js/gascursor.js), so the system one is hidden there. */
.gas-cursor { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
body.is-landing, body.is-landing * { cursor: none !important; }

/* UI waits until you have fallen in */
.hud { transition: opacity .9s var(--ease), visibility 0s linear 0s; }
body.is-landing .hud { opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .9s var(--ease), visibility 0s linear .9s; }
body.is-landing .hud * { pointer-events: none !important; }
body.is-landing .labels { opacity: 0; }
.labels { transition: opacity .6s var(--ease); }

/* ---------------------------------------------------------------- rail */

.rail {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: var(--gutter);
}
.rail ol {
  list-style: none;
  margin: 0;
  padding: 16px 0 0;
  display: grid;
  grid-template-columns: repeat(11, minmax(0, 1fr));
  position: relative;
}
/* the catalogue's line, through every star's dot */
.rail ol::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 20px;
  height: 1px;
  background: var(--hair);
}
.rail li { position: relative; min-width: 0; }
.stop {
  display: grid;
  gap: 12px;
  width: 100%;
  text-align: left;
  padding: 0 8px 0 0;
  color: var(--t-faint);
  transition: color .4s var(--ease);
}
.stop .dot {
  position: relative;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 0 4px #000;
  transition: transform .4s var(--ease);
}
.stop .n { font-family: var(--mono); font-weight: 500; font-size: 10px; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stop[aria-current="true"] { color: var(--star); }
.stop:disabled { cursor: default; }
@media (hover: hover) {
  .stop:hover { color: var(--t-dim); }
  .stop[aria-current="true"]:hover { color: var(--star); }
  .stop:not([aria-current="true"]):hover .dot { transform: scale(1.25); }
}

/* The current star: a faint orbit round its dot and a particle on it. Tap it and it slings off along the line into
   the Star Tour (or, left alone long enough, it winds up and goes by itself: main.js slingTour). Two turns, one
   inside the other: the inner one runs only to quicken it (under the pointer, and winding up), and pausing it holds
   its angle, so the particle never jumps. */
.stop .dot::after {
  content: '';
  position: absolute;
  left: -6px; top: -6px;
  width: 21px; height: 21px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--c) 40%, transparent);
  opacity: 0;
  transform: scale(.6);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.stop[aria-current="true"] .dot::after { opacity: 1; transform: none; }
.stop .orbit { position: absolute; left: -6px; top: -6px; width: 21px; height: 21px; opacity: 0; transition: opacity .5s var(--ease); }
.stop .spin { position: absolute; inset: 0; animation: orbit .6s linear infinite paused; }
.stop .orbit i {
  position: absolute;
  left: 50%; top: 0;
  width: 4px; height: 4px;
  margin: -2px 0 0 -2px;
  border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 6px var(--c);
}
.stop[aria-current="true"] .orbit { opacity: 1; animation: orbit 4s linear infinite; }
.rail.is-winding .stop[aria-current="true"] .spin { animation-play-state: running; }
@media (hover: hover) { .stop[aria-current="true"]:hover .spin { animation-play-state: running; } }
.rail.is-slinging .stop[aria-current="true"] .orbit { opacity: 0; transition: none; }
@keyframes orbit { to { transform: rotate(360deg); } }

/* the particle leaving: off along the line, faster and faster, a streak growing behind it (hud.js sling) */
.photon {
  position: absolute;
  left: 0; top: 0;
  width: 5px; height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: var(--star);
  box-shadow: 0 0 6px var(--star), 0 0 18px 3px color-mix(in srgb, var(--star) 50%, transparent);
  opacity: 0;
  pointer-events: none;
  transition: transform .9s cubic-bezier(.5, 0, .9, .35), opacity .25s linear;
}
.photon.is-on { opacity: 1; }
.photon::before {
  content: '';
  position: absolute;
  top: 50%; right: 50%;
  width: 140px; height: 1px;
  margin-top: -.5px;
  background: linear-gradient(90deg, transparent, var(--star));
  transform-origin: 100% 50%;
  transform: scaleX(0);
  transition: transform .9s cubic-bezier(.5, 0, .9, .35);
}
.photon[data-dir="l"]::before { right: auto; left: 50%; background: linear-gradient(270deg, transparent, var(--star)); transform-origin: 0 50%; }
.photon.is-on::before { transform: none; }
/* where it lands, a ring goes out from that star's dot as the tour takes over */
.stop.is-struck .dot::after { animation: struck .9s cubic-bezier(.2, .6, .3, 1); }
@keyframes struck {
  from { opacity: 1; transform: scale(.4); border-color: var(--c); }
  to { opacity: 0; transform: scale(3.2); border-color: var(--c); }
}

/* ---------------------------------------------------------------- 3D labels */

.labels { position: fixed; inset: 0; pointer-events: none; z-index: 1; overflow: hidden; }
.lbl {
  position: absolute;
  left: 0; top: 0;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--t-dim);
  text-shadow: 0 0 3px #000, 0 0 10px rgba(0, 0, 0, .9);
  will-change: transform, opacity;
  transition: opacity .35s linear;
}
.lbl::before {
  content: '';
  position: absolute;
  left: -14px; top: 50%;
  width: 10px; height: 1px;
  background: currentColor;
  opacity: .6;
}
.lbl.is-sky { color: var(--lc, var(--t-dim)); pointer-events: auto; cursor: pointer; padding: 6px 0; }
.lbl.is-sky:hover { color: var(--star); }
.lbl.is-sky::after {
  content: '';
  position: absolute;
  left: -24px; top: 50%;
  width: 18px; height: 18px;
  margin-top: -9px;
  border: 1px solid currentColor;
  border-radius: 50%;
  opacity: .5;
}
.lbl.is-sky::before { left: -6px; width: 4px; }

/* ---------------------------------------------------------------- loader */

.loader {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: #000;
  display: grid;
  place-items: center;
  transition: opacity .45s var(--ease), visibility .45s;
}
.loader.is-done { opacity: 0; visibility: hidden; }
.loader-inner { width: min(320px, 80vw); text-align: center; animation: loader-in .6s var(--ease) .5s both; }
@keyframes loader-in { from { opacity: 0; } }   /* a fast start never shows the loader's text at all */
.l-brand { margin: 0 0 22px; letter-spacing: .5em; font-size: 12px; padding-left: .5em; }
.l-bar { height: 1px; background: var(--hair); position: relative; overflow: hidden; }
.l-bar i { position: absolute; inset: 0; background: var(--star); transform-origin: 0 50%; transform: scaleX(0); transition: transform .5s var(--ease); }
.l-status { margin: 14px 0 0; color: var(--t-dim); font-size: 10px; min-height: 1.5em; }
.l-fine { margin: 14px 0 0; color: var(--t-faint); font-size: 10px; }

.noscript { position: fixed; inset: 0; display: grid; place-content: center; text-align: center; gap: 8px; padding: 24px; }

/* ---------------------------------------------------------------- states */

body.is-loading .hud { visibility: hidden; }
/* the interface comes in piece by piece once you've fallen in (the order: main.js boot). An animation rather than a
   transition, so that once it's over each piece's own states (the panel stepping out for a flight) are its own. */
.hud.is-live .intro-in { animation: intro-in 1.4s var(--ease) var(--d, 0s) backwards; }
@keyframes intro-in { from { opacity: 0; transform: translateY(8px); } }
body.is-flying .rail .stop { pointer-events: none; }

/* ---------------------------------------------------------------- short desktop windows */

@media (min-width: 901px) and (max-height: 760px) {
  .panel, .transit { bottom: calc(var(--gutter) + 88px); }
  .bhmod { bottom: calc(var(--gutter) + 76px); gap: 14px; }
  .name, .t-name { font-size: clamp(40px, 4.6vw, 80px); }
}

/* ---------------------------------------------------------------- small screens */

@media (max-width: 900px) {
  .panel, .transit { right: var(--gutter); width: auto; bottom: calc(var(--gutter) + 80px); }
  .kicker { margin-bottom: 10px; }
  .name, .t-name { font-size: clamp(34px, 10.5vw, 56px); }
  .p-sub, .t-dist { margin-top: 12px; font-size: 9px; letter-spacing: .2em; }
  .p-sub .sep { width: 18px; margin: 0 10px; }
  .qual { gap: 9px; font-size: 9px; letter-spacing: .18em; }
  .qual input { width: 52px; }
  /* gravity and the views: one quiet row under the header */
  .bhmod {
    top: calc(var(--gutter) + 46px); bottom: auto; left: var(--gutter); right: var(--gutter); width: auto;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: 28px; align-items: end;
  }
  .grav label { font-size: 9px; letter-spacing: .2em; }
  .grav input { margin-top: 2px; }
  .views button { padding: 6px 0 10px; font-size: 9px; letter-spacing: .2em; }
  .views button::after { margin-left: calc(-2px - .1em); }
  .hud::after { height: 50vh; background: linear-gradient(0deg, rgba(0,0,0,.7), rgba(0,0,0,.3) 50%, transparent); }
  /* the rail runs on past the screen's edge: it scrolls sideways */
  .rail ol {
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
    gap: 4px;
    margin: 0 calc(var(--gutter) * -1);
    padding: 16px var(--gutter) 2px;
    scroll-padding: 0 var(--gutter);   /* a stop snaps to the gutter, not the screen's edge */
    scroll-snap-type: x proximity;
  }
  .rail ol::-webkit-scrollbar { display: none; }
  .rail ol::before { left: var(--gutter); right: auto; width: calc(11 * 96px + 10 * 4px); }
  .rail li { flex: 0 0 96px; scroll-snap-align: start; }
}
/* the narrowest phones: the quality control drops its word */
@media (max-width: 370px) {
  .qual-k { display: none; }
}
