/* Mechanical orrery in 3D: full-screen bonus navigation built by orrery.js from the project articles.
   The stage is a turntable tilted in perspective; lengths on it are in --u (1% of its width)
   and angles are clockwise seen from above, 90deg being the front (reading position).
   Where the browser can tie animations to the scroll, the section is pinned while the scroll
   assembles the orrery; elsewhere it assembles by itself when it comes on screen. */

.orrery {
  --tilt: 64deg; /* keep equal to TILT in orrery.js */
  --screen: calc(100vh - var(--topbar-height)); /* what is left under the sticky top bar */
  --pin-min: 25.5rem; /* smallest scene + stacked panel; lower where the panel is a single band (see below) */
  --pin-height: max(var(--screen), var(--pin-min)); /* one screen, so the panel under the scene is always in view */
  --pin-distance: 150vh; /* how long the section stays pinned while the scroll assembles it */
  --orrery-ease: cubic-bezier(0.3, 0.1, 0.2, 1);

  /* Night-sky palette for this section only */
  --color-bg: #04050d;
  --color-text: #f1f2fb;
  --color-muted: #b9bbe6;
  --color-accent: #c9cbf5;
  --color-accent-contrast: #04050d;
  --color-highlight: #f0a3c4;
  --color-border: #7479c2;
  --color-focus: #ffffff;

  display: none;
}

@supports (height: 100svh) {
  .orrery {
    --screen: calc(100svh - var(--topbar-height));
  }
}

/* Shorter pinned stretch on small screens: less swiping before the list */
@media (max-width: 47.99em) {
  .orrery {
    --pin-distance: 120vh;
  }
}

/* Shown, and its space reserved, only when scripting is available */
@media (scripting: enabled) {
  .orrery {
    position: relative;
    isolation: isolate;
    display: block;
    overflow: clip;
    overflow-anchor: none; /* a pinned block that redraws itself must not shift the scroll position (Safari) */
    background-color: var(--color-bg);
    color: var(--color-text);
  }

  /* One screen: the panel takes the room it needs at the bottom, the scene gets everything that is left,
     so nothing can overlap whatever the screen size. The pause control sits in a corner, over the scene. */
  .orrery__pin {
    position: relative; /* frame of the optional 3D canvas; becomes sticky in the pinned layout below */
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    justify-items: center;
    gap: var(--space-sm);
    height: var(--pin-height);
    padding: var(--space-xs) var(--page-gutter) var(--space-md);
    text-align: center;
  }
}

/* Scroll target: where the section ends its pinned stretch, i.e. the orrery is fully assembled.
   One screen above the end of the section, so that the bottom of the pinned block (the project panel) is in view
   even when the screen is shorter than the block. Without pinning this is simply the top of the section. */
.orrery__anchor {
  position: absolute;
  bottom: var(--screen);
  left: 0;
  scroll-margin-top: calc(-1 * var(--space-sm)); /* lands flush under the top bar */
}

/* Night sky on its own layer, taller than the section so it can drift slower than the page */
.orrery::before {
  content: "";
  position: absolute;
  inset: -6% 0;
  z-index: -1;
  /* A few drawn stars until the picture is asked for: on a wide screen the top of the section shows under
     the profile as soon as the page opens, and a picture there would be the largest thing on the first screen */
  background-image:
    radial-gradient(circle at 8% 14%, #ffffff 0 1.2px, transparent 1.9px),
    radial-gradient(circle at 23% 62%, #dfe1ff 0 0.9px, transparent 1.6px),
    radial-gradient(circle at 37% 27%, #ffffff 0 1.6px, transparent 2.3px),
    radial-gradient(circle at 52% 81%, #c9cbf5 0 0.9px, transparent 1.6px),
    radial-gradient(circle at 64% 9%, #ffffff 0 1px, transparent 1.7px),
    radial-gradient(circle at 71% 47%, #dfe1ff 0 1.4px, transparent 2.1px),
    radial-gradient(circle at 86% 70%, #ffffff 0 0.9px, transparent 1.6px),
    radial-gradient(circle at 94% 31%, #c9cbf5 0 1.2px, transparent 1.9px);
  background-size: 37rem 23rem;
}

/* The starfield picture is only requested once the reader has moved and the section is about to be seen (orrery.js) */
.orrery.is-live::before {
  background-image: url("../img/stars.webp");
  background-size: 64rem 64rem;
}

/* ---------- Pinned assembly ----------
   Entering: the stand comes up from below. Pinned: the scroll puts the sun, then each arm, down onto
   its ghost, from the centre outwards; the panel comes last. Two drivers do the same thing:
   - native scroll-driven animations (Chrome, Edge, Safari);
   - GSAP ScrollTrigger where they do not exist (Firefox): orrery-gsap.js sets .is-js-driven and
     writes the --arrive / --mount / --seat / --follow variables read below. */

@media (prefers-reduced-motion: no-preference) {
  /* Layout: a tall section whose content stays pinned under the top bar */
  @supports (animation-timeline: view()) {
    .orrery {
      height: calc(var(--pin-height) + var(--pin-distance));
    }

    .orrery__pin {
      position: sticky;
      top: var(--topbar-height);
    }

    /* The scroll itself replays the assembly */
    .orrery .orrery__control--replay {
      display: none;
    }
  }

  .orrery.is-js-driven {
    height: calc(var(--pin-height) + var(--pin-distance));
  }

  .orrery.is-js-driven .orrery__pin {
    position: sticky;
    top: var(--topbar-height);
  }

  .orrery.is-js-driven .orrery__control--replay {
    display: none;
  }

  /* Driver 1: native timelines. Part animations only fill backwards, so nothing overrides
     the hover ghost once they are done. */
  @supports (animation-timeline: view()) {
    .orrery:not(.is-js-driven) {
      view-timeline: --orrery block;
      view-timeline-inset: var(--topbar-height) 0px;
    }

    .orrery:not(.is-js-driven)::before {
      animation: orrery-drift linear both;
      animation-timeline: --orrery;
      animation-range: cover 0% cover 100%;
    }

    .orrery:not(.is-js-driven) .orrery__stage {
      animation: orrery-arrive linear both;
      animation-timeline: --orrery;
      animation-range: entry 0% entry 100%;
    }

    .orrery:not(.is-js-driven) .orrery__ghost {
      animation: orrery-seat linear backwards;
      animation-timeline: --orrery;
      animation-range: contain calc(2% + var(--i) * 15%) contain calc(32% + var(--i) * 15%);
    }

    .orrery:not(.is-js-driven) .orrery__assembly {
      animation: orrery-mount linear backwards;
      animation-timeline: --orrery;
      animation-range: contain calc(8% + var(--i) * 15%) contain calc(30% + var(--i) * 15%);
    }

    .orrery:not(.is-js-driven) :is(.orrery__rod, .orrery__planet) {
      animation: orrery-fade linear backwards;
      animation-timeline: --orrery;
      animation-range: contain calc(8% + var(--i) * 15%) contain calc(17% + var(--i) * 15%);
    }

    .orrery:not(.is-js-driven) :is(.orrery__panel, .orrery__controls) {
      animation: orrery-follow linear both;
      animation-timeline: --orrery;
      animation-range: contain 80% contain 94%;
    }
  }

  /* Driver 2: variables written by GSAP, all running from 0 (not there yet) to 1 (in place) */
  .orrery.is-js-driven {
    --arrive: 0;
    --follow: 0;
  }

  .orrery.is-js-driven :is(.orrery__arm, .orrery__assembly--sun) {
    --mount: 0;
    --seat: 0;
  }

  .orrery.is-js-driven .orrery__stage {
    translate: 0 calc((1 - var(--arrive)) * 70vh);
    scale: calc(0.78 + 0.22 * var(--arrive));
  }

  .orrery.is-js-driven .orrery__assembly {
    translate: 0 0 calc((1 - var(--mount)) * var(--u) * 24);
  }

  .orrery.is-js-driven :is(.orrery__rod, .orrery__planet) {
    opacity: min(1, calc(var(--mount) * 2.5));
  }

  .orrery.is-js-driven .orrery__ghost {
    opacity: var(--seat);
  }

  .orrery.is-js-driven :is(.orrery__panel, .orrery__controls) {
    opacity: var(--follow);
    translate: 0 calc((1 - var(--follow)) * 3rem);
  }
}

@keyframes orrery-drift {
  from {
    translate: 0 -5%;
  }

  to {
    translate: 0 5%;
  }
}

@keyframes orrery-arrive {
  from {
    translate: 0 70vh;
    scale: 0.78;
  }
}

@keyframes orrery-follow {
  from {
    opacity: 0;
    translate: 0 3rem;
  }
}

/* ---------- 3D scene ---------- */

/* ---------- Optional Three.js layer (orrery-three.js): real spheres drawn over the CSS discs ---------- */

.orrery__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none; /* the CSS buttons underneath stay the controls */
}

.orrery.has-three :is(.orrery__surface, .orrery__shade, .orrery__initials) {
  visibility: hidden;
}

.orrery.has-three .orrery__planet {
  background-color: transparent;
}

.orrery.has-three .orrery__sun .orrery__planet {
  box-shadow: none;
}

/* The scene box takes the room left in the pin; the stage is sized from that box (cq units),
   so the whole stand always fits between the top bar and the panel.
   Seen on screen, the scene runs from 0.28 stage above the centre of the stage (the largest planet when it is
   at the back, plus the height of its name) to 0.49 stage under it (foot of the stand): 0.77 stage in all,
   which is what the box must hold. */
.orrery__viewport {
  position: relative;
  container-type: size;
  width: 100%;
  min-height: 0;
}

.orrery__stage {
  --stage: clamp(14rem, min(var(--stage-width, 96cqw), calc((100cqh - 2.4rem) / 0.77)), 120rem);
  --u: calc(var(--stage) / 100);
  position: absolute;
  top: calc(50% - var(--stage) * 0.105 + 0.9rem); /* the scene, not the stage, is centred in the box */
  left: 50%;
  width: var(--stage);
  height: var(--stage);
  margin: calc(var(--stage) / -2) 0 0 calc(var(--stage) / -2);
  transform: perspective(calc(var(--stage) * 1.8)) rotateX(var(--tilt));
  transform-style: preserve-3d;
}

/* ---------- Stand: turntable, column, foot ---------- */

.orrery__plate,
.orrery__base {
  position: absolute;
  border-radius: 50%;
}

.orrery__plate {
  inset: 0;
  background:
    repeating-radial-gradient(circle, rgb(255 255 255 / 3%) 0 1px, transparent 1px calc(var(--u) * 1.5)),
    radial-gradient(circle, #1c1f3c 0%, #0c0e20 58%, #181b36 100%);
  box-shadow:
    inset 0 0 0 calc(var(--u) * 0.35) #5a5fa3,
    inset 0 0 0 calc(var(--u) * 1.5) #111329;
}

.orrery__plate--under {
  background: #06070f;
  box-shadow: inset 0 0 0 calc(var(--u) * 0.3) #2a2e55;
  transform: translateZ(calc(var(--u) * -1.6));
}

.orrery__column {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--u) * 7);
  height: calc(var(--u) * 44);
  margin-left: calc(var(--u) * -3.5);
  background: linear-gradient(90deg, #07080f, #2f3360 30%, #a3a8de 48%, #3a3f70 62%, #07080f);
  transform-origin: 50% 0;
  transform: translateZ(calc(var(--u) * -1.6)) rotateX(-90deg);
}

.orrery__base {
  top: 50%;
  left: 50%;
  width: calc(var(--u) * 34);
  height: calc(var(--u) * 34);
  margin: calc(var(--u) * -17) 0 0 calc(var(--u) * -17);
  background: radial-gradient(circle, #2a2e58 0%, #101228 55%, #1b1e3c 100%);
  box-shadow: inset 0 0 0 calc(var(--u) * 0.35) #5a5fa3;
  transform: translateZ(calc(var(--u) * -45.6));
}

.orrery__base--under {
  background: #05060d;
  box-shadow: inset 0 0 0 calc(var(--u) * 0.3) #2a2e55;
  transform: translateZ(calc(var(--u) * -48));
}

/* ---------- Decor on the turntable: dotted orbits, gears, index mark ---------- */

.orrery__decor {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  transform: translateZ(calc(var(--u) * 0.3));
}

/* Dotted like the CV's leaders */
.orrery__orbit {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 0.1 8;
  vector-effect: non-scaling-stroke;
}

.orrery__gear {
  fill: #1f2242;
  stroke: #5a5fa3;
  transform-box: fill-box;
  transform-origin: center;
  rotate: var(--from);
  animation: orrery-turn var(--period) linear infinite;
}

.orrery__gear path {
  stroke-width: 1.5;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* Invisible circle that keeps the gear's bounding box centred on its axis */
.orrery__gear circle {
  fill: none;
  stroke: none;
}

/* Reading position at the front: the selected arm pivots to this mark */
.orrery__index {
  fill: var(--color-highlight);
}

/* ---------- Arms: one pivot joint per project ---------- */

.orrery__arm,
.orrery__assembly,
.orrery__moon-arm {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
}

.orrery__arm {
  top: 50%;
  left: 50%;
  translate: 0 0 calc(var(--u) * 0.6);
  rotate: var(--angle);
}

.orrery__assembly--sun {
  top: 50%;
  left: 50%;
}

.orrery__rod {
  position: absolute;
  top: calc(var(--u) * -0.3);
  left: 0;
  width: calc(var(--r) * var(--u));
  height: max(calc(var(--u) * 0.6), 2px);
  border-radius: 1px;
  background: linear-gradient(#dfe1ff, #7479c2);
}

/* ---------- Bodies: real buttons standing on their arm, always facing the viewer ---------- */

.orrery__body {
  --size: calc(var(--d) * var(--u));
  --hit: max(var(--size), 2rem); /* stays above 24px once perspective shrinks the far side */
  position: absolute;
  top: 0;
  left: calc(var(--r) * var(--u));
  display: grid;
  place-items: center;
  width: var(--hit);
  height: var(--hit);
  margin: calc(var(--hit) / -2) 0 0 calc(var(--hit) / -2);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  /* lifted above the arm, turned back against the arm's angle, then stood up against the tilt */
  translate: 0 0 calc(var(--size) / 2 + var(--u) * 0.8);
  rotate: calc(-1 * var(--angle));
  transform: rotateX(calc(-1 * var(--tilt)));
}

.orrery__body:focus-visible {
  border-radius: 50%;
}

.orrery__planet {
  position: relative;
  display: block;
  width: var(--size);
  height: var(--size);
  overflow: hidden;
  border-radius: 50%;
  background-color: #2a2e58;
  transition: scale 0.15s ease-out;
}

.orrery__body:hover .orrery__planet,
.orrery__body:focus-visible .orrery__planet {
  scale: 1.1;
}

.orrery__body[aria-current="true"] .orrery__planet {
  outline: 2px solid var(--color-text);
  outline-offset: 3px;
}

/* Surface: a 2:1 texture strip, twice side by side, sliding past the disc = the planet spinning */
.orrery__surface {
  position: absolute;
  top: 0;
  left: 0;
  width: 400%;
  height: 100%;
  background-repeat: repeat-x;
  background-size: 50% 100%;
  animation: orrery-spin var(--spin) linear infinite;
}

.orrery.is-live .orrery__surface--rock { background-image: url("../img/planets/rock.webp"); }
.orrery.is-live .orrery__surface--rust { background-image: url("../img/planets/rust.webp"); }
.orrery.is-live .orrery__surface--ice { background-image: url("../img/planets/ice.webp"); }
.orrery.is-live .orrery__surface--terra { background-image: url("../img/planets/terra.webp"); }
.orrery.is-live .orrery__surface--gas { background-image: url("../img/planets/gas.webp"); }
.orrery.is-live .orrery__surface--moon-ember { background-image: url("../img/planets/moon-ember.webp"); }
.orrery.is-live .orrery__surface--moon-mint { background-image: url("../img/planets/moon-mint.webp"); }
.orrery.is-live .orrery__surface--sun { background-image: url("../img/planets/sun.webp"); }

/* Sphere shading; orrery.js turns the lit side towards the sun */
.orrery__shade {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 82% 50%, transparent 0 20%, rgb(2 3 10 / 50%) 56%, rgb(2 3 10 / 94%) 92%);
  box-shadow: inset 0 0 calc(var(--size) * 0.12) rgb(2 3 10 / 60%);
  rotate: var(--light, 180deg);
}

/* ---------- Moons ---------- */

.orrery__moon-arm {
  left: calc(var(--r) * var(--u));
  rotate: var(--moon);
}

.orrery__rod--moon {
  top: calc(var(--u) * -0.15);
  width: calc(var(--mr) * var(--u));
  height: max(calc(var(--u) * 0.3), 1px);
}

.orrery__body--moon {
  --hit: max(var(--size), 1.75rem);
  left: calc(var(--mr) * var(--u));
  rotate: calc(-1 * (var(--angle) + var(--moon)));
}

/* ---------- Sun ---------- */

.orrery__sun {
  left: 0;
  rotate: none;
}

.orrery__sun .orrery__planet {
  box-shadow: 0 0 calc(var(--u) * 3.5) rgb(240 130 150 / 45%);
}

.orrery__initials {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #ffffff;
  font-family: var(--font-heading);
  font-size: max(calc(var(--u) * 5), 0.875rem);
  font-weight: 600;
  line-height: 1;
  text-shadow: 0 0 0.3em rgb(90 15 50 / 90%);
}

/* ---------- Labels: a flat layer in front of the whole scene, so no planet can pass over a name.
   orrery.js gives each one the screen position of its planet (--x: centre, --y: top) and its opacity.
   Always shown for the selection. ---------- */

.orrery__labels {
  position: absolute;
  inset: 0;
  z-index: 1; /* above the optional 3D canvas */
  pointer-events: none; /* the planets underneath stay the controls */
}

.orrery__label {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0 0.4em;
  border-radius: var(--radius-sm);
  background: rgb(4 5 13 / 78%);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.4;
  white-space: nowrap;
  translate: calc(var(--x, 0px) - 50%) calc(var(--y, 0px) - 100% - 0.3rem); /* centred just above the planet */
  opacity: 0; /* until placed */
  visibility: hidden;
}

.orrery__label:is(.is-active, .is-selected) {
  visibility: visible;
}

/* Once the planets are in place, a name can be clicked like its planet */
.orrery.is-assembled .orrery__label {
  cursor: pointer;
  pointer-events: auto;
}

/* Where two names meet, the selected one stays readable, and the pointed one comes first */
.orrery__label.is-selected {
  z-index: 1;
  color: var(--color-highlight);
  font-weight: 700;
}

.orrery__label.is-active {
  z-index: 2;
}

/* Wide screens have room to name every planet */
@media (min-width: 64em) {
  .orrery__label:not(.orrery__label--moon) {
    visibility: visible;
  }
}

/* ---------- Ghost: dashed ring on the turntable that shows where a part goes ---------- */

.orrery__ghost {
  --size: calc(var(--d) * var(--u));
  --ghost-angle: 0deg;
  position: absolute;
  top: calc(var(--size) / -2);
  left: calc(var(--size) / -2);
  width: var(--size);
  height: var(--size);
  border: 2px dashed var(--color-highlight);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  transform: rotate(var(--ghost-angle)) translateX(calc(var(--r) * var(--u)));
  transition:
    transform 0.5s var(--orrery-ease),
    opacity 0.2s;
}

/* Hover or focus: the ghost sweeps along the pivot's arc to the reading position */
.orrery__arm:not(.is-selected):has(.orrery__body:is(:hover, :focus-visible)) > .orrery__ghost {
  --ghost-angle: calc(90deg - var(--angle));
  opacity: 1;
}

/* ---------- Panel and controls ---------- */

.orrery__panel {
  display: grid;
  justify-items: center;
  gap: var(--space-2xs);
  align-content: start;
  max-width: 40rem;
  min-height: 9.75rem; /* name + dates + two lines of summary + button: same height for every project */
}

.orrery__name {
  color: var(--color-highlight);
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-tight);
}

.orrery__dates {
  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* Two lines at most, so the panel keeps its height and the scene its size; the full text is on the card */
.orrery__summary {
  display: -webkit-box;
  overflow: hidden;
  font-size: var(--text-sm);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.orrery__more,
.orrery__control {
  border: var(--border-width) solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: rgb(4 5 13 / 60%);
  color: var(--color-accent);
  cursor: pointer;
}

.orrery__more {
  display: inline-block;
  margin-top: var(--space-xs);
  padding: 0.35rem 0.9rem;
  font-weight: 700;
  text-decoration: none;
}

/* Down chevron */
.orrery__more::after {
  content: "";
  display: inline-block;
  width: 0.45em;
  height: 0.45em;
  margin-left: 0.6em;
  border-right: 2px solid;
  border-bottom: 2px solid;
  rotate: 45deg;
  translate: 0 -0.2em;
}

.orrery__more:hover,
.orrery__control:hover {
  text-decoration: underline;
}

/* Wide screens, and short ones that are wide enough: the panel lies in one band under the stand
   (name and dates, summary, link), and the turntable may take most of the width without being cut by the edges */
@media (min-width: 64em), (min-width: 40em) and (max-height: 42em) {
  .orrery {
    --pin-min: 19.5rem;
  }

  .orrery__stage {
    --stage-width: 70cqw;
  }

  .orrery__panel {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "name summary more"
      "dates summary more";
    align-items: center;
    justify-items: start;
    gap: 0 var(--space-lg);
    width: min(100%, 62rem);
    max-width: none;
    min-height: 3.75rem; /* two lines of summary: same height for every project */
    text-align: left;
  }

  .orrery__name {
    grid-area: name;
    align-self: end;
  }

  .orrery__dates {
    grid-area: dates;
    align-self: start;
  }

  .orrery__summary {
    grid-area: summary;
  }

  .orrery__more {
    grid-area: more;
    margin-top: 0;
  }
}

/* Pause (and, where the scroll does not replay the assembly, replay): in the top corner, over the scene */
.orrery__controls {
  position: absolute;
  top: var(--space-sm);
  right: var(--page-gutter);
  z-index: 2; /* above the names layer */
  display: flex;
  gap: var(--space-xs);
}

.orrery__control {
  display: grid;
  place-items: center;
  min-width: 2.5rem;
  min-height: 2.5rem;
  padding: 0.25rem 0.6rem;
  font: inherit;
  font-size: var(--text-xs);
}

.orrery__control svg {
  grid-area: 1 / 1;
  width: 1.1rem;
  height: 1.1rem;
  fill: currentColor;
}

/* Two bars while it moves, a triangle once paused */
.orrery__icon-resume,
.orrery.is-paused .orrery__icon-pause {
  display: none;
}

.orrery.is-paused .orrery__icon-resume {
  display: block;
}

/* ---------- Idle motion handled in CSS: spinning surfaces and gears ---------- */

@keyframes orrery-spin {
  to {
    transform: translateX(-50%);
  }
}

@keyframes orrery-turn {
  to {
    rotate: calc(var(--from) + var(--sweep));
  }
}

.orrery:is(.is-paused, .is-offscreen) :is(.orrery__surface, .orrery__gear) {
  animation-play-state: paused;
}

/* ---------- Assembly: once the stand is up, each part comes down onto its ghost ----------
   Opacity is only animated on flat leaves: fading a 3D group would flatten it. */

.orrery.is-pending :is(.orrery__rod, .orrery__planet) {
  opacity: 0;
}

/* Timed rise, only where the arrival cannot follow the scroll */
@supports not (animation-timeline: view()) {
  /* Parked below the bottom edge of the section, which clips it */
  .orrery.is-pending .orrery__stage {
    translate: 0 calc(var(--stage) * 0.9 + 18rem);
  }

  .orrery.is-assembling .orrery__stage {
    animation: orrery-rise 0.8s var(--orrery-ease) both;
  }
}

.orrery.is-assembling .orrery__ghost {
  animation: orrery-seat 0.9s ease both;
  animation-delay: calc(0.3s + var(--i) * 120ms);
}

.orrery.is-assembling .orrery__assembly {
  animation: orrery-mount 0.6s var(--orrery-ease) both;
  animation-delay: calc(0.45s + var(--i) * 120ms);
}

.orrery.is-assembling :is(.orrery__rod, .orrery__planet) {
  animation: orrery-fade 0.3s ease-out both;
  animation-delay: calc(0.45s + var(--i) * 120ms);
}

@keyframes orrery-rise {
  from {
    translate: 0 calc(var(--stage) * 0.9 + 18rem);
  }
}

@keyframes orrery-seat {
  from {
    opacity: 0;
  }

  25%,
  70% {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

/* Exploded view: the part waits above its seat along the vertical axis */
@keyframes orrery-mount {
  from {
    translate: 0 0 calc(var(--u) * 24);
  }
}

@keyframes orrery-fade {
  from {
    opacity: 0;
  }
}

/* ---------- Reduced motion: static orrery, instant pivots, no controls ---------- */

@media (prefers-reduced-motion: reduce) {
  .orrery__planet,
  .orrery__ghost {
    transition: none;
  }

  .orrery__surface,
  .orrery__gear {
    animation: none;
  }

  .orrery__controls {
    display: none;
  }
}

/* ---------- Windows high contrast: the scene keeps its own night colours (it is a picture made of shapes);
   the names, the panel and the buttons around it follow the system's colours ---------- */

@media (forced-colors: active) {
  .orrery__stage {
    forced-color-adjust: none;
  }
}
