/* OrbitLab /galaxy: the Sun's place in the Milky Way as an instrument.
 *
 * The orrery's shell, kept to the letter: a bar that names the tool, a 320px
 * rail of controls divided by hairlines, and an aperture that gives the
 * figure everything that is left. No boxes inside the shell; radius, border
 * and shadow are kept for what floats above it. Labels are sentence case in
 * the body face; the mono face is spent only on measured quantities and the
 * one-line figure caption.
 *
 * Colour: rose marks the current view (its rail button, a chosen name on the
 * stage) and teal carries measured information. Rose and teal are also two
 * of the stage's false colours, so a layer's row says which band it stands
 * for. The stage is a view OF space and paints its own night in both
 * themes. */

.layout {
  --rail-w: 320px;
  --rail-pad: 18px;
  --hair: var(--rule);
  --galaxy-space: #07060f;
  --galaxy-ink: #eceaff;
  --galaxy-dim: #9390b5;
  --galaxy-rose: #ff5c86;
  --galaxy-teal: #46e0d5;
  --galaxy-gold: #e6c25a;
  --galaxy-violet: #9b86ff;
}
.topbar { --hair: var(--rule); }

html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--app-bg);
  color: var(--text);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The bar is the site's instrument bar: its lockup, links, controls and both
   of its layouts are in theme.css. */

/* ---------------- the layout ---------------- */
.layout { flex: 1; display: flex; min-height: 0; }

/* ---------------- the rail ---------------- */
.rail {
  width: var(--rail-w); flex: none;
  display: flex; flex-direction: column; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  border-inline-end: 1px solid var(--hair);
  background: var(--panel);
}
.group { padding: var(--s-5) var(--rail-pad) var(--s-5); border-block-end: 1px solid var(--hair); }
.group:last-child { border-block-end: none; }
.group-title { margin: 0 0 var(--s-4); font-size: var(--fs-c-h3); font-weight: 600; line-height: 1.3; color: var(--text); }

.field { display: flex; flex-direction: column; gap: var(--s-2); margin: 0 0 var(--s-4); font-size: 12.5px; color: var(--text-soft); }
.field input, .field select {
  box-sizing: border-box; width: 100%; min-height: 36px; padding: var(--s-3) var(--s-3);
  background: var(--app-bg); color: var(--text);
  border: 1px solid var(--hair); border-radius: var(--r-xs);
  font-family: inherit; font-size: 13px;
  transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
}
.field input[type="date"] { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.field input:hover, .field select:hover { border-color: var(--muted); }
.field input:focus-visible, .field select:focus-visible { outline: 2px solid var(--focus); box-shadow: var(--ring); outline-offset: 1px; }
.field select { cursor: pointer; }
.field[hidden] { display: none; }
.clock-row { display: flex; gap: var(--s-3); margin: 0 0 var(--s-4); }
.clock-row .btn { flex: 1 1 0; justify-content: center; min-height: 40px; padding: var(--s-3) var(--s-4); font-size: 14px; }
.field:last-child { margin-bottom: 0; }

/* The four views: one row each, the current one carrying the house inset
   rule in rose, as the orrery's chosen world does. */
.goto-row { display: flex; flex-direction: column; margin: 0 calc(-1 * var(--rail-pad)); }
.goto {
  display: flex; align-items: center; width: 100%;
  min-height: 36px; padding: var(--s-2) var(--rail-pad);
  background: transparent; border: 0; border-inline-start: 3px solid transparent;
  font-family: inherit; font-size: 13.5px; color: var(--text-soft); text-align: start; cursor: pointer;
  transition: color var(--t-fast) ease, background var(--t-fast) ease;
}
.goto:hover { color: var(--text); background: var(--panel-2); }
.goto:focus-visible { outline: 2px solid var(--focus); box-shadow: var(--ring); outline-offset: -3px; }
.goto[aria-pressed="true"] { color: var(--text); font-weight: 700; border-inline-start-color: var(--accent); }

.check { display: flex; align-items: center; gap: var(--s-3); margin: 0 0 var(--s-3); font-size: 13px; color: var(--text); cursor: pointer; }
.check:last-child { margin-bottom: 0; }
.check input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--accent); }
.check input:focus-visible { outline: 2px solid var(--focus); box-shadow: var(--ring); outline-offset: 2px; }
/* Each layer's swatch is the colour the stage draws it in: data, so the
   stage's own inks, not the theme's. */
.layer-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.layer-dot--stars { background: var(--galaxy-ink); }
.layer-dot--fermi { background: var(--galaxy-rose); }
.layer-dot--erosita { background: var(--galaxy-teal); }
.layer-dot--sun { background: var(--galaxy-gold); }
.layer-dot--heliosphere { background: var(--galaxy-violet); }
.layer-dot--trails { background: var(--galaxy-dim); }
.layer-dot--droplines { background: transparent; border: 1px dashed var(--galaxy-dim); box-sizing: border-box; }
.layer-dot--none { background: transparent; border: 1px solid var(--border); box-sizing: border-box; }
.layer-sub { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); }

/* The Layers group sits in a <dialog>. On a desk it stays open and is reset
   to a plain block in the rail; the sheet chrome exists only on a phone. (No
   display on .sheet itself: an author display would override the user
   agent's display: none for a closed dialog.) */
.sheet { position: static; margin: 0; padding: 0; border: 0; width: auto; max-width: none; max-height: none; background: transparent; color: inherit; }
.sheet:not([open]) { display: none; }
.sheet-head, .sheet-open { display: none; }

/* ---------------- the aperture ---------------- */
.aperture { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.stage-head {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: var(--s-5);
  padding: 0 var(--s-5); border-block-end: 1px solid var(--hair); background: var(--panel);
}
.tabs { position: relative; display: flex; gap: 0; } /* relative: the sliding marker is placed in it */
.tab {
  position: relative; background: transparent; border: 0; color: var(--muted);
  padding: var(--s-5) 0 var(--s-4); margin-inline-end: var(--s-6);
  font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;
  transition: color var(--t-fast) ease;
}
.tab:last-child { margin-inline-end: 0; }
.tab::after {
  content: ""; position: absolute; inset-inline: 0; bottom: -1px; height: 2px;
  background: var(--spectral); opacity: 0; transition: opacity var(--t-fast) ease;
}
.tab:hover { color: var(--text-soft); }
.tab.active { color: var(--text); font-weight: 700; }
.tab.active::after { opacity: 1; }
.tab:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; box-shadow: var(--ring); }
/* The clock the model is drawn for: a measured quantity, so it is set like one. */
.clock { margin: 0; font-family: var(--font-mono); font-size: 14px; font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; }
.clock b { font-weight: 500; }
.clock span { margin-inline-start: var(--s-4); font-size: 12px; color: var(--muted); }
.clock span[hidden] { display: none; }

.view { display: none; flex: 1; min-height: 0; flex-direction: column; }
.view.active { display: flex; }
.view:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; }

/* ---------------- the stage ---------------- */
.stage {
  position: relative; flex: 1; min-height: 0;
  background: var(--galaxy-space); overflow: hidden;
  touch-action: none; cursor: grab;
}
.stage:active { cursor: grabbing; }
.stage:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; box-shadow: var(--ring); }
.stage canvas { display: block; width: 100%; height: 100%; }
/* Without WebGL the 2D cross-section takes the stage: the same model drawn
   flat, scrolling within the box when its panel runs long. */
.stage-fallback { position: absolute; inset: 0; margin: 0; overflow: auto; overscroll-behavior: contain; }
.stage.is-live .stage-fallback { display: none; }
.stage-fallback .viz { border: 0; border-radius: 0; }
.stage-fallback .viz-fallback { margin: 0; padding: var(--s-6); color: var(--galaxy-dim); font-size: 14px; line-height: 1.6; text-align: center; }
.stage-fallback .viz.scene-live .viz-fallback { display: none; }

/* Marks sit on the things the labels name: a gold ring on the Sun, a dot on
   the centre. Labels float beside them and are the keyboard route into the
   model: each is a button that goes to its subject. */
.stage-labels { position: absolute; inset: 0; pointer-events: none; }
.galaxy-mark {
  position: absolute; inset-inline-start: 0; inset-block-start: 0;
  width: 6px; height: 6px; border-radius: 50%; transform: translate(-50%, -50%);
  background: var(--galaxy-ink); pointer-events: none;
}
html[dir="rtl"] .galaxy-mark { transform: translate(50%, -50%); }
.galaxy-mark--sun {
  width: 12px; height: 12px; box-sizing: border-box;
  background: var(--galaxy-gold); border: 3px solid var(--galaxy-space); box-shadow: 0 0 0 1.5px var(--galaxy-gold);
}
/* A planet is a dot in its own tint (data, so a literal set by galaxy.js). */
.galaxy-mark--planet { width: 7px; height: 7px; background: var(--dot); box-shadow: 0 0 0 1px var(--galaxy-space); }
.galaxy-mark[hidden] { display: none; }
.galaxy-label {
  position: absolute; inset-inline-start: 0; inset-block-start: 0;
  transform: translate(-50%, -100%); pointer-events: auto; cursor: pointer;
  margin: 0; padding: var(--s-1) var(--s-3) 0; background: transparent; border: 0;
  font-family: var(--font-body); font-size: 12px; font-weight: 500; line-height: 1.2;
  color: var(--galaxy-dim); white-space: nowrap; text-align: center;
  /* A 2px halo in the space colour keeps a name legible over the point
     cloud: a legibility device, not a glow. */
  text-shadow: 0 0 2px var(--galaxy-space), 0 0 2px var(--galaxy-space), 0 0 3px var(--galaxy-space);
  transition: color var(--t-fast) ease;
}
html[dir="rtl"] .galaxy-label { transform: translate(50%, -100%); }
.galaxy-label small { display: block; font-family: var(--font-mono); font-size: 10.5px; font-weight: 400; font-variant-numeric: tabular-nums; margin-top: var(--s-1); opacity: 0.85; }
.galaxy-label::after { content: ""; display: block; width: 1px; height: 9px; margin: var(--s-2) auto 0; background: currentColor; opacity: 0.55; }
.galaxy-label.is-below { transform: translate(-50%, 0); padding: 0 var(--s-3) var(--s-1); }
html[dir="rtl"] .galaxy-label.is-below { transform: translate(50%, 0); }
.galaxy-label.is-below::after { display: none; }
.galaxy-label.is-below::before { content: ""; display: block; width: 1px; height: 9px; margin: 0 auto var(--s-2); background: currentColor; opacity: 0.55; }
.galaxy-label.is-end { transform: translate(0, -50%); padding: 0 var(--s-2); text-align: start; }
.galaxy-label.is-start { transform: translate(-100%, -50%); padding: 0 var(--s-2); text-align: end; }
html[dir="rtl"] .galaxy-label.is-start { transform: translate(100%, -50%); }
.galaxy-label.is-end::after, .galaxy-label.is-start::after { display: none; }
.galaxy-label:hover { color: var(--galaxy-ink); }
.galaxy-label:focus-visible { outline: 2px solid var(--focus); box-shadow: var(--ring); border-radius: var(--r-xs); }
.galaxy-label.is-selected { color: var(--galaxy-ink); font-weight: 700; }
/* A bubble's name is set in its false colour, the one place a label is coloured. */
.galaxy-label.is-fermi { color: var(--galaxy-rose); }
.galaxy-label.is-erosita { color: var(--galaxy-teal); }
.galaxy-label.is-heliosphere { color: var(--galaxy-violet); }
.galaxy-label[hidden] { display: none; }

/* The tour: its stop, one sentence and three buttons, between the stage
   and the reading. The stop count is a measured thing and set like one. */
.tour-start { width: 100%; justify-content: center; margin-top: var(--s-4); min-height: 40px; }
.tour { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5); padding: var(--s-4) var(--s-5) 0; }
.tour[hidden] { display: none; }
.tour-text { flex: 1 1 320px; margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--text-soft); }
.tour-text b { font-family: var(--font-mono); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--accent-2); margin-inline-end: var(--s-3); }
.tour-nav { display: flex; gap: var(--s-3); }
.tour-nav .btn { min-height: 36px; padding: var(--s-2) var(--s-4); font-size: 13.5px; }

/* The close-up of Sagittarius A*: a second frame at its own scale in the
   stage's top end corner, a hairline on the night, no glass. */
.inset {
  position: absolute; inset-inline-end: var(--s-4); inset-block-start: var(--s-4); z-index: 2;
  width: 200px; margin: 0; padding: var(--s-3); box-sizing: border-box;
  border: 1px solid var(--galaxy-dim); background: var(--galaxy-space); color: var(--galaxy-ink);
}
.inset[hidden] { display: none; }
.inset svg { display: block; width: 100%; height: auto; }
.inset figcaption { margin: var(--s-2) 0 0; font-family: var(--font-mono); font-size: 10.5px; line-height: 1.5; color: var(--galaxy-dim); }
.inset-horizon { fill: var(--galaxy-space); stroke: var(--galaxy-dim); stroke-width: 0.6; }
.inset-ring { fill: none; stroke: var(--galaxy-gold); stroke-width: 3; opacity: 0.9; }
.inset-ring-soft { fill: none; stroke: var(--galaxy-gold); stroke-width: 7; opacity: 0.22; } /* the ring's brightness falls off: a drawing of what the EHT sees, not chrome */
.inset-orbit { fill: none; stroke: var(--galaxy-dim); stroke-width: 0.8; stroke-dasharray: 3 3; }
.inset-text { fill: var(--galaxy-dim); font-family: var(--font-mono); font-size: 9.5px; }
.inset-bar { stroke: var(--galaxy-dim); stroke-width: 1; }

/* A word from the clock: the dated clock has reached its end, or has no
   positions far enough back for a trail. Text, between the reading and the
   caption; never a box. */
.stage-note { flex: none; margin: 0; padding: var(--s-2) var(--s-5) 0; font-size: 12.5px; line-height: 1.5; color: var(--text-soft); }
.stage-note[hidden] { display: none; }

/* The reading line under the figure: what the current view shows, as
   label-and-value pairs in one wrapping line. Values are teal mono. */
.reading {
  flex: none; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-3) var(--s-6);
  padding: var(--s-4) var(--s-5) var(--s-1); min-height: 24px; font-size: 13px; color: var(--text-soft);
}
.reading:empty { display: none; }
.reading .who { font-weight: 700; color: var(--text); }
.rd { display: inline; }
.rd-k { margin-inline-end: var(--s-3); }
/* A value may wrap at the dot between its two readings; a number and its
   unit are joined by a no-break space in galaxy.js, so they never part. */
.rd-v { font-family: var(--font-mono); font-size: 13.5px; font-variant-numeric: tabular-nums; color: var(--accent-2); margin-inline-end: var(--s-3); }
.rd-s { font-family: var(--font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--muted); }

/* The caption a figure in a paper carries: what is schematic, what the
   colours mean, and how to drive it. Mono, on the hairline. */
.caption {
  flex: none; margin: var(--s-4) var(--s-5) var(--s-4); padding-block-start: var(--s-3);
  border-block-start: 1px solid var(--hair);
  font-family: var(--font-mono); font-size: 11px; line-height: 1.6; letter-spacing: 0.01em; color: var(--muted);
}
html[lang="ar"] .caption { letter-spacing: 0; word-spacing: 0.06em; line-height: 1.8; }

/* ---------------- readings ---------------- */
.table-wrap { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 0 var(--s-5); }
.positions { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.positions caption {
  text-align: start; caption-side: top; padding: var(--s-5) 0 var(--s-4);
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6; color: var(--muted);
}
.positions th, .positions td { padding: var(--s-3) var(--s-4); border-block-end: 1px solid var(--hair); text-align: start; vertical-align: top; }
.positions th { position: sticky; top: 0; background: var(--app-bg); font-size: 12.5px; font-weight: 600; color: var(--text-soft); border-block-end: 1px solid var(--border); }
.positions th.num, .positions td.num { text-align: end; }
.positions td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--accent-2); white-space: nowrap; }
.positions td.kind { color: var(--muted); white-space: nowrap; }
.positions td a { color: var(--accent-2); text-decoration: none; border-block-end: 1px solid transparent; }
.positions td a:hover { border-block-end-color: currentColor; }
.positions .note { display: block; margin-top: var(--s-1); font-size: 12px; color: var(--muted); }

/* ---------------- sources ---------------- */
.sources { flex: 1; min-height: 0; overflow: auto; padding: var(--s-5) var(--s-5); }
.sources-lead { margin: 0 0 var(--s-5); max-width: 60ch; line-height: 1.6; color: var(--text-soft); }
.sources-list { list-style: none; margin: 0; padding: 0; max-width: 640px; }
.sources-list li { padding: var(--s-3) 0; border-block-end: 1px solid var(--hair); font-size: 13.5px; }
.sources-list a { color: var(--accent-2); text-decoration: none; border-block-end: 1px solid transparent; }
.sources-list a:hover { border-block-end-color: currentColor; }
.sources-list .for { display: block; margin-top: var(--s-1); font-size: 12px; color: var(--muted); }

/* ---------------- the bar below the desk ---------------- */
@media (max-width: 980px) {
  /* Two rows: the wordmark and the reading controls, then the destinations;
     the name keeps its own width and may break between the two words. */
}

/* ---------------- phones and touch ---------------- */
@media (max-width: 768px) {
  /* Release the viewport lock: the page scrolls, the model comes first. */
  body { height: auto; min-height: 100vh; min-height: 100dvh; overflow: visible; }
  .topbar { position: sticky; top: 0; z-index: 30; }

  .layout { flex-direction: column; }
  .aperture { order: 1; min-height: 0; }
  .rail { order: 2; width: 100%; overflow: visible; border-inline-end: 0; border-block-start: 1px solid var(--hair); }
  .view { flex: none; }
  .stage { aspect-ratio: 16 / 10; min-height: 280px; max-height: 70vh; }
  .positions th { position: static; }
  /* The quantity's name stays in view while its value scrolls. */
  .positions th:first-child, .positions td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1; background: var(--app-bg);
  }
}
@media (max-width: 560px) {
  .stage { aspect-ratio: 4 / 5; max-height: 76vh; }
  .inset { width: 160px; }
  .tour-nav { flex: 1 1 100%; }
  .tour-nav .btn { flex: 1 1 0; justify-content: center; min-height: 44px; }
  .stage-head { flex-wrap: wrap; row-gap: 0; padding-bottom: var(--s-3); }
  .clock { flex: 1 1 100%; }
  .tab { padding: var(--s-4) 0 var(--s-4); margin-inline-end: var(--s-5); }

  /* The Time group is a bar fixed to the bottom edge, in thumb reach of the
     stage: the clock, Play and Reset on one row, the speed and the Controls
     opener on the next, the date field across when the dated clock is on.
     Its controls name themselves, so the title and field captions go to the
     accessibility tree only. The page leaves room for it at the foot. */
  .group--time {
    position: fixed; inset-inline: 0; bottom: 0; z-index: 30;
    display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: var(--s-3); align-items: center;
    padding: var(--s-3) var(--s-4) calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
    background: var(--app-bg); border-block-start: 1px solid var(--hair); border-block-end: 0;
  }
  .group--time .group-title, .group--time .field > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .group--time .field { margin: 0; min-width: 0; }
  .group--time .clock-row { display: contents; }
  .group--time .clock-row .btn { flex: none; }
  .group--time [data-galaxy-date-field] { grid-column: 1 / -1; }
  .group--time .sheet-open { display: inline-flex; grid-column: 2 / -1; justify-content: center; }
  .layout { padding-block-end: calc(3 * 44px + 4 * var(--s-3) + env(safe-area-inset-bottom, 0px)); }

  /* Open, the dialog is a sheet from the bottom edge over a dimmed stage. */
  .sheet[open] {
    position: fixed; inset-inline: 0; bottom: 0; top: auto; z-index: 40;
    width: auto; max-width: none; max-height: 72vh; overflow: auto; overscroll-behavior: contain;
    padding-block-end: env(safe-area-inset-bottom, 0px);
    background: var(--panel); border-block-start: 1px solid var(--hair);
    border-start-start-radius: var(--r-lg); border-start-end-radius: var(--r-lg);
  }
  .sheet::backdrop { background: var(--app-bg); background: color-mix(in srgb, var(--app-bg) 65%, transparent); }
  .sheet-head { display: flex; justify-content: flex-end; padding: var(--s-3) var(--rail-pad) 0; }
  .sheet .group:last-child { padding-block-end: var(--s-6); }
}
@media (pointer: coarse) {
  .field input, .field select { min-height: 44px; font-size: 16px; }
  .clock-row .btn { min-height: 44px; }
  .goto { min-height: 44px; }
  .tour-start, .tour-nav .btn { min-height: 44px; }
  .check { min-height: 44px; margin-bottom: 0; }
  .tab { min-height: 44px; }
  .tab { min-width: 44px; }
  .icon-btn { min-width: 44px; min-height: 44px; }
  /* A name's label is a control over the stage. Its hit area grows through
     a pseudo-element so the box the label layout measures stays the text's. */
  .galaxy-label { padding: var(--s-3) var(--s-3) 0; }
  .galaxy-label::before { content: ""; position: absolute; inset: -12px -6px; }
}
@media (prefers-reduced-motion: reduce) {
  .galaxy-label, .tab, .goto { transition: none; }
}
@media print {
  .topbar, .rail, .stage, .caption .hint, .tab { display: none; }
  .view, .view.active { display: block; }
  .positions td.num { color: inherit; }
}
