@import url('fonts.css');

/* ==========================================================================
   Tokens. The single source of truth: app.js reads them through
   getComputedStyle, so the render palette and the interface palette cannot drift apart.
   ========================================================================== */

:root {
  /* Auditorium darkness and the plate body. Values raised after measurement: at
     0.08/0.12 the plate did not separate from the ground, and shadows are banned
     by the system, so separation rests entirely on body tone and a hairline border. */
  --ground: oklch(0.08 0.006 250);
  --plate: oklch(0.165 0.008 250);

  /* Signage light: the only accent in the system, in three brightness steps.
     Engraving was raised from 0.45 to 0.60 by measurement: switch borders and
     constellation lines are non-text elements required to understand the content,
     so they need at least 3:1. At 0.45 they measured 2.67:1 and 2.74:1. */
  --ink: oklch(0.82 0.11 65);
  --ink-secondary: oklch(0.64 0.09 65);
  --engraved: oklch(0.60 0.075 65);       /* non-text elements only */
  --engraved-text: oklch(0.64 0.09 65);   /* sky labels: a tone that passes AA */
  --rim: oklch(0.68 0.085 65);

  /* The sky: the only place where blue is permitted */
  --twilight: oklch(0.35 0.09 250 / 0.85);
  --milkyway: oklch(0.23 0.022 255);

  /* Bodies. Planet tints live in sky.js; this token is only a fallback */
  --planet: oklch(0.92 0.03 80);
  --moon-lit: oklch(0.93 0.015 85);
  --moon-dark: oklch(0.17 0.006 250);
  --sun: oklch(0.95 0.085 85);

  --brightness: 1;

  /* Semantic layer scale: there are no arbitrary 999 values in this project */
  --z-sky: 0;
  --z-dome: 10;
  --z-plate: 20;
  --z-status: 30;

  --step-1: 4px;
  --step-2: 8px;
  --step-3: 12px;
  --step-4: 18px;
  --step-5: 28px;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

/* The hidden attribute must always work. The rule [hidden]{display:none} lives
   in the browser stylesheet, and any author display declaration displaces it,
   hence !important here: without it .status{display:grid} leaves the loading
   screen hanging over a finished dome. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: 'PT Sans', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.45;
  overscroll-behavior: none;
}

body {
  overflow: hidden;
  -webkit-text-size-adjust: 100%;
}

/* ==========================================================================
   The dome: the sky always reaches the physical edges of the screen
   ========================================================================== */

#sky {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: var(--z-sky);
  touch-action: none;
  cursor: grab;
  outline: none;
}

#sky:active { cursor: grabbing; }

#sky:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -3px;
}

/* ==========================================================================
   The overview dome, a planisphere at the top edge
   ========================================================================== */

.dome {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + var(--step-3));
  left: 50%;
  translate: -50% 0;
  width: min(26vw, 116px);
  aspect-ratio: 1;
  z-index: var(--z-dome);
  touch-action: none;
  cursor: pointer;
  border: none;
  padding: 0;
  background: none;
  animation: signage-on 420ms var(--ease-out) both;
}

.dome__canvas { width: 100%; height: 100%; display: block; }

.dome__legend {
  position: absolute;
  top: 100%;
  left: 50%;
  translate: -50% 2px;
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-secondary);
  white-space: nowrap;
}

body[data-dome='collapsed'] .dome { display: none; }

.dome-restore {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + var(--step-2));
  left: 50%;
  translate: -50% 0;
  z-index: var(--z-dome);
  display: none;
}

body[data-dome='collapsed'] .dome-restore { display: block; }

/* ==========================================================================
   The plate: the only carrier of interface, at the bottom edge
   ========================================================================== */

.plate {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-plate);
  background: var(--plate);
  border-top: 1px solid var(--engraved);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  animation: signage-on 420ms 60ms var(--ease-out) both;
  /* At 200% zoom with the console open and an object selected the plate is taller
     than the screen. Without this it grows upward and carries the drive and the
     readout past the edge, where body{overflow:hidden} puts them out of reach. */
  max-height: 82vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The dome drive: the only continuous control in the whole interface */
.drive {
  position: relative;
  height: 26px;
  border: none;
  width: 100%;
  padding: 0;
  background:
    repeating-linear-gradient(to right,
      var(--engraved) 0 1px,
      transparent 1px 12px);
  background-position: 0 100%;
  background-size: 100% 7px;
  background-repeat: no-repeat;
  cursor: ew-resize;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--step-2);
}

.drive__label {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-secondary);
  pointer-events: none;
}

.drive[data-active] { background-color: color-mix(in oklab, var(--plate), var(--ink) 7%); }
.drive:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

.drift {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* The readout: what the user came for */
.readout {
  display: flex;
  flex-wrap: wrap;          /* at 360 px a three-digit azimuth plus a compass word will not fit one line */
  align-items: baseline;
  gap: var(--step-1) var(--step-3);
  padding: var(--step-2) var(--step-4) 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--ink);
}

.readout__key {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-secondary);
  margin-right: var(--step-1);
}

.readout__cardinal {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 12px;
  color: var(--ink-secondary);
  margin-left: auto;
}

/* The exhibit label */
.entry { padding: var(--step-2) var(--step-4); }

.entry__title {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-weight: 700;
  font-size: 19px;
  line-height: 1.15;
  margin: 0;
  letter-spacing: 0.01em;
}

.entry__sub {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--ink-secondary);
}

.entry__rows {
  display: grid;
  grid-template-columns: auto auto;
  gap: 1px var(--step-3);
  margin: var(--step-2) 0 0;
  font-size: 12px;
}

.entry__rows dt {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  color: var(--ink-secondary);
}

.entry__rows dd {
  margin: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.entry__hint {
  margin: var(--step-2) 0 0;
  font-size: 12px;
  color: var(--ink-secondary);
}

/* Discrete labelled switches */
.switches {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--step-1);
  padding: 0 var(--step-4) var(--step-2);
}

/* The console: opens on demand, takes no screen space at rest */
.switches--console {
  border-top: 1px solid color-mix(in oklab, var(--engraved), var(--plate) 55%);
  padding-top: var(--step-2);
  margin-top: var(--step-1);
}

.switches__toggle { margin-left: auto; }

.switch {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  background: transparent;
  border: 1px solid var(--engraved);
  border-radius: 2px;
  padding: 5px var(--step-2);
  min-height: 32px;
  cursor: pointer;
  transition: color 160ms var(--ease-out), border-color 160ms var(--ease-out);
}

a.switch { display: inline-flex; align-items: center; text-decoration: none; }
.switch:hover:not(:disabled) { color: var(--ink); border-color: var(--rim); }
.switch:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

.switch[aria-pressed='true'] {
  color: var(--ink);
  border-color: var(--rim);
  font-weight: 700;
}

.switch:disabled {
  color: var(--engraved);
  border-color: color-mix(in oklab, var(--engraved), var(--plate) 45%);
  cursor: not-allowed;
}

.nudge {
  display: flex;
  align-items: center;
  gap: var(--step-1);
  margin-left: auto;
}

.nudge__label {
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-secondary);
}

.sources {
  padding: 0 var(--step-4) var(--step-2);
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 10px;
  color: var(--ink-secondary);
}

/* ==========================================================================
   Loading and error
   ========================================================================== */

.status {
  position: fixed;
  inset: 0;
  z-index: var(--z-status);
  display: grid;
  place-content: center;
  gap: var(--step-3);
  justify-items: center;
  background: var(--ground);
  font-family: 'PT Sans Narrow', system-ui, sans-serif;
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--ink-secondary);
  text-align: center;
  padding: var(--step-5);
}

.status__line { margin: 0; max-width: 34ch; color: var(--ink); }

/* Screen reader: announcements about identified objects */
.live {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   Desktop: the sky stays full-bleed and the plate becomes a cartouche
   ========================================================================== */

@media (min-width: 780px) {
  .plate {
    inset: auto auto var(--step-4) var(--step-4);
    width: min(370px, 34vw);
    border: 1px solid var(--engraved);
    border-radius: 2px;
  }

  .dome {
    top: var(--step-4);
    left: auto;
    right: var(--step-4);
    translate: none;
    width: 148px;
  }

  .dome-restore { left: auto; right: var(--step-4); translate: none; }
}

/* ==========================================================================
   Motion: one authored moment, the signage coming on. Nothing else.
   ========================================================================== */

@keyframes signage-on {
  from { opacity: 0.45; translate: 0 5px; }
  to { opacity: 1; translate: 0 0; }
}

.dome { animation-name: signage-on; }

@media (prefers-reduced-motion: reduce) {
  .dome, .plate { animation: none; }
  .switch { transition: none; }
}

/* On desktop the overview dome moves right, so its legend follows */
@media (min-width: 780px) {
  .dome__legend { white-space: nowrap; }
}
