/* ============================================================
   davidgarner.uk / davidgarner.nz

   Palette is derived from stellar physics rather than picked:
   the star colours are Harvard spectral-class blackbody values,
   and the amber accent is a K-class star — which is also the
   amber of instrument panels and observatory red-light.
   ============================================================ */

:root {
  --void:  #05060D;
  --deep:  #0A0C1A;
  --haze:  #141935;
  --rule:  #232A4D;
  --dim:   #7E88B8;
  --ink:   #E6E9F5;
  --amber: #FFC24B;
  --amber-deep: #E5A32F;
  --blue:  #8FA8FF;

  --font-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --gutter: clamp(1.25rem, 5vw, 5.5rem);
  --rail-h: 3.1rem;
  --ease: cubic-bezier(0.22, 0.68, 0.24, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100%;
  background: var(--void);
  background-image: radial-gradient(125% 95% at 76% 46%, #111731 0%, #080A17 46%, #05060D 100%);
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 300;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}

:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.7rem 1rem;
  background: var(--amber);
  color: var(--void);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-decoration: none;
}
.skip:focus { left: 0; }

/* ---------- instrument rails ---------------------------------- */

.rail {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: var(--rail-h);
  padding: 0 var(--gutter);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  pointer-events: none;
  animation: fade 900ms var(--ease) 900ms both;
}
.rail > * { pointer-events: auto; }

.rail--top {
  top: 0;
  background: linear-gradient(to bottom, rgba(5, 6, 13, 0.94) 20%, rgba(5, 6, 13, 0));
}
.rail--bottom {
  bottom: 0;
  background: linear-gradient(to top, rgba(5, 6, 13, 0.94) 20%, rgba(5, 6, 13, 0));
}

.rail__id { color: var(--ink); }

.rail__readout {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--blue);
  /* Set in its own case by sky.js: uppercasing would turn the Bayer
     designation "σ Oct" into "Σ OCT". */
  text-transform: none;
}

.rail__meta { flex: none; color: var(--rule); }
.rail--bottom .rail__meta { color: #4A548A; }

/* Quiet rail links — the "Solar System" door on the front page, the way
   home on the solar page. Discreet at rest, amber when found. */
.rail__link {
  /* Quiet, but it must pass AA at 10px: --dim is 5.9:1 on the void. */
  color: var(--dim);
  text-decoration: none;
  white-space: nowrap;
  transition: color 220ms var(--ease);
}
.rail__link:hover,
.rail__link:focus-visible {
  color: var(--amber);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rail__link--home { color: var(--ink); }

.rail__group {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-width: 0;
}
.rail__group .rail__readout { flex: 0 1 auto; }

/* ---------- hemisphere toggle --------------------------------- */

.hemi {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex: none;
}

.hemi__btn {
  padding: 0.35rem 0.1rem;
  border: 0;
  background: none;
  /* Bright enough to be discovered AND to pass AA at this size — the body
     copy invites the flip, so the control cannot sit at hairline contrast. */
  color: var(--dim);
  font: inherit;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 220ms var(--ease);
}
.hemi__btn:hover { color: var(--blue); }
.hemi__btn[aria-pressed='true'] { color: var(--amber); }

.hemi__sep { color: var(--rule); }

/* ---------- hero ---------------------------------------------- */

.hero {
  position: relative;
  display: grid;
  align-content: center;
  /* The hero is the whole page. svh rather than vh so a mobile browser
     expanding its chrome can never push the page into scrolling. */
  min-height: 100svh;
  padding: calc(var(--rail-h) + 2rem) var(--gutter);
  overflow: hidden;
}

.hero__chart {
  /* The entrance animation ends on this value rather than a literal 1.
     An animated `opacity: 1` outranks any normal declaration, which silently
     cancelled the reduced opacity the narrow-screen rule asks for. The whole
     container carries it because the sky is two stacked layers — the baked
     Milky Way sheet sky.js mounts, and the live star canvas above it. */
  --chart-opacity: 1;
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: var(--chart-opacity);
  /* Short on purpose: this brings the photograph up, then sky.js reveals the
     stars on its own clock. A long element fade would swallow that. */
  animation: chart-in 1100ms var(--ease) both;
}

#sky {
  position: relative;   /* stacks above the mounted baked layer */
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: auto;
}

/* Contrast behind the copy at every width. On a wide screen the copy owns the
   left edge, so the scrim is a sideways dusk — deep over the text, gone by
   mid-frame. The narrow-screen rule swaps it for a top-down one. Sits above
   the sky (auto) and below .hero__copy (z-index 2). */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(97deg,
    rgba(5, 6, 13, 0.88) 0%,
    rgba(5, 6, 13, 0.64) 30%,
    rgba(5, 6, 13, 0.26) 50%,
    rgba(5, 6, 13, 0) 66%);
}

.hero__copy {
  position: relative;
  z-index: 2;
  max-width: 34rem;
  pointer-events: none;
}
.hero__copy a { pointer-events: auto; }

/* ---------- headline ------------------------------------------ */

.headline {
  margin: 0 0 1.6rem;
  font-weight: 400;
}

.headline__lede {
  display: block;
  max-width: 22ch;
  margin-bottom: 0.5rem;
  color: var(--dim);
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.75vw, 1.32rem);
  font-weight: 300;
  line-height: 1.42;
  animation: rise 900ms var(--ease) 150ms both;
}

.fig {
  color: var(--blue);
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.86em;
  letter-spacing: 0.01em;
}
.fig sup {
  font-size: 0.62em;
  vertical-align: super;
  line-height: 0;
}

.headline__name {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4.6vw, 4.75rem);
  line-height: 0.86;
  letter-spacing: -0.025em;
  text-transform: uppercase;
  color: var(--ink);
}

.headline__word {
  display: block;
  font-variation-settings: 'wght' 800, 'wdth' 125;
  animation: widen 1100ms var(--ease) calc(280ms + var(--i) * 110ms) both;
}

/* ---------- subheadline --------------------------------------- */

.sub {
  max-width: 33rem;
  margin: 0 0 2.1rem;
  color: #C3C9E4;
  font-size: clamp(1.02rem, 1.32vw, 1.19rem);
  font-weight: 300;
  line-height: 1.62;
  animation: rise 900ms var(--ease) 620ms both;
}
.sub b {
  color: var(--ink);
  font-weight: 400;
  font-family: var(--font-mono);
  font-size: 0.88em;
  letter-spacing: 0.01em;
}

/* ---------- call to action ------------------------------------ */

.cta {
  display: flex;
  margin: 0;
  animation: rise 900ms var(--ease) 760ms both;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.2rem;
  border: 1px solid transparent;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 220ms var(--ease), color 220ms var(--ease),
              border-color 220ms var(--ease), transform 220ms var(--ease);
}

.btn--primary {
  background: var(--amber);
  border-color: var(--amber);
  color: #120C00;
}
.btn--primary:hover {
  background: #FFD277;
  border-color: #FFD277;
  transform: translateY(-2px);
}

.btn__arrow { transition: transform 220ms var(--ease); }
.btn--primary:hover .btn__arrow { transform: translateX(4px); }

/* ---------- motion -------------------------------------------- */

@keyframes fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(1.1rem); }
  to   { opacity: 1; transform: none; }
}

/* The display face expands into place on its width axis — the page's one
   orchestrated moment, and the only place the variable axis is animated. */
@keyframes widen {
  from {
    opacity: 0;
    font-variation-settings: 'wght' 800, 'wdth' 72;
    transform: translateY(0.35rem);
  }
  to {
    opacity: 1;
    font-variation-settings: 'wght' 800, 'wdth' 125;
    transform: none;
  }
}

/* Scale only, and from above 1: a full-bleed element rotated or shrunk would
   pull its own corners into view. Settling down onto the frame reads as the
   sky coming into focus. */
@keyframes chart-in {
  from { opacity: 0; transform: scale(1.035); }
  to   { opacity: var(--chart-opacity); transform: none; }
}

/* ---------- narrow screens ------------------------------------ */

@media (max-width: 900px) {
  /* The copy sits high rather than centred, so it covers the top of the chart
     instead of its middle and the lower half of the sky stays visible. */
  .hero { align-content: start; }

  /* The sky is a backdrop here; sky.js has already moved the celestial pole
     into the lower half, under the copy, so the visible rotation happens in
     the empty part of the screen. The scrim carries legibility, so the sky
     itself keeps more presence than a bare opacity cut would allow. */
  .hero__chart { --chart-opacity: 0.62; }

  /* Contrast behind the copy. Spans the full width and starts flush with the
     top of the hero, so the only edge it ever shows is the soft one where it
     fades out — a box-shaped scrim clips its own gradient and leaves a seam. */
  .hero::before {
    inset: 0 0 auto 0;
    height: 64%;
    background: linear-gradient(to bottom,
      rgba(5, 6, 13, 0.93) 0%,
      rgba(5, 6, 13, 0.88) 46%,
      rgba(5, 6, 13, 0.58) 74%,
      rgba(5, 6, 13, 0) 100%);
  }

  .hero__copy { max-width: none; }

  .headline { margin-bottom: 1.15rem; }
  .headline__lede {
    max-width: 30ch;
    font-size: clamp(0.9rem, 3.4vw, 1.08rem);
  }
  .headline__name { font-size: clamp(1.25rem, 5.8vw, 2.5rem); }

  .sub {
    margin-bottom: 1.5rem;
    font-size: clamp(0.92rem, 3.5vw, 1.05rem);
    line-height: 1.55;
  }

  .rail__readout { display: none; }
}

@media (max-width: 560px) {
  :root { --rail-h: 2.8rem; }
  .headline__name { letter-spacing: -0.015em; }
  .headline__word { font-variation-settings: 'wght' 800, 'wdth' 108; }
  @keyframes widen {
    from { opacity: 0; font-variation-settings: 'wght' 800, 'wdth' 72; transform: translateY(0.35rem); }
    to   { opacity: 1; font-variation-settings: 'wght' 800, 'wdth' 108; transform: none; }
  }
}

/* ---------- short viewports ----------------------------------- */

/* Landscape phones, small handsets, a half-height desktop window. The page has
   to hold to one screen, so the type steps down instead of the content being
   clipped or the page starting to scroll. Placed after the width queries so it
   wins on --rail-h. */
@media (max-height: 620px) {
  :root { --rail-h: 2.5rem; }

  .hero { padding: calc(var(--rail-h) + 0.9rem) var(--gutter); }

  .headline { margin-bottom: 0.9rem; }
  .headline__lede {
    max-width: 40ch;
    margin-bottom: 0.3rem;
    font-size: clamp(0.86rem, 1.5vh, 1.05rem);
    line-height: 1.35;
  }
  /* Not halved like the other breakpoints. This block already shrinks the name
     to make a landscape phone fit, and halving a second time dropped it to
     ~17px — at or below the body text, which inverts the hierarchy. Reduced,
     but kept clearly above the paragraph it heads. */
  .headline__name { font-size: clamp(1.35rem, 6vh, 2.4rem); }

  .sub {
    max-width: 46rem;
    margin-bottom: 1.05rem;
    font-size: clamp(0.86rem, 1.5vh, 1.02rem);
    line-height: 1.45;
  }

  .btn { padding: 0.55rem 1rem; font-size: 0.68rem; }

  .hero__chart { --chart-opacity: 0.46; }
}

/* ---------- reduced motion ------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
