/* Locator's map shell. Hand-written CSS on purpose: the stack fixes "vanilla
   JS, no build step", and Leaflet brings its own stylesheet for the map canvas. */

:root {
  --ink: #1b1f24;
  --ink-soft: #5a6472;
  --panel: #ffffff;
  --panel-edge: #dfe3e8;
  --shadow: 0 1px 3px rgba(0, 0, 0, .18), 0 6px 20px rgba(0, 0, 0, .10);

  /* One colour per freshness state; the dot's drawing is the only thing the
     state controls (blueprint: the age is always shown alongside). */
  --fresh: #1668dc;
  --aging: #1668dc;
  --stale: #7b8494;
  --offline: #7b8494;

  /* Not a freshness state: a person who has stopped sharing on purpose. It
     replaces the state entirely, so it cannot be mistaken for an old fix. */
  --paused: #8a6d3b;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  font: 15px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
}

body {
  display: flex;
  flex-direction: column;
}

.topbar {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  padding: .55rem .9rem;
  background: var(--panel);
  border-bottom: 1px solid var(--panel-edge);
  z-index: 1000;
}

.topbar__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .01em;
}

.topbar__status {
  font-size: .82rem;
  color: var(--ink-soft);
}

.topbar__status--error { color: #b3261e; }

/* Who is signed in, and the two things they may do to their own session. */
.topbar__me {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .5rem;
}

.topbar__who {
  font-size: .85rem;
  font-weight: 600;
}

.topbar__signout { margin: 0; }

.button {
  font: inherit;
  padding: .35rem .8rem;
  border-radius: .4rem;
  border: 1px solid transparent;
  background: var(--fresh);
  color: #fff;
  cursor: pointer;
}

.button:disabled { opacity: .6; cursor: progress; }

.button--quiet {
  background: var(--panel);
  color: var(--ink);
  border-color: var(--panel-edge);
  font-size: .85rem;
  padding: .25rem .6rem;
}

.button--quiet[aria-pressed="true"] {
  border-color: var(--paused);
  color: var(--paused);
}

/* The login page: one card, nothing else. */
.page--login {
  align-items: center;
  justify-content: center;
  background: #eef1f5;
  padding: 1.5rem;
}

.card {
  width: min(22rem, 100%);
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: .7rem;
  box-shadow: var(--shadow);
  padding: 1.4rem 1.5rem 1.2rem;
}

.card__title {
  margin: 0;
  font-size: 1.35rem;
}

.card__lead {
  margin: .3rem 0 1.1rem;
  color: var(--ink-soft);
  font-size: .9rem;
}

.card__note {
  margin: 1rem 0 0;
  font-size: .78rem;
  color: var(--ink-soft);
}

.form { display: flex; flex-direction: column; gap: .8rem; }

.form__field { display: flex; flex-direction: column; gap: .25rem; }

.form__label { font-size: .85rem; color: var(--ink-soft); }

.form input {
  font: inherit;
  padding: .45rem .6rem;
  border: 1px solid var(--panel-edge);
  border-radius: .4rem;
  background: #fff;
  color: var(--ink);
}

.form input:focus-visible { outline: 2px solid var(--fresh); outline-offset: 1px; }

.form__errors { color: #b3261e; font-size: .85rem; }
.form__errors ul { margin: 0; padding-left: 1.1rem; }
.form__errors:empty { display: none; }

#map {
  flex: 1 1 auto;
  min-height: 0;
  background: #e8ebef;
}

/* Roster: every person, including those the map cannot draw because their
   phone has never reported. The map must not silently omit someone. */
.roster {
  position: absolute;
  right: .75rem;
  top: 3.4rem;
  z-index: 900;
  max-width: 15rem;
  max-height: calc(100% - 5rem);
  overflow-y: auto;
  padding: .5rem .65rem;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: .5rem;
  box-shadow: var(--shadow);
}

.roster:empty { display: none; }

.roster__person {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .2rem 0;
  cursor: pointer;
}

.roster__name { font-weight: 600; }

.roster__age {
  font-size: .8rem;
  color: var(--ink-soft);
}

.roster__person--none { cursor: default; }

.swatch {
  flex: 0 0 auto;
  width: .7rem;
  height: .7rem;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .25);
}

.swatch--fresh { background: var(--fresh); }
.swatch--aging { background: var(--aging); opacity: .55; }
.swatch--stale { background: var(--stale); }
.swatch--offline { background: var(--offline); opacity: .55; }
.swatch--none { background: transparent; box-shadow: 0 0 0 1px var(--panel-edge); }
.swatch--paused { background: var(--paused); opacity: .5; }

/* Map pins. The icon element sits exactly on the fix; its children are placed
   around that point. */
.pin { position: relative; }

.pin__dot {
  position: absolute;
  width: 15px;
  height: 15px;
  margin: -7.5px 0 0 -7.5px;
  border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
}

.pin__label {
  position: absolute;
  left: 13px;
  top: -11px;
  white-space: nowrap;
  padding: 1px 6px;
  border-radius: .35rem;
  background: rgba(255, 255, 255, .92);
  border: 1px solid var(--panel-edge);
  font-size: .8rem;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}

.pin__age { color: var(--ink-soft); }

/* Battery of the reporting phone, beside the age and quieter than it: the age
   is what the map is about, the charge is context. */
.pin__battery { color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* fresh: solid dot. */
.pin--fresh .pin__dot { background: var(--fresh); }

/* aging: dimmed dot. */
.pin--aging .pin__dot { background: var(--aging); opacity: .5; }
.pin--aging .pin__label { opacity: .85; }

/* stale: greyed dot, age shown prominently. */
.pin--stale .pin__dot { background: var(--stale); }
.pin--stale .pin__age { color: var(--ink); font-weight: 600; }

/* offline: greyed dot, "last seen ...". */
.pin--offline .pin__dot { background: var(--offline); opacity: .55; }
.pin--offline .pin__label { opacity: .8; }
.pin--offline .pin__age { font-style: italic; }

/* paused: the last stored position, dimmed, with when sharing stopped. */
.pin--paused .pin__dot { background: var(--paused); opacity: .45; }
.pin--paused .pin__label { opacity: .75; }
.pin--paused .pin__age { color: var(--paused); font-style: italic; }

.roster__person--paused .roster__age { color: var(--paused); font-style: italic; }

.popup__note {
  font-size: .8rem;
  color: var(--ink-soft);
  margin-bottom: .3rem;
}

.popup__name {
  font-weight: 600;
  font-size: 1rem;
}

.popup__age { margin: .1rem 0 .35rem; }

.popup__facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .5rem;
  font-size: .85rem;
}

.popup__facts dt { color: var(--ink-soft); }
.popup__facts dd { margin: 0; }

/* The operator's enrollment page. Same ink and panels as the map; a plain
   document rather than a canvas, because it is a list of people and forms. */
.page--admin {
  background: #eef1f5;
  overflow-y: auto;
}

.admin {
  width: min(52rem, 100%);
  margin: 0 auto;
  padding: 1.2rem 1rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.admin__section,
.admin__person {
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: .7rem;
  box-shadow: var(--shadow);
  padding: 1rem 1.1rem 1.1rem;
}

.admin__heading {
  margin: 0 0 .3rem;
  font-size: 1.05rem;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .5rem;
}

.admin__subheading {
  margin: 0 0 .35rem;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.admin__block + .admin__block { margin-top: 1rem; }

.admin__lead,
.admin__empty {
  margin: 0 0 .8rem;
  color: var(--ink-soft);
  font-size: .9rem;
}

.admin__lead--warn {
  color: var(--paused);
  font-weight: 600;
}

.admin__notice {
  margin: 0;
  padding: .6rem .8rem;
  border-radius: .5rem;
  background: #e7f0fb;
  border: 1px solid #c3d9f4;
  font-size: .9rem;
}

.admin__muted {
  color: var(--ink-soft);
  font-size: .82rem;
}

.admin__fact { margin: 0; }

.admin__tag {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: .1rem .4rem;
  border-radius: .3rem;
  background: #e7f0fb;
  color: var(--fresh);
}

.admin__tag--revoked,
.admin__tag--paused {
  background: #f5ece0;
  color: var(--paused);
}

/* Forms sit inline on a wide screen and stack on a phone: the operator does
   this standing next to the person, often on the phone being enrolled. */
.form--row {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: .6rem;
}

.form--row .form__field { flex: 1 1 10rem; }

.form__check {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .85rem;
  padding-bottom: .45rem;
}

.admin__phones {
  list-style: none;
  margin: 0 0 .8rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

.admin__phone {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .5rem .65rem;
  border: 1px solid var(--panel-edge);
  border-radius: .5rem;
}

.admin__phone--revoked { opacity: .65; }

.admin__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
}

.admin__actions form { margin: 0; }

.admin__facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .3rem .8rem;
  font-size: .9rem;
}

.admin__facts dt { color: var(--ink-soft); }
.admin__facts dd { margin: 0; }

.admin code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .85em;
  word-break: break-all;
}

.admin__token {
  display: inline-block;
  padding: .15rem .4rem;
  border-radius: .3rem;
  background: #f5ece0;
  font-size: 1em;
  user-select: all;
}

.admin__config {
  margin: .8rem 0 .4rem;
  padding: .7rem .8rem;
  border-radius: .5rem;
  background: #f4f6f9;
  border: 1px solid var(--panel-edge);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .8rem;
  overflow-x: auto;
}
