/* Night sky throughout: the subject is nocturnal migration, and a dark ground
   is also what lets the altitude colours carry meaning. */

:root {
  --ink: #070b18;
  --ink-panel: rgba(10, 16, 33, 0.72);
  --line: rgba(150, 180, 220, 0.16);
  --text: #dfe8f6;
  --text-dim: #93a6c4;
  --accent: #4cc9f0;
  --on: #5ad2b0;
  --off: #ff9e4a;
  --radius: 10px;
  --pad: 14px;
  /* Measured from the real controls bar, which wraps to two rows on narrow
     windows. A fixed offset here was letting the panels slide under it. */
  --controls-h: 60px;
}

* { box-sizing: border-box; }

/* Panels use display:flex, which would otherwise defeat the hidden attribute. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* -- stage ------------------------------------------------------------------ */

.stage {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 520px;
  overflow: hidden;
  /* Set from the computed sun elevation; --ink until the first frame. */
  background: var(--sky, var(--ink));
  transition: background-color 900ms linear;
}

.sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
#sky-bg { z-index: 1; }
/* All three layers are transparent. The sky colour is painted by .stage
   underneath them, so it can change with the sun without the canvases having
   to know about it. */
#sky-fg { z-index: 2; }
/* Heights live on their own layer: the trail layer is faded rather than
   cleared each frame, which would smear the digits into ghosts. */
#sky-labels { z-index: 3; pointer-events: none; }

.panel {
  position: absolute;
  z-index: 4;
  padding: var(--pad);
  background: var(--ink-panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
  max-width: min(34vw, 320px);
}
.panel--tl { top: 16px; left: 16px; }
.panel--bl { bottom: calc(var(--controls-h) + 28px); left: 16px; }

/* The right-hand column.
   Previously the summaries panel was anchored to the bottom, grew upward and
   was kept off the statistics panel by a hand-computed max-height. That number
   could not survive a different font size, zoom level or viewport, and when it
   was wrong the panel silently clipped its last element.
   The rail spans the space instead: the statistics take their natural height,
   the summaries take whatever is left, and the charts inside flex to fit. No
   arithmetic to get wrong. */
.rail {
  position: absolute;
  z-index: 4;
  top: 16px;
  right: 16px;
  bottom: calc(var(--controls-h) + 28px);
  width: 260px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* So the gap between the panels does not swallow clicks on the sky. */
  pointer-events: none;
}
.rail > .panel {
  position: static;
  max-width: none;
  pointer-events: auto;
}

.panel--stats {
  flex: 0 0 auto;
  text-align: right;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 14px;
}
/* The headline rate keeps a full row; the rest pair up. */
.panel--stats > .stat:first-child { grid-column: 1 / -1; }

.panel--summaries {
  flex: 1 1 auto;
  /* Without this a flex item refuses to shrink below its content. */
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

h1 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent);
}
.site {
  margin: 3px 0 0;
  font-size: 19px;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--text);
}
h2 {
  flex: 0 0 auto;
  margin: 0 0 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-dim);
}
.mt { margin-top: 10px; }
.sub { margin: 2px 0 0; color: var(--text-dim); font-size: 12px; }

.status { margin: 7px 0 0; font-size: 12px; color: var(--text-dim); }
.dot {
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--text-dim);
  vertical-align: middle;
}
.dot--on { background: var(--on); box-shadow: 0 0 8px var(--on); }
.dot--off { background: var(--off); }

.alarms {
  margin: 6px 0 0;
  font-size: 11px;
  color: var(--off);
  line-height: 1.35;
}

/* -- stats ------------------------------------------------------------------ */

.stat { margin-bottom: 0; }
.stat__value {
  display: block;
  font-size: 26px;
  font-weight: 650;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.stat__label {
  display: block;
  font-size: 11px;
  color: var(--text-dim);
}
.stat--small .stat__value { font-size: 16px; color: var(--text); }

/* -- embed mode ------------------------------------------------------------- */

/* The app alone: the explanation belongs to the hosting page. */
.embed .about { display: none; }

/* Above the stacking breakpoint the panels overlay the sky, so the app is
   exactly one frame tall and there is nothing to scroll to -- pin it.
   Below it the panels move under the sky and the document is far taller than
   any sensible iframe, so the frame scrolls internally instead. That is not
   lovely, but hiding the overflow there would clip the controls off the
   bottom of the frame with no way to reach them. */
@media (min-width: 861px) {
  .embed, .embed body { height: 100%; overflow: hidden; }
  .embed .stage { height: 100%; }
}
.is-link { cursor: pointer; }
.is-link:hover { text-decoration: underline; }

/* -- clock ------------------------------------------------------------------ */

.clock {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  align-items: baseline;
  column-gap: 16px;
  row-gap: 3px;
}
.clock__value {
  font-size: 26px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.clock__cap { font-size: 11px; color: var(--text-dim); line-height: 1.35; }
/* Only drawn when there is a phase to separate from the UTC time, so a blank
   sun phase cannot leave a dangling middot. */
.clock__phase { text-transform: capitalize; opacity: 0.8; }
.clock__phase:not(:empty)::before { content: ' \00b7 '; opacity: 0.6; }
#clock-utc:empty + .clock__phase::before { content: none; }
.note {
  margin: 7px 0 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--off);
  min-height: 1em;
  max-width: 30ch;
}

/* -- legend and profile ----------------------------------------------------- */

.legend { width: 100%; height: 10px; border-radius: 3px; display: block; flex: 0 0 auto; }
.legend__scale {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: 10px;
  color: var(--text-dim);
}
/* Both charts share the space the summaries panel has left over, so they
   shrink on a short window rather than overflowing it. */
.profile { width: 100%; display: block; flex: 1 1 0; min-height: 56px; }
.rose { width: 100%; display: block; flex: 1.15 1 0; min-height: 66px; }
.chart__caption {
  flex: 0 0 auto;
  margin: 2px 0 0;
  font-size: 10px;
  text-align: center;
  color: var(--text-dim);
  min-height: 1em;
}

/* -- controls --------------------------------------------------------------- */

.controls {
  position: absolute;
  z-index: 4;
  left: 16px; right: 16px; bottom: 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  background: var(--ink-panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
}

.btn {
  flex: none;
  padding: 7px 16px;
  font: inherit;
  font-weight: 600;
  color: var(--ink);
  background: var(--accent);
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.btn:hover { filter: brightness(1.1); }

.btn:disabled, .chip:disabled, .scrub:disabled {
  opacity: 0.4;
  cursor: default;
}

.scrub {
  flex: 1 1 220px;
  min-width: 160px;
  accent-color: var(--accent);
  cursor: pointer;
}

.chips { display: flex; gap: 6px; flex-wrap: wrap; }

/* -- date navigation -------------------------------------------------------- */

.datenav {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-left: auto;
}
.chip--arrow { padding: 5px 10px; font-size: 14px; line-height: 1; }
.chip--date { min-width: 118px; font-variant-numeric: tabular-nums; }

.calendar {
  position: absolute;
  right: 14px;
  bottom: calc(100% + 10px);
  width: 232px;
  padding: 12px;
  background: rgba(10, 16, 33, 0.96);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  z-index: 6;
}
.calendar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.calendar__month { font-size: 12px; font-weight: 600; }
.calendar__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
.calendar__dow {
  font-size: 9px;
  text-align: center;
  color: var(--text-dim);
  padding-bottom: 2px;
}
.calendar__day {
  aspect-ratio: 1;
  border: 1px solid transparent;
  border-radius: 5px;
  font: inherit;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  padding: 0;
}
/* A night with no data is shown rather than hidden, so a gap in the record is
   visible instead of silently closing up. */
.calendar__day:disabled {
  color: rgba(223, 232, 246, 0.22);
  background: transparent;
  cursor: default;
}
.calendar__day--on { outline: 2px solid var(--accent); outline-offset: -2px; }
.calendar__key {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  font-size: 9px;
  color: var(--text-dim);
}
.calendar__scale { flex: 1; height: 7px; border-radius: 2px; display: block; }

.chip {
  padding: 5px 11px;
  font: inherit;
  font-size: 12px;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
/* Scoped away from the selected chip: .chip:hover outranks .chip--on, so an
   unscoped rule paints light text on the selected chip's light background. */
.chip:not(.chip--on):hover { color: var(--text); border-color: rgba(150, 180, 220, 0.4); }
.chip--on:hover { filter: brightness(0.92); }
.chip--live::before {
  content: '';
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--on);
  vertical-align: middle;
}

.chip--on {
  color: var(--ink);
  background: var(--text);
  border-color: var(--text);
  font-weight: 600;
}

/* Panels sit over a background that ranges from near-black to daylight blue.
   Against a bright sky the translucent dark needs to be a little more solid to
   hold its text, and the borders need to darken rather than lighten. */
body.is-daylight .panel,
body.is-daylight .controls {
  background: rgba(8, 14, 30, 0.82);
  border-color: rgba(20, 40, 70, 0.35);
}

/* -- explanation ------------------------------------------------------------ */

.about {
  max-width: 62ch;
  margin: 0 auto;
  padding: 40px 20px 56px;
  color: var(--text-dim);
}
.about h2 { font-size: 12px; margin-bottom: 12px; }
.about p { margin: 0 0 14px; }
.about strong { color: var(--text); font-weight: 600; }
.fine { font-size: 12px; opacity: 0.75; }

/* -- narrow screens ---------------------------------------------------------
   Panels stop floating over the sky and stack beneath it, so the animation
   keeps a usable area on a phone. */

/* The charts flex, so they no longer clip -- but below this there is not enough
   height left for two of them to say anything, and one readable chart beats two
   squashed ones. */
@media (max-height: 620px) {
  .rose-part { display: none; }
}

@media (max-width: 860px) {
  .stage { height: auto; min-height: 0; display: flex; flex-direction: column; }
  /* Only the identity panel moves -- everything else keeps the order it
     already had (default order: 0, so DOM order among them is unchanged).
     The sky canvases are absolutely positioned (see #sky-fg/#sky-labels
     below) and pinned with inset:0, so reordering flex items around them
     does not affect their own placement. */
  .panel--tl { order: -1; }
  .sky { position: relative; height: 62vh; min-height: 320px; }
  #sky-fg, #sky-labels { position: absolute; inset: 0; height: 62vh; min-height: 320px; }

  /* Embedded, the sky's vh is the *iframe's own* height, not the visitor's
     screen -- so it cannot be used if the host page ever sizes the iframe to
     fit this document's content: taller iframe -> taller sky -> taller
     document -> host grows the iframe again, without end. A fixed height
     breaks that loop and makes the whole embedded document a known constant
     at this width, which is what lets a host page give the iframe one fixed
     height and never need to scroll it internally at all. */
  .embed .sky, .embed #sky-fg, .embed #sky-labels { height: 300px; }

  .rail {
    position: static;
    width: auto;
    display: block;
    pointer-events: auto;
  }
  .panel--summaries { overflow: visible; }
  .profile { height: 92px; flex: none; }
  .rose { height: 104px; flex: none; }

  .panel, .controls {
    position: static;
    max-width: none;
    width: auto;
    margin: 10px 12px 0;
    backdrop-filter: none;
  }
  .panel--stats { text-align: left; }
  .chips--dates { margin-left: 0; }
}

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