/* Planet Select demo — visual language borrowed from the reference video:
   near-black starfield, one warm orange accent, wide-tracked caps. */

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../../static/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
}
/* Inter is a VARIABLE font: inter-400/500/600-latin.woff2 are three names
   for one byte-identical file that already covers the whole weight axis.
   Declared under three URLs the browser downloaded the same 48 KB twice on
   every cold load (the landing page paints 400 and 600), so all three faces
   point at one URL. The weight descriptors stay — the browser sets the
   variation axis from them — so nothing renders differently.
   Measured 29 Aug 2026; do not "restore" the per-weight filenames. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../../static/fonts/inter-400-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../../static/fonts/inter-400-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../../static/fonts/inter-400-latin.woff2') format('woff2');
}

:root {
  /* the mission column, and the region left over to its right — overlays
     (3D model, article) live in that region so they never cover it */
  --col-left: clamp(28px, 6vw, 92px);
  --col-w: min(32vw, 480px);
  --col-gap: 42px;
  --accent: #e08239;
  --accent-bright: #f0923f;
  --ink: #f4f2ee;
  --ink-dim: #b9b5ae;
  --bg: #060608;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* Scrollbars: the browser default is a light grey slab that reads as a
   foreign object on a near-black page. Track disappears into the
   background; the thumb is a faint bar that warms to the accent on
   hover. (Panes that hide their bar entirely override this below.) */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .15) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .15);
  background-clip: content-box;
  border: 3px solid transparent;   /* insets the bar so it reads as a hairline */
  border-radius: 6px;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(224, 130, 57, .55); background-clip: content-box; }
*::-webkit-scrollbar-corner { background: transparent; }

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Inter', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* ----------------------------------------------------- background layers */
#stars {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
}
/* faint violet nebulae, like the reference */
#stars::before { content: none; }
body::before {
  content: '';
  position: fixed; inset: 0; z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(38% 30% at 78% 12%, rgba(88, 60, 160, .14), transparent 70%),
    radial-gradient(30% 26% at 12% 78%, rgba(70, 45, 140, .10), transparent 70%),
    radial-gradient(26% 22% at 55% 90%, rgba(120, 60, 120, .07), transparent 70%);
}

#scene {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 2;
  touch-action: none;
}
#scene.grabbable { cursor: grab; }
#scene.grabbing  { cursor: grabbing; }

/* Two rules that lived in edit.css until 29 Aug 2026, when that stylesheet
   became owner-only. Both are reading rules and every visitor needs them:
   .btn-read's display:flex outranks the browser's own [hidden] handling, and
   a timeline entry nobody has illustrated must read as a hole in the film
   rather than a dead row. */
[hidden] { display: none !important; }
.tl-row.no-shot { opacity: .72; cursor: default; }

/* --------------------------------------------------------------- views */
.view {
  position: fixed; inset: 0; z-index: 4;
  opacity: 0;
  visibility: hidden;
  transition: opacity .7s ease, visibility 0s linear .7s;
}
.view.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity .7s ease .15s, visibility 0s;
}
/* let drags reach the canvas; only real controls take pointer events */
#explore { pointer-events: none; }
#explore a, #explore button { pointer-events: auto; }
#detail { pointer-events: none; }
#detail a, #detail button, #detail .patch-panel { pointer-events: auto; }
/* the panel itself is pointer-events: none until it is shown, so every
   view that shows one has to say so — a tour's page shows one too */
#detail .mission-panel.show,
#tour .mission-panel.show { pointer-events: auto; }
#detail .model-viewer.show { pointer-events: auto; }
#detail .film-panel.show { pointer-events: auto; }

/* ---------------------------------------------------- mission library
   The whole catalogue as a wall of badges, filtered by the almanac's own
   console (category, era, programme, live-tracked, free text) restyled to
   this site: mono labels, amber accents, nothing but hairlines. */
#library { overflow-y: auto; }
.lib-inner {
  max-width: 1500px;
  margin: 0 auto;
  padding: 122px clamp(28px, 6vw, 92px) 90px;
}
.lib-title {
  font-size: clamp(26px, 3vw, 40px);
  font-weight: 600; letter-spacing: .01em;
}
.lib-console {
  margin-top: 26px;
  padding: 20px 22px;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 8px;
  background: rgba(6, 8, 12, .45);
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 14px 18px;
}
.lib-search {
  flex: 1 1 300px;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 4px;
  background: rgba(255, 255, 255, .04);
  color: #fff;
  font-family: inherit; font-size: 14px; letter-spacing: .02em;
}
.lib-search::placeholder { color: rgba(255, 255, 255, .35); }
.lib-search:focus { outline: none; border-color: var(--accent); }
.lib-chips, .lib-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.lib-chip {
  min-height: 36px;
  padding: 0 15px;
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  cursor: pointer;
  transition: color .2s, border-color .2s, background .2s;
}
.lib-chip i {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
.lib-chip:hover { color: #fff; border-color: rgba(255, 255, 255, .34); }
.lib-chip.on {
  color: #12100d;
  background: var(--accent-bright);
  border-color: var(--accent-bright);
}
.lib-select {
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px;
  background: rgba(255, 255, 255, .04);
  color: var(--ink);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
}
.lib-select:focus { outline: none; border-color: var(--accent); }
.lib-select option { background: #0b0d12; color: #fff; letter-spacing: 0; }
.lib-count {
  margin: 22px 0 26px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px; letter-spacing: .24em; text-transform: uppercase;
  color: var(--ink-dim);
}
.lib-count b { color: var(--accent-bright); font-size: 13px; }
.lib-grid { gap: 34px 18px; }
.lib-card { cursor: default; }
.lib-card .p-name { line-height: 1.35; }
.lib-live-dot {
  display: inline-block;
  width: 5px; height: 5px; margin-right: 5px;
  border-radius: 50%;
  background: #63d18a;
  vertical-align: middle;
}
/* a card not yet connected to a world stands back — greyscale badge,
   dimmed text. The .has-mission class IS the connection, so the card
   lights the moment a grid links its mission, with nothing to update. */
.lib-card:not(.has-mission) { opacity: .5; }
.lib-card:not(.has-mission) .art img { filter: grayscale(1); }

/* the entries with a page of their own: the same gesture as a patch on a
   world's wall, plus a quiet mark so they can be found among the rest */
.lib-card.has-mission .p-name { transition: color .25s; }
.lib-card.has-mission:hover .p-name { color: var(--accent-bright); }
.lib-page-dot {
  display: inline-block;
  width: 5px; height: 5px; margin-left: 6px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: middle;
  opacity: .8;
}
.lib-empty {
  grid-column: 1 / -1;
  padding: 60px 0;
  text-align: center;
  color: var(--ink-dim);
  font-size: 14px;
}
.lib-empty b { display: block; margin-bottom: 8px; font-size: 17px; color: #fff; }
.lib-pages {
  margin-top: 44px;
  display: flex; justify-content: center; align-items: center; gap: 8px;
}
.lib-page {
  min-width: 36px; height: 36px;
  padding: 0 10px;
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 4px;
  background: transparent;
  color: var(--ink-dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: .1em;
  cursor: pointer;
  transition: color .2s, border-color .2s, background .2s;
}
.lib-page:hover:not([disabled]) { color: #fff; border-color: rgba(255, 255, 255, .34); }
.lib-page.on {
  color: #12100d;
  background: var(--accent-bright);
  border-color: var(--accent-bright);
}
.lib-page[disabled] { opacity: .3; cursor: default; }
.lib-gap { color: var(--ink-dim); padding: 0 2px; }

/* ------------------------------------------------- detail patch wall */
.patch-panel {
  position: absolute;
  left: clamp(28px, 6vw, 92px);
  top: 15vh;
  /* Five patches to a row, not three. The art is a fixed ~130px, so the
     columns cannot give much — the width had to come from the panel, and
     there was plenty of it going spare across the middle of the screen.
     Mars is 23 missions: at three a row that was eight rows of scrolling. */
  width: min(64vw, 925px);   /* 50px of it is the scrim padding */
  max-height: 78vh;
  overflow-y: auto;
  /* The panel now reaches across the planet, so its scrollbar drew a
     bright vertical line down the middle of Mars — for eleven pixels of
     overflow. Height enough that the largest grid does not scroll, and no
     scrollbar chrome when a smaller window makes it. */
  scrollbar-width: none;
  transition: opacity .45s ease;
  /* Five to a row reaches across the planet, and a mission name in white
     over a lit limb of Mars is unreadable. A scrim under the panel, fading
     out on the right so it never reads as a box sitting on the sky. */
  padding: 16px 30px 20px 20px;
  margin-left: -20px;
  border-radius: 16px;
  background: linear-gradient(100deg,
    rgba(5, 6, 10, .90) 0%,
    rgba(5, 6, 10, .84) 52%,
    rgba(5, 6, 10, .55) 80%,
    rgba(5, 6, 10, 0) 100%);
}
.patch-panel::-webkit-scrollbar { width: 0; height: 0; }
.patch-panel.gone { opacity: 0; pointer-events: none !important; }
.patch.has-mission { cursor: pointer; }
.patch.has-mission .art { transition: transform .3s ease; }
.patch.has-mission:hover .art { transform: scale(1.09); }
/* Which patches open something is said with an arrow on the role line,
   not by dimming the art: Pioneer 11's insignia is dark navy and at any
   useful opacity it simply disappeared against this background. */
.patch:not(.has-mission) .p-name { color: var(--ink-dim); }
.patch-panel h3 {
  font-size: 12px; font-weight: 600;
  letter-spacing: .3em; text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 26px;
}
.patch-grid {
  display: grid;
  /* 140 + 16 gap x 5 = 764, inside the 870 above; a sixth would need 940,
     so it holds at five and auto-fill drops to four or three on a narrower
     screen rather than crushing the art. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 26px 16px;
}
.patch { text-align: center; }
.patch .art {
  height: 122px;
  display: flex; align-items: center; justify-content: center;
}
.patch img {
  max-width: 120px; max-height: 122px;
  filter: drop-shadow(0 5px 12px rgba(0, 0, 0, .65));
}
.patch .medallion {
  width: 102px; height: 102px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .35);
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 600; letter-spacing: .1em;
  color: var(--ink-dim);
}
.patch .p-name {
  margin-top: 9px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .03em;
}
.patch .p-role {
  margin-top: 3px;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-dim);
}

/* --------------------------------------------- landing & crash sites
   Marks pinned to the globe: a ringed dot on the spot, the mission and
   the place beside it on a hairline leader. The render loop sets left,
   top and opacity every frame, so nothing here may transition. */
.site-layer { position: absolute; inset: 0; pointer-events: none; }
/* The site is a point of zero size: left/top ARE the spot on the globe.
   The mark centres itself on it and the label hangs off to one side — if
   the two shared a box, centring that box would push the mark off the
   ground it is pointing at, by half the label's width. */
.site { position: absolute; width: 0; height: 0; opacity: 0; }
.site-mark {
  position: absolute;
  left: 0; top: 0;
  width: 15px; height: 15px;
  margin: -7.5px 0 0 -7.5px;
  border-radius: 50%;
  border: 1.5px solid var(--accent-bright);
  box-shadow: 0 0 14px rgba(224, 138, 62, .55);
}
.site-mark::after {
  content: '';
  position: absolute; inset: 3.5px;
  border-radius: 50%;
  background: var(--accent-bright);
}
.site.crash .site-mark { border-color: #e2564a; box-shadow: 0 0 14px rgba(226, 86, 74, .5); }
.site.crash .site-mark::after { background: #e2564a; }
.site-tag {
  position: absolute;
  left: 16px; top: 0;
  transform: translateY(-50%);
  padding-left: 12px;
  border-left: 1px solid rgba(255, 255, 255, .3);
  white-space: nowrap;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .95), 0 0 8px rgba(0, 0, 0, .9);
}
.site-tag b { display: block; font-size: 12px; font-weight: 600; letter-spacing: .04em; }
.site-tag i {
  display: block; margin-top: 2px;
  font-style: normal;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .68);
}
.site-tag u {
  display: block; margin-top: 3px;
  text-decoration: none;
  font-size: 9.5px; letter-spacing: .06em;
  color: rgba(255, 255, 255, .45);
}
.site-tag u:empty { display: none; }
/* near the right edge the tag hangs off the other side of the mark */
.site.flip .site-tag {
  left: auto; right: 16px;
  padding: 0 12px 0 0;
  border-left: 0;
  border-right: 1px solid rgba(255, 255, 255, .3);
  text-align: right;
}
/* the mission page comes down close: bigger mark, bigger name */
.site.big .site-mark { width: 20px; height: 20px; margin: -10px 0 0 -10px; }
.site.big .site-mark::after { inset: 4.5px; }
.site.big .site-tag b { font-size: 16px; }
.site.big .site-tag i { font-size: 10.5px; }

/* ------------------------------------------------ mission focus panel */
.mission-panel {
  position: absolute;
  left: var(--col-left);
  top: 10vh;
  width: var(--col-w);
  max-height: 82vh;
  overflow-y: auto;
  overflow-x: hidden;                /* the column never scrolls sideways */
  scrollbar-width: thin;
  padding-right: 10px;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .5s ease, transform .5s ease;
  pointer-events: none;
}
.mission-panel.show { opacity: 1; transform: none; }
/* the patch stands beside the header, its top level with the type chip
   and its bottom level with the foot of the mission name */
/* flex-start, not stretch: stretching would size the text block to the
   patch, and the patch is sized from the text block — it would measure
   itself and keep the previous mission's height */
.m-head { display: flex; align-items: flex-start; gap: 18px; }
/* a floor on the header height keeps the patch large and legible; the
   title is pushed to the bottom of that block so the patch still runs
   exactly from the chip's top to the foot of the name */
.m-head-text { min-width: 0; }
/* The patch column is a fixed share of the panel, whatever shape the
   badge is. It has to be: the patch used to take its height from the
   text and its width from its own aspect, so a wide badge (Pathfinder's
   ellipse) squeezed the title into more lines, which made the block
   taller, which made the badge wider still — until the header ran off
   the column. A fixed width breaks that circle. JS still caps the
   height, so round badges run from the chip's top to the title's foot. */
.m-patch-box {
  flex: 0 0 34%;
  min-width: 0;                      /* or the box grows to the art's own width */
  max-width: 170px;
  display: flex; align-items: flex-start; justify-content: center;
}
.m-patch-box[hidden] { display: none; }
/* a tour's chip says more than a mission's — "Kuiper belt tour · 2
   worlds" against "Rover + helicopter" — so its patch takes a slightly
   smaller share and the chip stays on one line */
#tour-panel .m-patch-box { flex-basis: 27%; }
.m-patch { max-width: 100%; width: auto; height: auto; object-fit: contain; }
/* agency and year sit under the type chip — the taller header gives the
   patch beside it more room, which is the point */
.m-chip-row {
  display: flex; flex-direction: column;
  align-items: flex-start; gap: 9px;
}
.m-chip {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent-bright);
  border: 1px solid rgba(224, 130, 57, .55);
  padding: 6px 13px 5px;
  border-radius: 3px;
}
.m-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
}
.mission-panel h2 {
  margin-top: 14px;
  font-size: clamp(23px, 2.1vw, 33px);
  font-weight: 600; letter-spacing: .01em;
  text-wrap: balance;
}
/* the objectives paragraph, which opens the record under the title */
.m-blurb {
  margin-top: 16px;
  font-size: 14px; line-height: 1.65;
  color: var(--ink);
}
.m-sec .m-blurb { margin-top: 0; }
.m-card {
  margin-top: 18px;
  background: rgba(255, 255, 255, .03);
  border: 1px solid rgba(255, 255, 255, .09);
  border-radius: 6px;
  padding: 16px 18px;
}
.m-card h4 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; font-weight: 500;
  letter-spacing: .25em; text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 14px;
}

/* the almanac's own record layout: a rule, an amber section label, then
   flight data as label/value rows with the values ranged right */
.m-sec {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, .12);
}
.m-sec h4 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; font-weight: 500;
  letter-spacing: .25em; text-transform: uppercase;
  color: var(--accent-bright);
  margin-bottom: 14px;
}
.m-flight { display: grid; grid-template-columns: auto 1fr; gap: 9px 16px; }
.m-flight .f-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-dim);
  align-self: center;
}
.m-flight .f-value {
  font-size: 14.5px; font-weight: 500;
  text-align: right;
  align-self: center;
}
/* every timeline is a stack of rows now — the old two-column grid
   was for the bare date/text pairs a plain timeline used to be */
.m-timeline { display: block; }

/* ------------------------------------------ the timeline, played as film
   A mission with film of its events gets a playable timeline: the same
   dates and the same lines, with a rail down the side of them, a dot on
   the one you are watching, and a hairline that fills as its shot runs.
   Everything here is scoped to .playable, so every other mission's
   timeline is left exactly as it was. */
.tl-head { display: flex; align-items: center; gap: 14px; }
.tl-head h4 { margin-bottom: 0; }
.m-sec .tl-head { margin-bottom: 14px; }
.tl-play {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .42);
  transition: color .2s; }
.tl-play[hidden] { display: none; }
.tl-play:hover { color: var(--accent-bright); }
.tl-play .disc {
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1px solid rgba(224, 130, 57, .6);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent-bright);
  font-size: 8px; padding-left: 1px;
  transition: border-color .2s, background .2s; }
.tl-play:hover .disc {
  border-color: var(--accent-bright);
  background: rgba(224, 130, 57, .14); }


.tl-row {
  position: relative;
  display: grid; grid-template-columns: 14px auto 1fr;
  gap: 0 14px;
  padding: 7px 8px;
  margin-left: -8px;
  border-radius: 4px;
  cursor: pointer;
  transition: background .25s; }
.tl-row:hover { background: rgba(255, 255, 255, .045); }
.tl-rail { position: relative; grid-row: 1 / 3; }
.tl-rail::before {
  content: '';
  position: absolute;
  left: 6px; top: -8px; bottom: -8px;
  width: 1px;
  background: rgba(255, 255, 255, .12); }
.tl-row:first-child .tl-rail::before { top: 14px; }
.tl-row:last-child .tl-rail::before { bottom: calc(100% - 14px); }
.tl-dot {
  position: absolute;
  left: 1px; top: 8px;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid rgba(255, 255, 255, .26);
  transition: .3s; }
.m-timeline .t-date { grid-column: 2; transition: color .25s; }
.m-timeline .t-text { grid-column: 3; }
/* A `.tl-bar` progress bar lived here. fillTimeline stopped building one
   on 15 Aug 2026 — the reel waits for Continue, so there is no clock to
   draw — and the rules went with the last line that reached for it
   (closeFilm, 8 Sep 2026). */
/* the line you are watching */
.tl-row.on { background: rgba(224, 130, 57, .10); }
.tl-row.on::before {
  content: '';
  position: absolute;
  left: -8px; top: 0; bottom: 0;
  width: 2px;
  background: var(--accent); }
.tl-row.on .tl-dot {
  background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 14px var(--accent); }
.tl-row.on .t-text { font-weight: 500; }
.tl-row.past .tl-dot {
  background: rgba(224, 130, 57, .5);
  border-color: rgba(224, 130, 57, .5); }
.tl-row.past .t-date, .tl-row.past .t-text { color: var(--ink-dim); }

/* ------------------------------------------------------- the film itself
   Two layers: the outgoing shot pushes up and out while the incoming one
   rises into place, which is the same direction the eye travels down the
   timeline beside it. */
.film-frame {
  position: relative;
  /* the frame takes the film's own shape and stands in the middle of the
     overlay: stretched to the full height it would be a 16:9 picture in
     a tall box, which is a band of black above and below every shot */
  /* the same stage as a flight card's: it takes every pixel the words
     below it do not need */
  flex: 1 1 auto;
  width: 100%; min-height: 200px;
  background: #000;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 6px;
  overflow: hidden; }
.film-layer { position: absolute; inset: 0; }
.film-layer video, .film-layer img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  background: #000; }
.film-layer.out { animation: filmOut .72s cubic-bezier(.65, 0, .35, 1) forwards; }
.film-layer.in { animation: filmIn .72s cubic-bezier(.65, 0, .35, 1) forwards; }
@keyframes filmOut { to { transform: translateY(-18%); opacity: 0; filter: brightness(.4); } }
@keyframes filmIn {
  from { transform: translateY(22%); opacity: 0; filter: brightness(.4); }
  to { transform: none; opacity: 1; filter: none; } }
/* the caption and credit now use the flight card's own classes */
.film-cap p { margin: 0; font-size: 13.5px; line-height: 1.5; max-width: 64ch; }
.film-cap .film-cred { margin-top: 6px; }

/* the transport. A wide, thin track rather than a hairline: it is the
   one control that has to be hit accurately, and a three-and-a-half
   minute descent is worth being able to move around inside. */
.film-scrub {
  position: relative;
  height: 4px; margin-top: 10px;
  background: rgba(255, 255, 255, .12);
  border-radius: 3px;
  cursor: pointer; }
.film-scrub::before {                /* a taller invisible target */
  content: '';
  position: absolute; left: 0; right: 0; top: -8px; bottom: -8px; }
.film-scrub i {
  display: block; height: 100%; width: 0;
  background: var(--accent-bright);
  border-radius: 3px;
  position: relative; }
.film-scrub i::after {
  content: '';
  position: absolute; right: -5px; top: 50%;
  width: 10px; height: 10px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--accent-bright);
  box-shadow: 0 0 10px rgba(224, 130, 57, .8);
  opacity: 0; transition: opacity .2s; }
.film-scrub:hover i::after, .film-scrub:focus-visible i::after { opacity: 1; }
.film-scrub.dead { cursor: default; }      /* a still cannot be scrubbed */
.film-scrub.dead i::after { display: none; }

.film-foot {
  display: flex; align-items: center; gap: 16px;
  margin-top: 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .34); }
.film-foot button {
  font: inherit; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .34);
  transition: color .2s; white-space: nowrap; }
.film-foot button:hover { color: var(--accent-bright); }
.film-foot button.on { color: var(--accent-bright); }
.film-time { font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, .5); }
/* the right-hand group starts here — unless this shot has no sound to
   offer, in which case full screen takes over the job */
#film-sound { margin-left: auto; }
#film-sound[hidden] { display: none; }
#film-sound[hidden] ~ #film-full { margin-left: auto; }
.film-count { font-variant-numeric: tabular-nums; }

/* in full screen the frame is the whole display, so the caption has to
   float over it rather than sit under a border that is no longer there */
.film-frame:fullscreen { aspect-ratio: auto; width: 100%; height: 100%; border: 0; border-radius: 0; }
.film-frame:fullscreen .film-cap { padding: 90px 5vw 5vh; }
.film-frame:fullscreen .film-cap p { font-size: 17px; }
.m-timeline .t-date {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-bright);
  padding-top: 2px;
  white-space: nowrap;
}
.m-timeline .t-text { font-size: 13px; line-height: 1.45; }

.m-model { cursor: pointer; transition: border-color .25s; position: relative; }
.m-model:hover { border-color: rgba(255, 255, 255, .28); }
.m-model .h4-hint { float: right; color: rgba(255, 255, 255, .3); }
.m-model img { display: block; height: 92px; margin: 4px auto 6px; }
.m-model img[hidden] { display: none; }
/* At the head of the media column, over the pictures rather than over
   the index beside them. The space beneath is its own margin, not a row
   gap, so a mission with no model leaves none behind. */
/* ------------------------------------------- the mission's door
   A slim bar at the head of the media column, the size of "Return to
   article" below the mission column and the width of the model card and
   the gallery under it — all three share the pictures track, so they
   share one left edge and one right edge (owner's calls, 7 Sep 2026).
   This is what the page-centre disc became. */
.btn-play {
  grid-column: 2; grid-row: 1;
  width: 100%;
  /* .btn-read measures 54px, not the 58 its own rule asks for: `.btn`'s
     min-height comes later in this file and wins. Fixed rather than a
     minimum, so a long label can never grow the bar out of shape. */
  height: 54px;
  margin-bottom: 22px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 0 14px;
  background: rgba(240, 146, 63, .07);
  border: 1px solid rgba(240, 146, 63, .5);
  border-radius: 6px;
  cursor: pointer;
  color: var(--accent-bright);
  transition: background .25s, border-color .25s, transform .25s;
}
/* the same trap .btn-read fell into: `display: flex` outranks the
   browser's own [hidden] rule, so a mission with nothing to play would
   keep the bar. A reading rule, so it belongs here and not in edit.css. */
.btn-play[hidden] { display: none; }
.btn-play:hover {
  background: rgba(240, 146, 63, .16);
  border-color: var(--accent-bright);
  transform: translateY(-1px);
}
.btn-play:active { transform: none; }
.btn-play .disc {
  width: 26px; height: 26px; flex: 0 0 auto;
  border: 1px solid var(--accent-bright); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; line-height: 1;
  padding-left: 1px;                  /* the triangle's own weight */
  box-sizing: border-box;
  transition: background .25s;
}
.btn-play:hover .disc { background: rgba(240, 146, 63, .18); }
/* `.btn`'s type, stepped down to the column it has to fit: the article's
   own button wears 12px/.22em across a 460px mission column, and at that
   size "PLAY THE MISSION" wraps to two lines in a media column that is
   213px at 1440 and 220px at its narrowest. */
.btn-play-label {
  font-size: 10.5px; font-weight: 600;
  letter-spacing: .16em; text-indent: .16em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}

.art-model { grid-column: 2; grid-row: 2; margin-top: 0; margin-bottom: 22px; }
.art-model img { height: 84px; }
/* 156px of content between the padding: the old "click to enlarge" alone
   ran to 136 of it and pushed the title onto its own line */
.art-model .h4-hint { letter-spacing: .18em; }
/* a tour's craft is rendered as a wide strip rather than a square */
.art-model.wide img { height: auto; width: 100%; }
.m-model-credit {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .3);
}

/* ------------------------------------------------ photo lightbox */
.lightbox {
  position: fixed; inset: 0;
  z-index: 9;
  background: rgba(4, 5, 8, .93);
  display: flex; align-items: center; justify-content: center;
  padding: 5vh 6vw;
  opacity: 0; visibility: hidden;
  transition: opacity .3s ease, visibility 0s linear .3s;
}
.lightbox.show {
  opacity: 1; visibility: visible;
  transition: opacity .3s ease, visibility 0s;
}
.lb-fig {
  margin: 0;
  display: flex; flex-direction: column; align-items: center;
  max-height: 100%;
}
.lb-fig img {
  display: block;
  max-width: 100%; max-height: 100%;
  flex: 0 1 auto; min-height: 0;
  object-fit: contain;
  border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, .14);
}
.lb-fig figcaption {
  margin-top: 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px; line-height: 1.5; letter-spacing: .02em;
  color: rgba(255, 255, 255, .5);
  text-align: center;
  max-width: 70ch;
}
.lb-close {
  position: absolute;
  top: 3vh; right: 3vw;
  font-size: 30px; line-height: 1;
  color: rgba(255, 255, 255, .55);
  transition: color .2s;
}
.lb-close:hover { color: #fff; }
/* stepping through the set, the same way a rover's stop card and a
   tour's pass card already do it */
.lb-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 30px; line-height: 1;
  color: rgba(255, 255, 255, .5);
  transition: color .2s;
}
.lb-nav:hover { color: #fff; }
#lb-prev { left: 2vw; }
#lb-next { right: 2vw; }
.lb-count {
  position: absolute; bottom: 3vh; left: 50%; transform: translateX(-50%);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  color: rgba(255, 255, 255, .45);
}
/* a single photograph is not a sequence */
.lb-nav[hidden], .lb-count[hidden] { display: none; }

/* -------------------------------------------- enlarged model viewer */
.model-viewer, .article-panel, .film-panel {
  /* fixed, not absolute: these are shared by a world's mission page and a
     tour's, so they sit at page level rather than inside one view */
  position: fixed;
  top: 2.5vh; bottom: 2.5vh;
  left: calc(var(--col-left) + var(--col-w) + var(--col-gap));
  right: 2.5vw;
  background: rgba(8, 10, 14, .975);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 8px;
  padding: 20px 26px;
  /* the same column gap as a flight card, so the picture never sits
     tight against the words under it */
  display: flex; flex-direction: column; gap: 12px;
  opacity: 0; visibility: hidden;
  transition: opacity .4s ease, visibility 0s linear .4s;
  z-index: 6;
}
.model-viewer.show, .article-panel.show, .film-panel.show {
  opacity: 1; visibility: visible;
  transition: opacity .4s ease, visibility 0s;
  pointer-events: auto;
}

/* ------------------------------------------------- "read more" button */
.btn-read {
  display: flex;
  width: 100%;
  margin-top: 38px;
  min-height: 58px;
  position: relative;
}
/* `display: flex` above outranks the browser's own rule for [hidden], so
   the button stayed on screen for a mission with no article to open.
   Every mission page had one until Cassini, which is not in the almanac
   yet — the first time this could show. */
.btn-read[hidden] { display: none; }
/* The article opens with the page now, so this is the way BACK to it, and
   it stands in one state only: the article closed and nothing playing
   (owner's calls, 8 Sep 2026). Open, it offered to open what was already
   open; playing, it sat in the mission column beside the film and invited
   the reader out of the ride.

   The flags live on the body because neither panel can be reached from
   this button in CSS: #read-more is inside #detail, and .article-panel
   and .film-panel both come AFTER that section. Two classes deep on
   purpose — `.btn-read[hidden]` is (0,2,0) and would otherwise be the
   strongest rule here. */
body.article-open .btn-read,
body.mission-playing .btn-read { display: none; }
.btn-read::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: -16px;
  height: 1px;
  background: rgba(255, 255, 255, .12);
}

/* --------------------------------------------------- article overlay
   The almanac's own magazine setting: Georgia, justified, an amber drop
   cap, and photos floated so the text wraps around them. */
/* the article fills the overlay, set the way the almanac sets it: one
   justified column with the owner's photos floated into it */
/* the piece on the left, the mission's photographs on the right */
.art-cols {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 40px;
}
.art-body {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  padding: 20px 6px 8px 0;
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
}

/* ---------------------------------------------- two-column gallery
   A vertical scroller of thumbnails two abreast, with a numbered index
   beside it: the arrows step a row at a time, the numbers jump, and the
   current row stays lit as you scroll. */
/* The media column. It holds the width the gallery used to own, so the
   model card above the photographs lines up with them exactly, and the
   gallery takes whatever height is left under it. */
.art-side {
  width: clamp(220px, 34%, 560px);
  flex: 0 0 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);        /* index | pictures */
  grid-template-rows: auto auto minmax(0, 1fr);      /* play | model | gallery */
  column-gap: 18px;
  padding-top: 20px;
}
/* The gallery's own box dissolves, so its index column and its grid of
   pictures become columns of the media column itself. That is what lets
   the model card start exactly where the photographs start, whatever
   width the page numbers happen to need — no measuring, no guessing. */
.art-gallery { display: contents; }
.art-gallery[hidden] { display: none; }
.gal-nav {
  grid-column: 1; grid-row: 3;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding-top: 4px;
}
.gal-step {
  font-size: 15px; line-height: 1;
  color: rgba(255, 255, 255, .45);
  transition: color .2s;
}
.gal-step:hover { color: #fff; }
.gal-step[disabled] { opacity: .25; pointer-events: none; }
.gal-nums { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.gal-num {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: .1em;
  color: rgba(255, 255, 255, .4);
  padding: 3px 6px;
  border-radius: 3px;
  transition: color .2s, background .2s;
}
.gal-num:hover { color: #fff; }
.gal-num.on { color: #fff; background: rgba(255, 255, 255, .12); }
/* two columns that fill the box: the row height is set in JS so exactly
   two rows fit the visible area, which makes one screenful one "page" */
.gal-grid {
  grid-column: 2; grid-row: 3;
  min-width: 0;
  overflow-y: auto;
  scrollbar-width: none;
  scroll-behavior: smooth;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-content: start;
}
.gal-grid::-webkit-scrollbar { display: none; }
.gal-grid figure {
  margin: 0;
  /* min-height stays AUTO on purpose. The thumbnail gets its height from
     `aspect-ratio` against a width that is itself still being resolved,
     and with the automatic minimum removed the browser is entitled to
     size this figure as though the picture contributed nothing: the row
     came out 19px tall, the 72px image overflowed it, and every row sat
     on top of the one below. Cheap to reintroduce, hard to spot with a
     handful of photographs and unmissable with thirty. */
  /* without this a grid item refuses to go narrower than its longest
     unbreakable word, and some captions are filenames — one of New
     Horizons' ran the first column to 217px inside a 194px box and
     pushed the pictures out past everything else in the column */
  min-width: 0;
  display: flex; flex-direction: column;
  cursor: zoom-in;
}
.gal-grid img {
  display: block; width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .12);
  transition: border-color .2s, opacity .2s;
}
.gal-grid figure:hover img { border-color: rgba(255, 255, 255, .4); opacity: .88; }
.gal-grid figcaption {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; line-height: 1.45; letter-spacing: .02em;
  color: rgba(255, 255, 255, .35);
  margin-top: 7px;
  /* two lines is enough to identify a thumbnail; the full caption and
     credit live in the title attribute */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;     /* wrap a long filename, don't slice it */
}
/* the piece fills its column — the gallery beside it already keeps the
   measure in readable territory, so there is no need to gutter it */
.art-inner { max-width: 1080px; }
.art-inner::after { content: ''; display: block; clear: both; }
.art-body::after { content: ''; display: block; clear: both; }
.art-fig { margin: 8px 0 22px; }
.art-fig img {
  display: block; width: 100%; height: auto;
  border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, .12);
}
.art-fig figcaption {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px; line-height: 1.5; letter-spacing: .02em;
  color: rgba(255, 255, 255, .38);
  margin-top: 8px;
}
/* clear: both — with two photos placed a paragraph apart, clearing only
   their own side lets them sit opposite each other and squeeze the text
   into an unreadable gutter between them */
/* the width the almanac stores is a pixel value chosen for its own wide
   column; cap it so a photo can never crowd out the text here */
.art-fig.side-right { float: right; clear: both; margin-left: 40px; max-width: 46%; }
.art-fig.side-left  { float: left;  clear: both; margin-right: 40px; max-width: 46%; }
.art-fig.side-full  { float: none;  clear: both;  width: 100%; margin: 16px 0 24px; }
.art-head {
  clear: both;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent);
  margin: 38px 0 16px; padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, .12);
}
.art-head:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* owner-authored pull-quote, breaking the column */
.art-pull {
  clear: both; margin: 26px 0;
  padding: 8px 0 8px 26px;
  border-left: 3px solid var(--accent);
}
.art-pull p {
  font-size: 27px; line-height: 1.24; font-style: italic;
  color: #f5f2ea; margin: 0;
  text-align: left; hyphens: none; max-width: 30ch;
}
.art-pull cite {
  display: block; margin-top: 12px;
  font-family: 'IBM Plex Mono', monospace; font-style: normal;
  font-size: 11.5px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent-bright);
}
.art-body p {
  font-size: 19px; line-height: 1.66; margin: 0 0 20px;
  color: #e6e1d6;
  text-align: justify; hyphens: auto; -webkit-hyphens: auto;
}
.art-body p:first-of-type::first-letter {
  float: left;
  font-family: Georgia, serif; font-weight: 700;
  font-size: 70px; line-height: .66;
  padding: 9px 12px 0 0;
  color: var(--accent-bright);
}
.art-body p.no-cap::first-letter {
  float: none;
  font: inherit; line-height: inherit;
  padding: 0; color: inherit;
}
/* ------------------------------------------------ the reference list
   The foot of an article: its sources, set in the style the record
   names (main.js refParts). A numbered list is what the [n] markers in
   the paragraphs point at; an author–date list hangs its entries. */
.art-refs {
  clear: both;
  margin: 0 0 12px; padding-left: 2em;
  font-size: 15px; line-height: 1.55; color: #cfc9bd;
  text-align: left; hyphens: none;
}
/* an entry is spaced like a paragraph, and its number is white (owner's
   call, 6 Sep 2026) */
.art-refs li { margin: 0 0 20px; padding-left: 4px; }
.art-refs li::marker {
  font-family: 'IBM Plex Mono', monospace; font-size: 12.5px;
  color: #fff;
}
.art-refs.alpha { list-style: none; padding-left: 0; }
.art-refs.alpha li { padding-left: 2em; text-indent: -2em; }
.art-refs a { color: var(--accent-bright); text-decoration: none; overflow-wrap: anywhere; }
.art-refs a:hover { text-decoration: underline; }
.art-refs .ref-note { color: #9c968b; }
.art-refs li.lit { background: rgba(224, 130, 57, .16); border-radius: 4px; }
.art-refs li { transition: background .6s ease; }
/* the marker in the text: a small raised number that is a link */
.ref-mark { font-size: .62em; line-height: 0; vertical-align: super; margin-left: 1px; font-style: normal; }
.ref-mark a { color: var(--accent-bright); text-decoration: none; padding: 0 1px; }
.ref-mark a:hover { text-decoration: underline; }
.mv-head { display: flex; justify-content: space-between; align-items: center; }
.mv-head h4 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; font-weight: 500;
  letter-spacing: .25em; text-transform: uppercase;
  color: var(--ink-dim);
}
.mv-close {
  font-size: 26px; line-height: 1;
  color: var(--ink-dim);
  transition: color .25s;
}
.mv-close:hover { color: #fff; }
/* the stage takes the whole overlay below its heading, and the canvas
   takes the whole stage — anything less and the render is cut off */
.mv-stage canvas { width: 100% !important; height: 100% !important; display: block; }
/* a model that brings its own page (model.embed) fills the stage the same way */
.mv-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; background: #060608; }
.mv-stage {
  position: relative;
  /* Take the whole overlay. It used to be a square of min(76vh, 46vw),
     which is narrower than the panel it sits in — so a craft framed to
     fill the view was cut off at the canvas edge with empty overlay
     either side of it. */
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  margin: 0;
}
/* labels never blink out as the craft turns: an anchor that swings round
   the back just recedes, so the text stays readable throughout */
.mv-callout { position: absolute; transition: opacity .28s ease; }
.mv-dot {
  position: absolute;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent-bright);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 8px rgba(224, 130, 57, .9);
}
.mv-line {
  position: absolute;
  height: 1px;
  background: rgba(255, 255, 255, .5);
  transform-origin: 0 50%;
}
/* A plate, not a text-shadow. The world tooltip carries itself over a
   bright limb on shadow alone, but that is 38px type over a planet; this
   is 11px over a lit metal hull, and the reader can zoom until the
   spacecraft fills the stage and there is no empty sky left to stand in.
   Same plate the world stat and the tours card use, darkened because the
   text on it is small. Border-box, so the width still measures the box:
   196 leaves the text the 178 it had. */
.mv-lbl {
  position: absolute;
  width: 196px;
  padding: 7px 9px;
  border-radius: 5px;
  /* .82, not less: the dim body text over a white-lit hull measures
     4.1:1 at .72 and 5.9:1 at .82, and small text wants 4.5:1 to be
     read rather than deciphered. */
  background: rgba(6, 8, 12, .82);
  backdrop-filter: blur(5px);
  box-shadow: 0 2px 14px rgba(0, 0, 0, .45);
  font-size: 11px; line-height: 1.45;
}
.mv-lbl b {
  display: block;
  font-size: 11.5px; font-weight: 600; letter-spacing: .05em;
  margin-bottom: 2px;
}
.mv-lbl span { color: var(--ink-dim); }
.mv-credit {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .32);
}

@media (max-width: 760px) {
  .patch-panel { width: 60vw; }
  .mission-panel { width: 70vw; }
}
#hero { pointer-events: none; }
#hero a, #hero button { pointer-events: auto; }

/* ---------------------------------------------------------------- brand */
.brand {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 15px; font-weight: 600;
  letter-spacing: .32em; text-transform: uppercase;
  white-space: nowrap;
}
.brand-mark { width: 30px; height: 30px; flex: 0 0 auto; }

/* ----------------------------------------------------------------- hero */
.hero-top {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 44px clamp(28px, 6vw, 92px) 0;
}
.hero-copy {
  position: absolute;
  left: clamp(28px, 6vw, 92px);
  top: 50%;
  transform: translateY(-54%);
  max-width: 760px;
}
.hero-copy h1 {
  font-size: clamp(30px, 4.1vw, 56px);
  line-height: 1.22;
  font-weight: 600;
  letter-spacing: .045em;
  text-transform: uppercase;
  text-wrap: balance;
}
.hero-copy h1 .thin { font-weight: 400; color: #e9e6e0; }
.hero-copy h1 b { font-weight: 600; color: #fff; }

/* The word struck out by hand.  A drawn line rather than
   text-decoration: the browser's rule is dead straight and the exact
   colour of the text, which reads as a typographic effect; this is a
   red pen going over the word — two passes, wobbling, overshooting
   both ends and rising to the right the way a right hand does.  The
   SVG is stretched to the word (preserveAspectRatio: none) but the ink
   is not: non-scaling-stroke keeps the nib an even width, and the
   width is set in em so it thins with the heading on small screens. */
.hero-copy h1 .struck {          /* <s>, so the sense survives without CSS */
  position: relative; display: inline-block; text-decoration: none;
}
.strike {
  position: absolute;
  left: -.16em; top: 0;
  width: calc(100% + .32em); height: 100%;
  overflow: visible;
  pointer-events: none;
}
.strike path {
  fill: none;
  stroke: #e0392b;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: strike-draw .38s cubic-bezier(.36, .06, .3, 1) .75s forwards;
}
/* Stroke widths in viewBox units, and deliberately NO vector-effect:
   non-scaling-stroke moves the dash pattern into screen space in
   Chromium, which defeats the pathLength calibration above — the drawn
   strike stopped short of the word and a stray tail of the "hidden"
   line sat over the final letters until the animation fired (found
   28 Aug 2026 while testing the word at full heading size).  Unit
   strokes scale with the stretched viewBox, so the nib still thins
   with the heading on small screens, which is what the old em widths
   were for. */
.strike .s-1 { stroke-width: 3.1; }
.strike .s-2 {                         /* the pen going back over it */
  stroke-width: 2.1;
  stroke-opacity: .72;
  animation-duration: .3s;
  animation-delay: 1.16s;
}
@keyframes strike-draw { to { stroke-dashoffset: 0; } }

/* Struck out either way — the mark is the point, the drawing of it isn't. */
@media (prefers-reduced-motion: reduce) {
  .strike path { animation: none; stroke-dashoffset: 0; }
}

.hero-sub {
  margin-top: 30px;
  font-size: 16.5px;
  line-height: 1.65;
  color: var(--ink-dim);
  letter-spacing: .02em;
}
.hero-sub em { font-style: normal; color: var(--accent-bright); font-weight: 600; }

.hero-cta { margin-top: 56px; display: flex; gap: 18px; flex-wrap: wrap; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  min-height: 54px;
  padding: 0 34px;
  font-size: 12px; font-weight: 600;
  letter-spacing: .22em; text-transform: uppercase;
  transition: background .25s, border-color .25s, color .25s, transform .25s;
}
.btn-solid {
  background: var(--accent);
  color: #16110b;
  color: #fff;
}
.btn-solid:hover { background: var(--accent-bright); transform: translateY(-1px); }
.btn-solid .arrow { font-size: 15px; transition: transform .25s; }
.btn-solid:hover .arrow { transform: translateX(4px); }
.btn-ghost {
  border: 1px solid rgba(255, 255, 255, .45);
  color: #fff;
}
.btn-ghost:hover { border-color: #fff; background: rgba(255, 255, 255, .06); }
.btn-ghost .play { font-size: 13px; color: var(--ink); }

.hero-foot {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: flex-end;
  gap: clamp(32px, 8vw, 130px);
  padding: 0 clamp(28px, 6vw, 92px) 46px;
}
.socials { margin-left: auto; display: flex; gap: 14px; }
.socials a {
  width: 38px; height: 38px; border-radius: 50%;
  background: #f4f2ee;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .25s, transform .25s;
}
.socials a:hover { background: var(--accent-bright); transform: translateY(-2px); }
.socials svg { width: 16px; height: 16px; fill: #14120f; }

/* ------------------------------------------------------------- explorer */
/* The wordmark lives outside every view, so it never inherits a view's
   fade — moving between the carousel, planet and mission pages leaves it
   solid. It appears once, when the hero gives way to the explorer. */
.brand-fixed {
  position: fixed;
  top: 44px; right: clamp(28px, 6vw, 92px);
  /* Above the canvas but below the views. Each view is its own stacking
     context (z-index 4), so an overlay inside one cannot out-rank a
     sibling: at 5 the wordmark printed straight through the opened
     article and model panels. At 3 those panels cover it. */
  z-index: 3;
  opacity: 0; visibility: hidden;
  transition: opacity .5s ease, visibility 0s linear .5s;
}
body.mode-explore .brand-fixed {
  opacity: 1; visibility: visible;
  transition: opacity .5s ease, visibility 0s;
}
.explore-top {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center;
  padding-top: 44px;
}
.back {
  position: absolute;
  left: clamp(28px, 6vw, 92px);
  top: 38px;
  font-size: 30px;
  color: var(--ink-dim);
  transition: color .25s, transform .25s;
}
.back:hover { color: #fff; transform: translateX(-4px); }

.edge-nav {
  position: absolute; top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  font-size: 44px; line-height: 1;
  font-weight: 400;
  color: rgba(255, 255, 255, .35);
  padding: 18px 20px;
  transition: color .25s;
}
.edge-nav:hover { color: #fff; }
.edge-prev { left: clamp(6px, 2vw, 30px); }
.edge-next { right: clamp(6px, 2vw, 30px); }
.edge-nav[disabled] { opacity: 0; pointer-events: none; }

/* the hover tooltip: the same name and tally that used to sit under the
   planet, now following the pointer across it. No plate behind it — the
   deep text-shadow carries it over both a bright limb and empty space. */
.world-tip {
  position: fixed;
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  transition: opacity .18s ease;
}
.world-tip.show { opacity: 1; }
.world-tip h2 {
  font-size: clamp(26px, 2.7vw, 38px);
  font-weight: 600;
  letter-spacing: .015em;
  text-shadow: 0 2px 26px rgba(0, 0, 0, .95), 0 0 10px rgba(0, 0, 0, .8);
}
.world-stat {
  margin-top: 2px;
  font-size: clamp(15px, 1.5vw, 20px);
  font-weight: 600;
  color: var(--accent-bright);
  text-shadow: 0 2px 22px rgba(0, 0, 0, .95), 0 0 10px rgba(0, 0, 0, .8);
}
/* The world selector, lower right: one dot per body, sized by the same
   compression the carousel uses, so the ladder reads at a glance. The
   view above is pointer-events: none, so the panel opts back in — without
   that its clicks fall through to the canvas behind it. */
.world-select {
  position: absolute;
  right: clamp(28px, 6vw, 92px);
  bottom: 5.5vh;                     /* level with the card's button */
  padding: 16px 24px 18px;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 8px;
  background: rgba(6, 8, 12, .42);
  backdrop-filter: blur(6px);
  pointer-events: auto;
  text-align: right;
}
.ws-now {
  font-size: 15px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent-bright);
}
.scale-strip {
  margin-top: 22px;
  padding-bottom: 2px;
  display: flex; align-items: flex-end; justify-content: flex-end;
  gap: 13px;
  min-height: 68px;                  /* the Sun sets the height; no jumping */
}
.scale-strip b {
  position: relative;
  display: block;
  border-radius: 50%;
  background: rgba(255, 255, 255, .34);
  cursor: pointer;
  transition: background .25s, box-shadow .25s;
}
/* a generous invisible hit area, so the small worlds stay easy to click */
.scale-strip b::before { content: ''; position: absolute; inset: -12px -6px; }
/* the world's name, on hover, above its dot */
.scale-strip b::after {
  content: attr(data-name);
  position: absolute;
  bottom: calc(100% + 12px); left: 50%;
  transform: translateX(-50%);
  font-size: 10.5px; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: #fff;
  white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity .2s;
}
.scale-strip b:hover { background: rgba(255, 255, 255, .68); }
.scale-strip b:hover::after { opacity: 1; }
.scale-strip b.on {
  background: var(--accent-bright);
  box-shadow: 0 0 0 4px rgba(224, 138, 62, .22);
}

.hint {
  position: absolute;
  left: 50%; top: 13vh;
  transform: translateX(-50%);
  font-size: 11px; letter-spacing: .26em; text-transform: uppercase;
  color: rgba(255, 255, 255, .38);
  transition: opacity 1s ease;
}
.hint.fade { opacity: 0; }
/* on a world's own page the planet sits right of centre, so its hint
   goes under it rather than across the top */
.hint-detail { left: 73%; top: 86vh; }

/* Hover affordance on the carousel: a hairline halo just outside the
   featured world's limb, saying "this opens its page". The render loop
   parks it, so it tracks the planet's glide and size. */
.planet-ring {
  position: fixed;
  z-index: 3;                        /* over the canvas, under the UI */
  border-radius: 50%;
  border: 1px solid rgba(224, 138, 62, .5);
  box-shadow: 0 0 46px rgba(224, 138, 62, .18),
              inset 0 0 46px rgba(224, 138, 62, .09);
  pointer-events: none;
  opacity: 0;
  transition: opacity .28s ease;
}
.planet-ring.show { opacity: 1; }
#scene.pointing { cursor: pointer; }

.credit {
  position: fixed;
  right: 14px; bottom: 8px;
  z-index: 6;
  font-size: 9.5px;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .28);
}
/* the corner is a door to /credits now, and the attribution rides on it
   being findable — a touch brighter than the old whisper, underlined on
   approach */
.credit a {
  color: rgba(255, 255, 255, .45);
  text-decoration: none;
}
.credit a:hover, .credit a:focus-visible {
  color: rgba(255, 255, 255, .8);
  text-decoration: underline;
}

/* ------------------------------------------------------------ small screens */
@media (max-width: 760px) {
  .hero-copy { top: 46%; }
  .hero-foot { gap: 26px; padding-bottom: 30px; }
  .socials { display: none; }
  .edge-nav { top: 42%; }
  .brand span { font-size: 12px; letter-spacing: .24em; }
  .hint { display: none; }
}


/* ==================================================================
   GRAND TOURS
   Missions that visited several worlds belong to none of them, so they
   get a door of their own on the carousel, a page that shows each
   route, and a mission page where the flight replaces the planet.
   ================================================================== */

/* ------------------------------------------- the card on the carousel
   A window onto the craft and the worlds they crossed, in the corner
   opposite the world selector and cropped by its own frame so it reads
   as a view onto something larger. */
.tours-card {
  position: absolute;
  left: clamp(28px, 6vw, 92px);
  bottom: 5.5vh;                     /* level with the world selector */
  width: clamp(230px, 22vw, 310px);
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 8px;
  background: rgba(6, 8, 12, .5);
  backdrop-filter: blur(6px);
  overflow: hidden;
  text-align: left;
  pointer-events: auto;
  transition: border-color .3s ease, transform .3s ease, box-shadow .3s ease;
}
.tours-card:hover {
  border-color: rgba(240, 146, 63, .5);
  transform: translateY(-3px);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .5);
}
.tc-art {
  position: relative; display: block;
  height: clamp(104px, 11vh, 148px);
  overflow: hidden;
  background: radial-gradient(120% 100% at 70% 10%, rgba(60, 50, 90, .35), rgba(6, 6, 8, 0) 70%);
}
/* a world, from the demo's own texture, lit from one side — the small
   discs get a gentler terminator or they read as grey pebbles */
.tc-disc {
  position: absolute; aspect-ratio: 1; border-radius: 50%;
  background-size: 210% 100%; background-repeat: no-repeat;
  opacity: .5;
}
.tc-disc::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background:
    radial-gradient(circle at 32% 30%, rgba(255, 255, 255, .10), rgba(255, 255, 255, 0) 50%),
    radial-gradient(circle at 74% 68%, rgba(0, 0, 0, 0) 34%, rgba(0, 0, 0, .62) 88%);
  box-shadow: inset -5px -4px 14px rgba(0, 0, 0, .4);
}
.tc-craft { position: absolute; object-fit: contain; opacity: .92; }
.tc-foot { display: block; padding: 11px 15px 13px; border-top: 1px solid rgba(255, 255, 255, .08); }
.tc-foot b {
  display: block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .24em; text-transform: uppercase;
  color: var(--accent-bright);
}
.tc-foot span {
  display: block; margin-top: 5px;
  font-size: 11.5px; line-height: 1.5; color: var(--ink-dim);
}
.tours-card:hover .tc-foot span { color: var(--ink); }

/* --------------------------------------------------- the tours page */
.tours-inner {
  /* the routes are the point of this page, so give them the width: the
     arc simply stretches, and every stop gets room for its own label */
  max-width: 2000px;
  margin: 0 auto;
  padding: 10vh clamp(28px, 4vw, 72px) 8vh;
}
.tours-lede {
  margin-top: 12px;
  font-size: 15px; line-height: 1.7; color: var(--ink-dim); max-width: 66ch;
}
#tours { overflow-y: auto; }

.t-case { margin-top: 30px; padding-top: 30px; border-top: 1px solid rgba(255, 255, 255, .12); }
.t-case-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
.t-case-head h3 { font-size: clamp(21px, 2.3vw, 30px); font-weight: 600; }
.t-chip-lg {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent-bright);
  border: 1px solid rgba(224, 130, 57, .55);
  border-radius: 3px; padding: 5px 11px 4px;
}
.t-case-sub { margin-top: 8px; font-size: 13.5px; color: var(--ink-dim); max-width: 70ch; }

/* the badge is the selector, the same gesture as a patch on a wall */
.t-route-row { display: flex; align-items: center; gap: clamp(20px, 3vw, 46px); }
.t-badge {
  flex: 0 0 auto;
  width: clamp(92px, 8.5vw, 126px);
  text-align: center;
}
/* A patch on the wall, the same as a planet's: the insignia at its own
   shape and full size, on nothing. The ring these used to sit in was
   right for the agency roundel they used to be, and wrong for a patch —
   it cropped the shape the mission chose down to somebody else's
   circle. Same drop shadow and the same grow-on-hover as .patch. */
.t-badge-art {
  position: relative;
  width: 100%; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  transition: transform .3s ease;
}
.t-badge img {
  width: auto; height: auto; max-width: 100%; max-height: 100%;
  filter: drop-shadow(0 5px 12px rgba(0, 0, 0, .65));
}
.t-badge:hover .t-badge-art { transform: scale(1.09); }
.t-badge b { display: block; margin-top: 11px; font-size: 12px; font-weight: 600; letter-spacing: .03em; }
.t-badge i {
  display: block; margin-top: 3px; font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
}
.t-badge:hover i { color: var(--accent-bright); }

/* the journey itself */
.t-route { position: relative; flex: 1 1 auto; min-width: 0; height: clamp(240px, 34vh, 380px); }
.t-route svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.t-stop { position: absolute; transform: translate(-50%, -50%); text-align: center; }
.t-orb {
  position: relative; margin: 0 auto; border-radius: 50%;
  background-size: 200% 100%; background-position: 30% 50%;
  box-shadow: inset -8px -6px 22px rgba(0, 0, 0, .85),
              inset 3px 2px 10px rgba(255, 255, 255, .10),
              0 0 26px rgba(0, 0, 0, .7);
}
.t-orb.ring::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 210%; height: 26%;
  transform: translate(-50%, -50%) rotate(-14deg);
  border-radius: 50%;
  border: 2px solid rgba(226, 205, 165, .55);
  border-bottom-color: rgba(226, 205, 165, .18);
}
.t-stop b { display: block; margin-top: 12px; font-size: 13px; font-weight: 600; letter-spacing: .04em; }
.t-stop i {
  display: block; margin-top: 3px; font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim);
}
.t-crawler { position: absolute; width: 15px; height: 15px; transform: translate(-50%, -50%); }
.t-crawler svg { width: 100%; height: 100%; overflow: visible; }
.t-end {
  position: absolute; transform: translate(-50%, -50%);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .5); text-align: center; white-space: nowrap;
}
.t-end b { display: block; color: var(--ink); font-size: 12px; letter-spacing: .06em; margin-bottom: 4px; }
.t-scale {
  display: flex; align-items: center; gap: 14px; margin-top: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255, 255, 255, .3);
}
.t-scale i { flex: 1 1 auto; height: 1px; background: rgba(255, 255, 255, .1); }

/* ------------------------------------------ a tour's mission page */
/* the worlds it reached, as a row of discs */
.t-worlds { display: flex; flex-wrap: wrap; gap: 16px; }
.t-world { display: flex; align-items: center; gap: 8px; }
.t-world i {
  width: 20px; height: 20px; border-radius: 50%;
  background-size: 200% 100%; background-position: 34% 50%;
  box-shadow: inset -4px -3px 9px rgba(0, 0, 0, .8), inset 2px 1px 5px rgba(255, 255, 255, .12);
}
.t-world span {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim);
}
.t-list { list-style: none; }
.t-list li {
  position: relative; padding-left: 16px; margin-bottom: 9px;
  font-size: 13.5px; line-height: 1.6; color: var(--ink-dim);
}
.t-list li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}

/* the flight, in the space a planet would occupy: no frame of its own,
   and no second starfield — it is transparent over the demo's own */
.tour-stage {
  position: absolute;
  left: calc(var(--col-left) + var(--col-w) + var(--col-gap));
  right: 2vw; top: 8vh; bottom: 4vh;
}
.tour-stage iframe {
  width: 100%; height: 100%;
  border: 0; display: block; background: transparent;
}

/* The page-centre cinema glyph, `.ignite`, lived here. Both its users
   are gone: a mission page's (`.m-ignite`) on 7 Sep 2026 and a tour's
   landing gate (`#t-ignite`) on 8 Sep, when a tour began opening on
   its article like a mission and the play bar in that article became
   the door — see `.btn-play` above. The overture itself is unchanged;
   it is the backdrop now rather than a gate. */

/* The halt window: option 2b of the merge study. It inherits everything a
   window is from .film-panel — the .975 ground, the head, the foot — and
   changes only its ground plan: pulled off the bottom so the flight's own
   strip stays readable, and edged in the ring's colour, because at full
   size it covers the world it grew out of and belonging to the ring is
   the only link left to carry. */
.halt-panel {
  /* The full window every other mission gets — the Mercury cards' own
     2.5vh frame, inherited from .film-panel. It was pulled in to keep
     the flight's strip visible, and that squeezed the body until its
     text grew a scrollbar; the owner's call (17 Aug 2026) is that the
     window may stand over the strip at a stop. Only the edge colour is
     this window's own: the ring's amber, the link to the world it grew
     from. */
  border-color: rgba(224, 138, 62, .55);
}
.halt-frame { position: relative; flex: 1 1 auto; min-height: 200px; }
/* Absolute inside the frame, the way the Mercury cards stage their media
   (.fl-stage): with nothing in normal flow the frame's flex basis is
   zero, so the story below gets its full height FIRST and the stage
   takes every pixel it does not need — in-flow media handed the frame
   its own height as a basis and squeezed the text into a scrollbar. */
.halt-img, .halt-video { position: absolute; inset: 0;
                         width: 100%; height: 100%; display: block; }
/* Nothing on a stop card is cropped (owner's call, 30 Aug 2026). These
   are the mission's own photographs and the whole frame is the point:
   cover filled the stage handsomely and quietly ate the edges — three
   of the four Galilean moons, half of a true-beside-false pair, the
   ends of every panorama. A picture that does not match the stage's
   shape sits in black instead, which is what the sky was anyway.
   A film was always contained, for the same reason. */
.halt-img, .halt-video { object-fit: contain; background: #000; }



/* ==================================================================
   THE SURFACE
   Reached by pressing the landing site on a mission page. The planet
   above is the demo's own — nothing here changes how it is drawn; the
   descent only grows it until it fills the frame, and the map arrives
   underneath at that moment and not before.
   ================================================================== */

/* the hand-over from planet to ground is a cut, not a dissolve: a fade
   would show whatever is behind both of them for a third of a second */
.view.cut { transition: none; }

/* the way back out, in the colour the route is drawn in */
#surface .back {
  z-index: 9; pointer-events: auto;
  color: var(--accent-bright);
  text-shadow: 0 0 18px rgba(0, 0, 0, .9);
}
#surface .back:hover { color: #ffc48a; }

/* the mark on the globe becomes a door */
.site-go {
  display: inline-flex; margin-top: 9px;
  padding: 5px 11px 4px;
  border: 1px solid rgba(240, 146, 63, .55); border-radius: 3px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent-bright);
  transition: background .25s, border-color .25s, color .25s;
}
.site .site-go { display: none; }
.site.big.can-land .site-go { display: inline-flex; }
.site.big.can-land { pointer-events: auto; cursor: pointer; }
.site.big.can-land:hover .site-go {
  background: var(--accent-bright); border-color: var(--accent-bright); color: #17130e;
}

/* the readout on the way down */
#sf-descent {
  position: fixed; inset: 0; z-index: 7; pointer-events: none;
  opacity: 0; transition: opacity .35s ease;
}
#sf-descent.on { opacity: 1; }
.sf-reticle {
  position: absolute; left: 50%; top: 50%; width: 42vmin; height: 42vmin;
  margin: -21vmin 0 0 -21vmin; border: 1px solid rgba(240,146,63,.35); border-radius: 50%;
  animation: sf-close 2.6s ease-in-out forwards;
}
@keyframes sf-close {
  from { transform: scale(2.4); opacity: 0; } 40% { opacity: 1; } to { transform: scale(.28); opacity: 0; }
}

/* the map */
#surface { z-index: 6; }              /* over the planet, under the overlays */
.sf-frame { position: absolute; inset: 0; overflow: hidden; }
.sf-map { position: absolute; left: 50%; top: 50%; transform-origin: 50% 50%; will-change: transform; }
.sf-map img.sf-base { display: block; width: 100%; height: 100%; }
.sf-map .sf-ink { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.sf-frame::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 50%, rgba(6,6,8,0) 55%, rgba(6,6,8,.85) 100%);
}

.sf-pin { position: absolute; pointer-events: none;
  transform: translate(-50%, -50%) scale(var(--sf-inv, 1)); transform-origin: 50% 50%; }
.sf-pin b {
  position: absolute; left: 14px; top: -7px; white-space: nowrap;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px; letter-spacing: .18em; text-transform: uppercase; color: rgba(255,255,255,.62);
  opacity: 0; transition: opacity .45s ease;
}
.sf-pin.done b { opacity: 1; }
.sf-pin i {
  display: block; width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.6); background: rgba(6,6,8,.6);
}
.sf-pin.done i { border-color: var(--accent-bright); background: rgba(240,146,63,.35); }
.sf-pin.last i { width: 13px; height: 13px; border: 1px dashed rgba(120,200,255,.75); background: none; }
.sf-pin.last b { color: rgba(150,210,255,.8); opacity: 1; }

.sf-rover { position: absolute; width: 14px; height: 14px; pointer-events: none;
  transform: translate(-50%, -50%) scale(var(--sf-inv, 1)); transform-origin: 50% 50%; }
.sf-rover i { position: absolute; inset: 4px; border-radius: 50%;
  background: #fff; box-shadow: 0 0 14px rgba(255,255,255,.95); }
.sf-rover s { position: absolute; inset: 0; border-radius: 50%; text-decoration: none;
  border: 1px solid rgba(255,255,255,.85); animation: sf-rove 1.8s ease-out infinite; }
@keyframes sf-rove {
  0% { transform: scale(.5); opacity: 1; } 100% { transform: scale(3.2); opacity: 0; }
}

.sf-halo {
  position: absolute; transform: translate(-50%, -50%);
  width: 96px; height: 96px; border-radius: 50%; z-index: 7;
  border: 1px solid rgba(240,146,63,.8);
  box-shadow: 0 0 34px rgba(240,146,63,.3), inset 0 0 26px rgba(240,146,63,.12);
  opacity: 0; transition: opacity .4s ease; pointer-events: none;
}
.sf-halo.on { opacity: 1; }

.sf-card {
  position: absolute; width: min(370px, 34vw); z-index: 8;
  padding: 18px 20px 16px;
  border: 1px solid rgba(224,138,62,.55); border-radius: 8px;
  background: rgba(8,7,10,.94);
  box-shadow: 0 0 46px rgba(224,138,62,.14);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .4s ease, visibility 0s linear .4s;
}
.sf-card.on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .4s ease, visibility 0s; }
.sf-card h3 { font-size: 19px; font-weight: 600; }
.sf-when { display: block; margin-top: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px; letter-spacing: .18em; text-transform: uppercase; color: var(--accent-bright); }
.sf-lead { margin-top: 12px; font-size: 14.5px; line-height: 1.5; font-weight: 600; }
.sf-fact { margin-top: 9px; font-size: 12.5px; line-height: 1.6; color: var(--ink-dim); }
.sf-shots { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 14px; }
.sf-shots figure { margin: 0; cursor: zoom-in; }
.sf-shots img { display: block; width: 100%; aspect-ratio: 4/3; object-fit: cover; border-radius: 4px;
  border: 1px solid rgba(255,255,255,.14); transition: border-color .2s, opacity .2s; }
.sf-shots figure:hover img { border-color: rgba(255,255,255,.45); opacity: .9; }
.sf-shots figcaption { margin-top: 5px; font-family: 'IBM Plex Mono', monospace;
  font-size: 8px; line-height: 1.45; color: rgba(255,255,255,.4);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.sf-more { display: block; margin-top: 11px; cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--accent-bright); }
.sf-go {
  display: block; width: 100%; margin-top: 13px; padding: 10px 0 9px;
  border-radius: 4px; background: var(--accent-bright); color: #17130e;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; font-weight: 600;
}
.sf-go:hover { background: #ffa964; }
.sf-leader { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible;
  z-index: 7; pointer-events: none; opacity: 0; transition: opacity .4s ease; }
.sf-leader.on { opacity: 1; }
.sf-leader line { stroke: rgba(224,138,62,.5); stroke-width: 1; stroke-dasharray: 4 5; }

.sf-hud { position: absolute; left: 50%; bottom: 4.5vh; transform: translateX(-50%);
  z-index: 8; width: min(84vw, 780px); }
.sf-rail { position: relative; height: 1px; background: rgba(255,255,255,.14); }
.sf-fill { position: absolute; left: 0; top: 0; height: 1px; background: var(--accent-bright); }
.sf-tick { position: absolute; top: -3px; width: 7px; height: 7px; margin-left: -3.5px;
  border-radius: 50%; border: 1px solid rgba(255,255,255,.45); background: #0a0b0e; }
.sf-tick.done { border-color: var(--accent-bright); background: var(--accent-bright); }
.sf-legend { display: flex; align-items: center; justify-content: space-between; margin-top: 20px; }
.sf-read { font-family: 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: rgba(255,255,255,.7); }
.sf-read b { color: var(--accent-bright); font-weight: 500; }
.sf-legend button {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-dim);
  border: 1px solid rgba(255,255,255,.18); border-radius: 3px; padding: 6px 12px 5px;
  margin-left: 8px; transition: color .2s, border-color .2s;
}
.sf-legend button:hover { color: #fff; border-color: rgba(255,255,255,.4); }
.sf-scale { position: absolute; left: var(--col-left); bottom: 4.5vh; z-index: 8;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8px; letter-spacing: .16em; text-transform: uppercase; color: rgba(255,255,255,.45); }
.sf-scale i { display: block; height: 1px; background: rgba(255,255,255,.45); margin-bottom: 5px; }
.sf-credit { position: absolute; right: 2.5vw; bottom: 4.5vh; z-index: 8; text-align: right;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8px; letter-spacing: .13em; text-transform: uppercase;
  color: rgba(255,255,255,.32); line-height: 1.7; }

.sf-lightbox {
  position: fixed; inset: 0; z-index: 10; background: rgba(4,5,8,.94);
  display: flex; align-items: center; justify-content: center; padding: 6vh 7vw;
  opacity: 0; visibility: hidden; transition: opacity .3s ease, visibility 0s linear .3s;
}
.sf-lightbox.on { opacity: 1; visibility: visible; transition: opacity .3s ease, visibility 0s; }
.sf-lightbox figure { margin: 0; display: flex; flex-direction: column; align-items: center; max-height: 100%; }
.sf-lightbox img { max-width: 100%; max-height: 78vh; object-fit: contain; border-radius: 5px; }
.sf-lightbox figcaption { margin-top: 14px; font-size: 12.5px; color: rgba(255,255,255,.7); text-align: center; }
.sf-lb-nav { position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 30px; color: rgba(255,255,255,.5); }
.sf-lb-nav:hover { color: #fff; }
#sf-lb-prev { left: 2vw; } #sf-lb-next { right: 2vw; }
#sf-lb-close { position: absolute; top: 3vh; right: 3vw; font-size: 30px; color: rgba(255,255,255,.55); }
#sf-lb-count { position: absolute; bottom: 3vh; left: 50%; transform: translateX(-50%);
  font-family: 'IBM Plex Mono', monospace; font-size: 9px; letter-spacing: .2em;
  text-transform: uppercase; color: rgba(255,255,255,.45); }

/* a landing site has no drive: the scrubber, the odometer and the sol
   counter are a rover's instruments, and there is nothing for them to
   read at a spacecraft that never moved */
#surface.still .sf-hud { display: none; }

/* ---------------------------------------------- the mark at a landing site
   A rover's stops are pins on a route you are already following, so a
   9px dot is enough. A lander has one mark on a kilometre of ground, and
   the machine it points at is a few pixels wide — a dot that size is
   invisible on bright terrain. So the mark becomes a ring drawn around
   the spot rather than a dot on it, its label always showing, with a
   slow pulse going out from it to catch the eye. */
#surface.still .sf-pin i {
  position: relative;                /* the ticks and the pulse hang off it */
  width: 104px; height: 104px;
  border: 2.5px solid var(--accent-bright);
  background: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .6),
              0 0 26px rgba(240, 146, 63, .55),
              inset 0 0 26px rgba(240, 146, 63, .18);
}
/* the four ticks that turn a circle into a sight */
#surface.still .sf-pin i::before {
  content: '';
  position: absolute; inset: -15px;
  border-radius: 50%;
  background:
    linear-gradient(var(--accent-bright), var(--accent-bright)) 50% 0/2px 11px no-repeat,
    linear-gradient(var(--accent-bright), var(--accent-bright)) 50% 100%/2px 11px no-repeat,
    linear-gradient(var(--accent-bright), var(--accent-bright)) 0 50%/11px 2px no-repeat,
    linear-gradient(var(--accent-bright), var(--accent-bright)) 100% 50%/11px 2px no-repeat;
  opacity: .9;
}
#surface.still .sf-pin i::after {
  content: '';
  position: absolute; inset: -2px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  animation: sf-stand 3s ease-out infinite;
}
@keyframes sf-stand {
  0% { transform: scale(1); opacity: .85; }
  100% { transform: scale(2.1); opacity: 0; }
}
#surface.still .sf-pin b {
  left: 76px; top: 50%;
  transform: translateY(-50%);
  opacity: 1;
  font-size: 9.5px;
  color: var(--accent-bright);
  text-shadow: 0 0 12px rgba(0, 0, 0, .95), 0 0 4px rgba(0, 0, 0, .9);
}

/* the moving dot is the rover itself; nothing moves at a landing site */
#surface.still .sf-rover { display: none; }

/* The arrow marks a patch that opens something — its own mission page,
   or a grand tour's elsewhere. A patch without one has no page yet, and
   that has to be visible at rest: Pioneer 11 looked identical to its
   neighbours and swallowed the click. */
.patch.has-mission .p-role::after { content: ' →'; color: var(--accent-bright); }

/* ---------------------------------------------- flying a mission (Earth)
   The stop card of a flight: the surface traverse's card idea, sitting
   over the globe. Fixed to the right where the sky is, clear of the
   mission panel. */
.fl-card {
  /* the film panel's own setting: fixed over the globe, to the right
     of the mission column, with the stop's film or photograph large */
  position: fixed;
  top: 2.5vh; bottom: 2.5vh;
  left: calc(var(--col-left) + var(--col-w) + var(--col-gap));
  right: 2.5vw;
  background: rgba(8, 10, 14, .975);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 8px;
  padding: 18px 26px 20px;
  display: flex; flex-direction: column; gap: 12px;
  opacity: 0; visibility: hidden;
  transition: opacity .35s ease, visibility 0s linear .35s;
  pointer-events: none;
  z-index: 7;
}
.fl-card.on {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity .35s ease, visibility 0s;
}
.fl-card .mv-head { flex: none; }
.fl-card .mv-head h4 {
  font-family: inherit; font-size: 19px; font-weight: 600;
  letter-spacing: .01em; text-transform: none; color: #fff;
  display: flex; align-items: baseline; gap: 16px; min-width: 0;
}
.fl-card .fl-when {
  font-family: 'IBM Plex Mono', monospace; font-size: 10px;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent-bright); white-space: nowrap;
}
.fl-card .mv-close { background: none; border: 0; cursor: pointer; }
.fl-frame {
  /* the stage takes every pixel the story below does not need */
  position: relative; flex: 1 1 auto;
  width: 100%; min-height: 200px;
  background: #000;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 6px; overflow: hidden;
}
.fl-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 2; border: 0; cursor: pointer;
  background: rgba(6, 8, 12, .5); color: #fff;
  font-size: 40px; line-height: 1; padding: 16px 10px;
  border-radius: 6px; opacity: .6; transition: opacity .2s;
}
.fl-nav:hover { opacity: 1; }
.fl-prev { left: 10px; }
.fl-next { right: 10px; }
.fl-caprow {
  flex: none; display: flex; align-items: baseline;
  justify-content: space-between; gap: 18px;
}
.fl-count {
  /* always at the right, whether or not a caption shares the row */
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace; font-size: 10px;
  letter-spacing: .18em; color: var(--ink-faint); white-space: nowrap;
}
.fl-frame[hidden] { display: none; }
.fl-stage { position: absolute; inset: 0; }
.fl-stage video, .fl-stage img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.fl-stage img { cursor: zoom-in; }
.fl-mcap { flex: 1 1 auto; min-width: 0; font-size: 12.5px; line-height: 1.5;
           color: var(--ink-faint); margin: 0; }
.fl-mcap:empty { display: none; }
.fl-mcred {
  /* at the foot of the window, under the story, where credits belong */
  flex: none; font-family: 'IBM Plex Mono', monospace; font-size: 9.5px;
  letter-spacing: .08em; color: var(--ink-faint); margin: 14px 0 0;
}
.fl-mcred:empty { display: none; }
.fl-body { flex: 0 1 auto; min-height: 0; overflow-y: auto;
           scrollbar-width: thin; }
.fl-lead { font-weight: 600; font-size: 15px; margin: 0 0 8px; }
.fl-fact { font-size: 13.5px; line-height: 1.6; color: var(--ink-dim);
           margin: 0; max-width: 76ch; }
.fl-foot { flex: none; display: flex; justify-content: flex-end;
           padding-top: 2px; }
.fl-video { margin-bottom: 14px; }
.fl-video:empty { display: none; }
.fl-video figure { margin: 0; }
.fl-video video {
  width: 100%; display: block; border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .14); background: #000;
}
.fl-video audio { width: 100%; display: block; margin-top: 10px; }
.fl-video figure + figure { margin-top: 10px; }
.fl-video figcaption {
  font-size: 10.5px; line-height: 1.4; color: var(--ink-faint); margin-top: 5px;
}
.fl-shots { display: flex; flex-wrap: wrap; gap: 12px; margin: 16px 0 0; }
.fl-shots:empty { display: none; }
.fl-shots figure { margin: 0; width: 200px; flex: none; }
.fl-shots img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: 5px; border: 1px solid rgba(255, 255, 255, .14);
}
.fl-shots figcaption {
  font-size: 10.5px; line-height: 1.4; color: var(--ink-faint); margin-top: 5px;
}
.fl-go {
  width: auto; min-width: 260px; border: 0; border-radius: 6px;
  cursor: pointer;
  background: var(--accent); color: #17090b; font: inherit; font-size: 13px;
  font-weight: 600; letter-spacing: .06em; padding: 12px 40px;
}
.fl-go:hover { background: var(--accent-bright); }
.fl-card .mv-close:hover { color: #fff; }

/* the flight's track: a hairline from the pad, around, down to the water */
.fl-track { position: absolute; inset: 0; width: 100%; height: 100%;
            pointer-events: none; }
.fl-track .fl-halo { fill: none; stroke: rgba(6, 8, 14, .55);
                     stroke-width: 3.5; }
.fl-track .fl-line { fill: none; stroke: rgba(255, 222, 180, .9);
                     stroke-width: 1.6; }

/* a flight picture, opened: the image with its caption, arrows to browse */
.fl-shots figure { cursor: zoom-in; }
.fl-view {
  position: fixed; inset: 0; z-index: 60; background: rgba(4, 5, 9, .93);
  display: none; align-items: center; justify-content: center; gap: 6px;
}
.fl-view.on { display: flex; }
.fl-view figure { margin: 0; max-width: 84vw; text-align: center; }
.fl-view img {
  max-width: 84vw; max-height: 76vh; display: block; margin: 0 auto;
  border-radius: 6px; border: 1px solid rgba(255, 255, 255, .16);
}
.fl-view figcaption {
  font-size: 13px; line-height: 1.5; color: var(--ink-dim);
  max-width: 640px; margin: 12px auto 0;
}
.flv-x {
  position: fixed; top: 16px; right: 24px; background: none; border: 0;
  color: var(--ink-dim); font-size: 32px; cursor: pointer; line-height: 1;
}
.flv-x:hover { color: #fff; }
.flv-prev, .flv-next {
  background: none; border: 0; color: #fff; font-size: 44px; cursor: pointer;
  padding: 18px 14px; opacity: .65; line-height: 1; flex: none;
}
.flv-prev:hover, .flv-next:hover { opacity: 1; }


/* the film panel wears the flight card's furniture, so its head, frame,
   caption row, credit and foot are the same objects in both windows */
.film-panel .mv-head h4 {
  font-family: inherit; font-size: 19px; font-weight: 600;
  letter-spacing: .01em; text-transform: none; color: #fff;
}
.film-panel .fl-nav { z-index: 3; }
/* a row with nothing to show is not a door */
.tl-row.no-shot { cursor: default; }
.tl-row.no-shot:hover { background: none; }
/* a timeline that cannot be played still lights up while a flight runs */
.m-timeline:not(.playable) .tl-row { cursor: default; }
.m-timeline:not(.playable) .tl-row:hover { background: none; }
