@import url("./fonts/fonts.css");

/* The hidden attribute has to win. Any author `display` beats the UA sheet's
   `display: none`, so `.chrome { display: grid }` kept the whole interface on
   screen behind the boot overlay, and `.coords { display: flex }` kept a row of
   placeholder dashes visible that nothing had filled in yet. One rule here
   rather than a `[hidden]` companion beside every component that grows a
   display of its own. */
[hidden] {
  display: none !important;
}

/* ============================================================================
   Tokens. Every colour, every measure, defined once here. The renderer reads
   these same custom properties at boot through readPalette(), so a shader and a
   panel border can never disagree about what --ember means.
   ========================================================================= */

:root {
  /* Ground is biased cool so the ember reads warmer than it measures. */
  --ink: #08090b;
  --ink-raised: #101116;
  --hairline: #22242b;

  /* Warm is the physical Earth. */
  --ember: #f6821f;
  --ember-deep: #8a3b08;
  --ember-bright: #ffb865;

  /* Cool is data in flight, and is used nowhere else. */
  --signal: #7dd3e0;

  --text: #c8c3bb;
  --text-dim: #7c7770;

  --panel: rgb(16 17 22 / 0.72);
  --panel-solid: #0d0e12;

  --display: "Archivo Variable", "Archivo", system-ui, sans-serif;
  --body: "Public Sans", system-ui, -apple-system, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  --step-0: 0.8125rem;
  --step-1: 0.9375rem;
  --step-2: 1.25rem;
  --step-3: 1.75rem;
  --eyebrow: 0.6875rem;

  --gap: 0.75rem;
  --pad: 0.875rem;
  --rail: 19rem;
  --radius: 2px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  color-scheme: dark;
}

/* This page is one committed visual world: an instrument lit in a dark room.
   It renders the same on either host theme rather than inverting into a light
   variant that would have nothing to do with the subject. */

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

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--step-1);
  line-height: 1.5;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
}

button,
input,
select {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 1px solid var(--ember);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ============================================================================
   Stage
   ========================================================================= */

.stage {
  position: fixed;
  inset: 0;
  /* Explicit single track, not the default auto row. An auto row is sized by
     its content, and the content is a canvas whose intrinsic height comes from
     its own height attribute, so `height: 100%` below resolved against a row
     that resolved against the canvas. That circular dependency left the backing
     store at one size and the element at another, which stretched the sphere
     into an ellipse on portrait viewports. */
  display: grid;
  grid-template: 100% / 100%;
}

.stage > *,
.stage::after {
  /* The vignette is a grid item too. Without this it opens a second row and
     pushes the canvas up. */
  grid-area: 1 / 1;
}

.globe-canvas,
.poster-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: grab;
}

.stage[data-dragging="true"] .globe-canvas {
  cursor: grabbing;
}

/* A faint vignette pulls the eye to the globe and keeps panel text off the
   brightest part of the limb. */
.stage::after {
  content: "";
  pointer-events: none;
  background: radial-gradient(
    120% 90% at 50% 45%,
    transparent 45%,
    rgb(4 5 7 / 0.55) 100%
  );
}

/* ============================================================================
   Chrome. Everything floats over the canvas on a dashed hairline grid.
   ========================================================================= */

.chrome {
  position: fixed;
  inset: 0;
  pointer-events: none;
  display: grid;
  grid-template-columns: var(--rail) 1fr auto;
  grid-template-rows: auto 1fr auto;
  gap: var(--gap);
  padding: clamp(0.75rem, 2vw, 1.5rem);
}

.chrome > * {
  pointer-events: auto;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
}

.eyebrow {
  font-family: var(--display);
  font-size: var(--eyebrow);
  font-weight: 600;
  font-stretch: 118%;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0;
}

/* ---- lockup ---- */

.lockup {
  grid-column: 1;
  grid-row: 1;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  background: none;
  border: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.lockup h1 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step-3);
  font-weight: 700;
  font-stretch: 125%;
  letter-spacing: 0.06em;
  line-height: 1;
  color: var(--text);
}

.lockup .mark {
  color: var(--ember);
}

.lockup p {
  margin: 0;
  max-width: 22ch;
  font-size: var(--step-0);
  color: var(--text-dim);
}

/* ---- search ---- */

.finder {
  grid-column: 3;
  grid-row: 1;
  align-self: start;
  justify-self: end;
  position: relative;
  width: min(20rem, 42vw);
}

.finder input {
  width: 100%;
  padding: 0.5rem 0.7rem;
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  font-family: var(--mono);
  font-size: var(--step-0);
}

.finder input::placeholder {
  color: var(--text-dim);
}

.finder ul {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 15rem;
  overflow-y: auto;
  background: var(--panel-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
}

.finder ul:empty {
  display: none;
}

.finder li button {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding: 0.4rem 0.7rem;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  text-align: left;
  font-size: var(--step-0);
  cursor: pointer;
}

.finder li:last-child button {
  border-bottom: 0;
}

.finder li button:hover,
.finder li button[aria-selected="true"] {
  background: rgb(246 130 31 / 0.12);
  color: var(--ember-bright);
}

.finder li button span:last-child {
  font-family: var(--mono);
  color: var(--text-dim);
}

/* ---- readout rail ---- */

.readout {
  grid-column: 1;
  grid-row: 2;
  align-self: end;
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: 100%;
  overflow-y: auto;
}

.readout[data-empty="true"] .facts,
.readout[data-empty="true"] .neighbours {
  display: none;
}

.readout h2 {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step-2);
  font-weight: 600;
  font-stretch: 112%;
  line-height: 1.1;
  letter-spacing: 0.01em;
}

.readout .official {
  margin: 0;
  font-size: var(--step-0);
  color: var(--text-dim);
}

.coords {
  display: flex;
  gap: 0.75rem;
  font-family: var(--mono);
  font-size: var(--step-0);
  font-variant-numeric: tabular-nums;
  color: var(--ember);
  border-top: 1px dashed var(--hairline);
  padding-top: 0.5rem;
}

.facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.75rem;
  font-size: var(--step-0);
}

.facts dt {
  color: var(--text-dim);
  font-family: var(--display);
  font-size: var(--eyebrow);
  font-stretch: 112%;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding-top: 0.15em;
}

.facts dd {
  margin: 0;
}

.facts dd.num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.neighbours {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  border-top: 1px dashed var(--hairline);
  padding-top: 0.5rem;
}

.neighbours button {
  padding: 0.15rem 0.4rem;
  background: none;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: var(--eyebrow);
  letter-spacing: 0.04em;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}

.neighbours button:hover {
  color: var(--ember-bright);
  border-color: var(--ember-deep);
}

/* ---- telemetry callouts ---- */

.callouts {
  grid-column: 3;
  grid-row: 2;
  align-self: end;
  justify-self: end;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: none;
  border: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  text-align: right;
}

.callout {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px dashed var(--hairline);
}

.callout .value {
  font-family: var(--mono);
  font-size: var(--step-2);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--ember);
}

.callout .value[data-cool="true"] {
  color: var(--signal);
}

.callout .label {
  font-family: var(--display);
  font-size: var(--eyebrow);
  font-stretch: 112%;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* ---- control strip ---- */

.controls {
  grid-column: 1 / -1;
  grid-row: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.5rem var(--pad);
}

.toggles {
  display: flex;
  gap: 0.35rem;
}

.toggle {
  padding: 0.3rem 0.6rem;
  background: none;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  font-family: var(--display);
  font-size: var(--eyebrow);
  font-stretch: 112%;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease),
    background-color 0.15s var(--ease);
}

.toggle[aria-pressed="true"] {
  color: var(--ember-bright);
  border-color: var(--ember-deep);
  background: rgb(246 130 31 / 0.1);
}

.toggle[aria-pressed="true"][data-layer="network"] {
  color: var(--signal);
  border-color: rgb(125 211 224 / 0.4);
  background: rgb(125 211 224 / 0.08);
}

.slider {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.slider label {
  font-family: var(--display);
  font-size: var(--eyebrow);
  font-stretch: 112%;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.slider input {
  width: 8rem;
  accent-color: var(--ember);
}

.status {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--mono);
  font-size: var(--eyebrow);
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.status .api {
  padding: 0.15rem 0.4rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.status .api[data-api="webgpu"] {
  color: var(--signal);
  border-color: rgb(125 211 224 / 0.35);
}

/* ---- boot and fallback ---- */

.boot {
  position: fixed;
  inset: 0;
  display: grid;
  place-content: center;
  gap: 0.5rem;
  justify-items: center;
  background: var(--ink);
  text-align: center;
  transition: opacity 0.5s var(--ease);
}

.boot.done {
  opacity: 0;
  pointer-events: none;
}

.boot .bar {
  width: 12rem;
  height: 1px;
  background: var(--hairline);
  position: relative;
  overflow: hidden;
}

.boot .bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ember);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
  transition: transform 0.3s var(--ease);
}

.notice {
  grid-column: 1 / -1;
  grid-row: 2;
  align-self: start;
  justify-self: center;
  padding: 0.4rem 0.7rem;
  font-size: var(--step-0);
  color: var(--text-dim);
  max-width: 42ch;
  text-align: center;
}

/* The no-JavaScript message lives outside the chrome grid, so it places
   itself. Kept as a class rather than a style attribute because the CSP
   allows no inline styles at all. */
.notice-fixed {
  position: fixed;
  inset: auto 1rem 1rem;
}

/* ============================================================================
   Narrow viewports: the globe keeps the whole screen and the chrome becomes a
   sheet along the bottom edge.
   ========================================================================= */

@media (max-width: 900px) {
  .chrome {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto auto;
    padding: 0.6rem;
    gap: 0.5rem;
  }

  .lockup,
  .finder,
  .callouts,
  .readout,
  .controls {
    grid-column: 1;
    justify-self: stretch;
  }

  .lockup {
    grid-row: 1;
  }

  .lockup h1 {
    font-size: var(--step-2);
  }

  .lockup p {
    display: none;
  }

  .finder {
    grid-row: 1;
    justify-self: end;
    width: 10rem;
  }

  .callouts {
    grid-row: 2;
    align-self: start;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.75rem;
  }

  .callout {
    border-bottom: 0;
    padding-bottom: 0;
    flex-direction: column;
    align-items: flex-end;
    gap: 0;
  }

  .callout .value {
    font-size: var(--step-1);
  }

  .readout {
    grid-row: 3;
    align-self: end;
    max-height: 40vh;
  }

  .controls {
    grid-row: 4;
    justify-content: space-between;
  }

  .status {
    width: 100%;
    margin-left: 0;
  }

  .slider input {
    width: 5.5rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
