/* ---------------------------------------------------------------------------
   The admin console's own stylesheet.

   Deliberately separate from styles.css, which dresses the parent-facing pages.
   Those are one screen read one-handed in a driveway; this is a desk tool with
   a map at the centre of it, and the two want different furniture. Keeping them
   apart means neither is styled by accident when the other changes.

   The console is built around three ideas:
     - nothing is editable until you say so, so the resting state is readable;
     - one run at a time, because a morning and an afternoon are two drives and
       showing both at once is what made the old page unreadable;
     - the map is the page, not an illustration on it.
   --------------------------------------------------------------------------- */

:root {
  color-scheme: light dark;

  --bg: #f4f6fa;
  --bg-tint: #e9edf6;
  --surface: #ffffff;
  --surface-2: #f5f7fb;
  --surface-3: #eceff6;
  --line: rgba(15, 23, 42, 0.09);
  --line-strong: rgba(15, 23, 42, 0.16);

  --text: #10141c;
  --text-2: #3d475a;
  --muted: #6b7688;
  --faint: #98a2b3;

  --accent: #4f46e5;
  --accent-hi: #6366f1;
  --accent-lo: #4338ca;
  --accent-soft: rgba(79, 70, 229, 0.1);
  --on-accent: #ffffff;
  --ring: rgba(79, 70, 229, 0.28);

  --ok: #0f8a5f;
  --ok-soft: rgba(15, 138, 95, 0.12);
  --warn: #b45309;
  --warn-soft: rgba(180, 83, 9, 0.12);
  --danger: #c0342a;
  --danger-soft: rgba(192, 52, 42, 0.1);

  --r-xs: 6px;
  --r-sm: 9px;
  --r: 12px;
  --r-lg: 16px;
  --r-xl: 22px;

  --sh-1: 0 1px 2px rgba(16, 24, 40, 0.05);
  --sh-2: 0 1px 2px rgba(16, 24, 40, 0.05), 0 10px 24px -14px rgba(16, 24, 40, 0.28);
  --sh-3: 0 4px 8px -4px rgba(16, 24, 40, 0.12), 0 24px 48px -20px rgba(16, 24, 40, 0.35);

  --bar-h: 3.5rem;
  --sidebar-w: 25rem;

  --font: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
  --mono: ui-monospace, SFMono-Regular, 'JetBrains Mono', Menlo, monospace;

  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7688' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a0d13;
    --bg-tint: #101623;
    --surface: #131822;
    --surface-2: #19202c;
    --surface-3: #212a38;
    --line: rgba(255, 255, 255, 0.08);
    --line-strong: rgba(255, 255, 255, 0.16);

    --text: #eef1f6;
    --text-2: #c3cbd8;
    --muted: #8f9bad;
    --faint: #6b7688;

    --accent: #7c7bf5;
    --accent-hi: #918ffa;
    --accent-lo: #6461e8;
    --accent-soft: rgba(124, 123, 245, 0.16);
    --ring: rgba(124, 123, 245, 0.4);

    --ok: #3ecf8e;
    --ok-soft: rgba(62, 207, 142, 0.14);
    --warn: #f0a638;
    --warn-soft: rgba(240, 166, 56, 0.14);
    --danger: #ff7b6f;
    --danger-soft: rgba(255, 123, 111, 0.13);

    --sh-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --sh-2: 0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 28px -16px rgba(0, 0, 0, 0.8);
    --sh-3: 0 6px 12px -6px rgba(0, 0, 0, 0.5), 0 28px 56px -24px rgba(0, 0, 0, 0.9);

    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238f9bad' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body { overflow: hidden; }

h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -0.014em; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.05rem; }
h3 { font-size: 0.95rem; }
h4 { font-size: 0.8rem; }
p { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--accent-soft); }

/* Data reads as columns even when it is not in a table. */
.num { font-variant-numeric: tabular-nums; }

/* --- App shell ------------------------------------------------------------ */

.app {
  display: grid;
  grid-template-rows: var(--bar-h) minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
  height: 100%;
}

.appbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0 0.75rem;
  padding-left: calc(0.75rem + env(safe-area-inset-left));
  padding-right: calc(0.75rem + env(safe-area-inset-right));
  flex-wrap: nowrap;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  position: relative;
  z-index: 40;
}

.appbar__left, .appbar__right {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}
/* Equal flex on the outer slots is what keeps the run switch optically centred
   however long the school's name turns out to be. */
.appbar__left { flex: 1 1 0; }
.appbar__right { flex: 1 1 0; justify-content: flex-end; }
.appbar__center { flex: 0 0 auto; }

.appbar__title {
  font-size: 0.95rem;
  font-weight: 650;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.appbar__sub {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
}

.mark {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
  border-radius: 9px;
  color: var(--on-accent);
  background: linear-gradient(150deg, var(--accent-hi), var(--accent-lo));
  box-shadow: var(--sh-1), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

/* The school this route belongs to, and the way back to it. The name is the
   first thing to go when the bar runs out of room: the arrow still says where
   the button leads, and the route's own name below it is the identifying one. */
.crumb { min-width: 0; max-width: 15rem; }
.crumb span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sep {
  width: 1px;
  height: 1.35rem;
  background: var(--line);
  flex: none;
  margin: 0 0.15rem;
}

.app__body { min-height: 0; overflow: hidden; position: relative; }

/* --- Scrolling document pages (schools, one school) ----------------------- */

.scroll { height: 100%; overflow: auto; overscroll-behavior: contain; }

.sheet {
  max-width: 62rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 5rem;
  padding-left: calc(1.25rem + env(safe-area-inset-left));
  padding-right: calc(1.25rem + env(safe-area-inset-right));
}

/* A single wash of colour behind the document pages, so they do not read as a
   flat grey slab the way the old console did. */
.scroll::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 20rem;
  pointer-events: none;
  background: radial-gradient(70rem 20rem at 50% -8rem, var(--bg-tint), transparent 70%);
}

.pagehead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.pagehead h1 { font-size: 1.75rem; }
.pagehead p { color: var(--muted); font-size: 0.9rem; margin-top: 0.2rem; }

/* --- Cards ---------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-2);
  margin-bottom: 1rem;
  overflow: hidden;
}

.card__head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.9rem 1.1rem;
  border-bottom: 1px solid var(--line);
}
.card__head--plain { border-bottom: 0; padding-bottom: 0.35rem; }
.card__head h2 { flex: 1 1 auto; min-width: 0; }
.card__head .sub { display: block; font-size: 0.8rem; font-weight: 400; color: var(--muted); margin-top: 0.1rem; }

.card__body { padding: 1.1rem; }
.card__body--flush { padding: 0; }

/* --- Rows ----------------------------------------------------------------- */

.rows { display: flex; flex-direction: column; }
.rows > * + * { border-top: 1px solid var(--line); }

.rowitem {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.8rem 1.1rem;
  min-height: 3.5rem;
  background: none;
  border: 0;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
}
a.rowitem:hover, button.rowitem:hover { background: var(--surface-2); text-decoration: none; cursor: pointer; }

/* A row that is a link but also carries its own buttons: the link takes the
   place of the flexing child, and the hover still reads as the whole row. The
   button reset is here rather than only on .rowitem because a row whose target
   is a form rather than a page uses a <button> for this child instead of an
   <a> -- same class, same appearance, correct semantics either way. */
.rowitem__link {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  font: inherit;
}
.rowitem__link:hover { text-decoration: none; }
.rowitem:has(> .rowitem__link:hover) { background: var(--surface-2); cursor: pointer; }

.rowitem__main { flex: 1 1 auto; min-width: 0; }
.rowitem__title { display: block; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rowitem__meta { display: block; font-size: 0.82rem; color: var(--muted); margin-top: 0.1rem; }
.rowitem__side { flex: none; display: flex; align-items: center; gap: 0.4rem; color: var(--faint); }

/* The inline editor a row hides until its Edit button is pressed. */
.drawer {
  padding: 0 1.1rem 1.1rem;
  border-top: 1px dashed var(--line);
  background: var(--surface-2);
  animation: drop 0.16s ease-out;
}
.drawer > * + * { margin-top: 0.9rem; }
.drawer__top { padding-top: 1rem; }

@keyframes drop {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* --- Picking rows --------------------------------------------------------- */

/* The tick box on a row, in its own label so that clicking it is not also
   clicking the row it sits in. */
.pick { display: flex; align-items: center; flex: none; padding: 0.2rem; cursor: pointer; }
.pick input {
  width: 1.05rem;
  height: 1.05rem;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.pick--all { gap: 0.45rem; }

/* The strip that carries "select all", between the head of a list and its rows.
   Gone entirely when there is nothing to select. */
.pickhead { padding: 0 1.1rem 0.7rem; }
.pickhead:empty { display: none; }

/* What is about to happen to what is ticked. Sits under the head of the list
   rather than floating over it: it appears when the first row is ticked, and a
   strip that pushes the list down is easier to notice than one that covers it. */
.bulkbar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.6rem 1.1rem;
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.bulkbar__count { font-size: 0.84rem; font-weight: 600; color: var(--text-2); margin-right: auto; }

/* --- Buttons -------------------------------------------------------------- */

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--text);
  --btn-line: var(--line-strong);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.42rem;
  height: 2.25rem;
  padding: 0 0.85rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--btn-line);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 560;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--sh-1);
  transition: background 0.14s, border-color 0.14s, transform 0.08s, box-shadow 0.14s;
}
.btn:hover:not(:disabled) { background: var(--surface-2); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

.btn--primary {
  --btn-fg: var(--on-accent);
  --btn-line: transparent;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent-lo));
  box-shadow: var(--sh-1), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
.btn--primary:hover:not(:disabled) { background: linear-gradient(180deg, var(--accent-hi), var(--accent)); }

.btn--ghost { --btn-bg: transparent; --btn-line: transparent; box-shadow: none; color: var(--text-2); }
.btn--ghost:hover:not(:disabled) { background: var(--surface-3); color: var(--text); }

.btn--danger { --btn-fg: var(--danger); --btn-line: color-mix(in srgb, var(--danger) 35%, transparent); --btn-bg: transparent; box-shadow: none; }
.btn--danger:hover:not(:disabled) { background: var(--danger-soft); }

.btn--sm { height: 1.85rem; padding: 0 0.6rem; font-size: 0.8rem; gap: 0.3rem; border-radius: var(--r-xs); }
.btn--icon { width: 2.25rem; padding: 0; }
.btn--sm.btn--icon { width: 1.85rem; }
.btn--wide { width: 100%; }

.btn svg { flex: none; }

.btnrow { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.btnrow--end { justify-content: flex-end; }
.btnrow--split { justify-content: space-between; }
.spacer { flex: 1 1 auto; }

/* --- Segmented control ---------------------------------------------------- */

.seg {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0;
  padding: 3px;
  border-radius: 999px;
  background: var(--surface-3);
  border: 1px solid var(--line);
  isolation: isolate;
}
.seg__thumb {
  position: absolute;
  z-index: 0;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc((100% - 6px) / var(--seg-n, 2));
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--sh-1);
  transform: translateX(calc(var(--seg-i, 0) * 100%));
  transition: transform 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}
.seg__opt {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  height: 1.85rem;
  padding: 0 0.85rem;
  border: 0;
  background: none;
  border-radius: 999px;
  font: inherit;
  font-size: 0.83rem;
  font-weight: 560;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.16s;
}
.seg__short { display: none; }
.seg__opt:hover { color: var(--text-2); }
.seg__opt.is-on { color: var(--text); }
.seg__opt:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

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

/* --- Chips ---------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  height: 1.4rem;
  padding: 0 0.5rem;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--surface-3);
  color: var(--muted);
  white-space: nowrap;
}
.chip--ok { background: var(--ok-soft); color: var(--ok); }
.chip--warn { background: var(--warn-soft); color: var(--warn); }
.chip--danger { background: var(--danger-soft); color: var(--danger); }
.chip--accent { background: var(--accent-soft); color: var(--accent); }
.chip svg { width: 0.8rem; height: 0.8rem; }

/* --- Forms ---------------------------------------------------------------- */

.field { display: block; min-width: 0; }
.field > .lbl {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-2);
  margin-bottom: 0.3rem;
}
.field > .hint { display: block; font-size: 0.78rem; color: var(--muted); margin-top: 0.3rem; }

input[type='text'], input[type='email'], input[type='number'], input[type='time'], input[type='password'],
select, textarea {
  width: 100%;
  height: 2.4rem;
  padding: 0 0.65rem;
  font: inherit;
  font-size: 0.9rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color 0.14s, box-shadow 0.14s;
}
textarea { height: auto; padding: 0.55rem 0.65rem; resize: vertical; font-family: var(--mono); font-size: 0.82rem; line-height: 1.5; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--ring);
}
input::placeholder, textarea::placeholder { color: var(--faint); }

/* The chevron is a background image rather than the usual pair of gradient
   triangles, which read as a heavy solid arrowhead next to monoline icons. */
select {
  appearance: none;
  padding-right: 2.1rem;
  background-image: var(--chevron);
  background-position: right 0.6rem center;
  background-size: 15px 15px;
  background-repeat: no-repeat;
  cursor: pointer;
}
select.sel--sm { height: 1.9rem; font-size: 0.8rem; padding-right: 1.8rem; background-size: 13px 13px; background-position: right 0.45rem center; }

/* The route switcher in the top bar. A select rather than a bespoke popover:
   it is a list of one school's routes, and the native control already knows
   how to be long, searchable by keystroke and reachable from a keyboard. */
select.pillselect {
  width: auto;
  max-width: 16rem;
  height: 2rem;
  font-size: 0.87rem;
  font-weight: 600;
  border-radius: 999px;
  border-color: var(--line);
  background-color: var(--surface-3);
  padding-left: 0.8rem;
  padding-right: 1.9rem;
  background-position: right 0.85rem center, right 0.55rem center;
}

/* One service window: which day, from when, to when, and a way to drop it. */
.hoursrow {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 0.5rem;
  align-items: center;
}

input[type='file'] {
  width: 100%;
  font-size: 0.85rem;
  padding: 0.5rem;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  cursor: pointer;
}
input[type='file']::file-selector-button {
  font: inherit;
  font-size: 0.82rem;
  font-weight: 560;
  margin-right: 0.65rem;
  padding: 0.3rem 0.7rem;
  border-radius: var(--r-xs);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}

.grid { display: grid; gap: 0.85rem; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }

.stack > * + * { margin-top: 0.85rem; }
.stack--tight > * + * { margin-top: 0.5rem; }
.mt { margin-top: 0.9rem; }
.mt-sm { margin-top: 0.55rem; }

/* A checkbox as a switch: the console's options are all on/off decisions about
   how the next drive is planned, and a switch says "setting" where a tickbox
   says "form field". */
.switch {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  cursor: pointer;
  user-select: none;
}
.switch input {
  appearance: none;
  flex: none;
  width: 2.2rem;
  height: 1.3rem;
  margin: 0.1rem 0 0;
  border-radius: 999px;
  background: var(--surface-3);
  border: 1px solid var(--line-strong);
  position: relative;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s;
}
.switch input::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 2px;
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--sh-1);
  transform: translate(0, -50%);
  transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.switch input:checked { background: var(--accent); border-color: transparent; }
.switch input:checked::after { transform: translate(0.9rem, -50%); }
.switch input:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.switch__text { font-size: 0.87rem; }
.switch__text .hint { display: block; font-size: 0.78rem; color: var(--muted); margin-top: 0.1rem; }

/* --- Notes and alerts ----------------------------------------------------- */

.note {
  display: flex;
  gap: 0.55rem;
  padding: 0.65rem 0.8rem;
  border-radius: var(--r-sm);
  font-size: 0.84rem;
  line-height: 1.45;
  background: var(--surface-2);
  color: var(--text-2);
  border: 1px solid var(--line);
}
.note svg { flex: none; margin-top: 0.1rem; }
.note--warn { background: var(--warn-soft); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 22%, transparent); }
.note--danger { background: var(--danger-soft); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 22%, transparent); }
.note--ok { background: var(--ok-soft); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 22%, transparent); }
.note ul { margin: 0.35rem 0 0; padding-left: 1.05rem; }

/* A note that says what it is folded up, and why when it is opened.
   `display: block` overrides the flex above: a <details> laid out as a flex
   container puts its summary and its body side by side, which is not a
   disclosure. The marker is drawn on the right instead, where the eye already is
   after reading the line. */
details.note { display: block; }
details.note > summary {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  font-weight: 560;
  list-style: none;
}
details.note > summary::-webkit-details-marker { display: none; }
details.note > summary::after { content: '⌄'; margin-left: auto; line-height: 1; opacity: 0.7; }
details.note[open] > summary::after { content: '⌃'; }
details.note > *:not(summary) { margin-top: 0.5rem; }

/* The inline editor keeps empty slots for the things a row may have to say --
   why it was held, the addresses Google offers instead -- and an empty slot is
   still a gap in a stack. Folded away until it has something in it. */
.drawer div:empty { display: none; }

.muted { color: var(--muted); font-size: 0.85rem; }
.tiny { font-size: 0.78rem; }

/* A device token or invite link, shown exactly once. */
.secret {
  font-family: var(--mono);
  font-size: 0.85rem;
  word-break: break-all;
  user-select: all;
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0.55rem 0.7rem;
}

/* --- Tables --------------------------------------------------------------- */

.table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--faint);
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.table td { padding: 0.55rem 0.6rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tr:last-child td { border-bottom: 0; }
.table td.right, .table th.right { text-align: right; }
.table td.right { white-space: nowrap; }

/* --- Empty states --------------------------------------------------------- */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.4rem;
  padding: 2.5rem 1.5rem;
  color: var(--muted);
}
.empty__icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--r);
  background: var(--surface-3);
  color: var(--faint);
  margin-bottom: 0.35rem;
}
.empty h3 { color: var(--text); }
.empty p { font-size: 0.87rem; max-width: 28rem; }
.empty .btn { margin-top: 0.6rem; }

/* --- Workspace: sidebar + map -------------------------------------------- */

.workspace {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  height: 100%;
}

/* The school page uses the same frame -- a rail that stays and a pane that
   scrolls -- but the rail lists sections rather than holding controls, so it is
   narrower, and the pane is a document rather than a map. The gradient wash is
   re-anchored to the pane: .scroll::before is absolute against .app__body, and
   left there it would spill across the rail as well. */
.workspace--sections { --sidebar-w: 15.5rem; }
.workspace--sections .scroll { position: relative; }
.workspace--sections .sheet { max-width: 56rem; }

.sectionnav { padding: 0.5rem 0; }
.sectionnav .rowitem { min-height: 3rem; }
/* aria-current is the whole state: what the browser is told and what the eye is
   told are the same attribute, so they cannot drift apart. */
.sectionnav .rowitem[aria-current='page'] {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.sectionnav .rowitem[aria-current='page'] .rowitem__title { color: var(--accent-lo); }
.sectionnav .rowitem[aria-current='page'] .rowitem__side { color: var(--accent); }

.sidebar {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: var(--surface);
  border-right: 1px solid var(--line);
  z-index: 20;
}
.sidebar__scroll { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.sidebar__foot {
  flex: none;
  padding: 0.7rem 0.9rem;
  padding-bottom: calc(0.7rem + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 92%, var(--bg));
}

.block { padding: 0.95rem 1rem; border-bottom: 1px solid var(--line); }
.block:last-child { border-bottom: 0; }
.block__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}
.block__head h3 { flex: 1 1 auto; }
.block__head .eyebrow {
  flex: 1 1 auto;
  font-size: 0.7rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The run headline: which drive is on screen and whether it is the live one. */
.runhead { display: flex; align-items: flex-start; gap: 0.6rem; }
.runhead__icon {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  color: var(--accent);
}
.runhead__main { flex: 1 1 auto; min-width: 0; }
.runhead h2 { font-size: 1rem; }
.runhead__sub { font-size: 0.82rem; color: var(--muted); margin-top: 0.05rem; }

/* The numbers about the drive. Three short ones read across; the service hours
   are a sentence, so they take the row underneath rather than wrapping inside a
   third of the panel's width. */
.stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
.stat--full { grid-column: 1 / -1; }
.stat {
  padding: 0.55rem 0.65rem;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.stat__k { font-size: 0.66rem; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); }
.stat__v { font-size: 0.95rem; font-weight: 620; margin-top: 0.1rem; font-variant-numeric: tabular-nums; }
.stat__v small { font-weight: 500; color: var(--muted); font-size: 0.78rem; }

/* --- Stop list ------------------------------------------------------------ */

.stops { display: flex; flex-direction: column; gap: 2px; }

.stop {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  width: 100%;
  padding: 0.5rem 0.55rem;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.stop:hover { background: var(--surface-2); }
.stop.is-on { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 25%, transparent); }
.stop:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

.stop__num {
  display: grid;
  place-items: center;
  flex: none;
  width: 1.45rem;
  height: 1.45rem;
  border-radius: 50%;
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: var(--accent);
  color: var(--on-accent);
}
.stop__num.is-school { background: var(--ok); }
.stop__num.is-depot { background: var(--faint); }

.stop__body { flex: 1 1 auto; min-width: 0; }
.stop__name { display: block; font-size: 0.87rem; font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stop__addr { display: block; font-size: 0.78rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stop__meta { display: flex; gap: 0.45rem; flex-wrap: wrap; margin-top: 0.2rem; font-size: 0.74rem; color: var(--faint); font-variant-numeric: tabular-nums; }
.stop__meta .warn { color: var(--warn); }
.stop__due {
  flex: none;
  font-size: 0.8rem;
  font-weight: 620;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
  padding-top: 0.05rem;
}

/* The editing variant: the same rows turned into forms -- the address and name
   as fields, the two rulings as pickers, and a way out. */
.stop--edit { align-items: flex-start; cursor: default; background: var(--surface-2); border-color: var(--line); }
.stop--edit:hover { background: var(--surface-2); }
/* The number and the remove button line up with the address, which is the row's
   subject; everything under it is qualification. */
.stop--edit > .stop__num, .stop--edit > .btn { margin-top: 0.2rem; }

.stop__fields { display: grid; gap: 0.3rem; }
.stop__fields input { height: 2.05rem; font-size: 0.84rem; }
.stop__fields input:nth-child(2) { font-size: 0.8rem; color: var(--text-2); }

.stop__note { display: block; font-size: 0.73rem; color: var(--warn); margin-top: 0.3rem; }
.stop__note[hidden] { display: none; }

.stop--edit .stop__controls { display: flex; gap: 0.35rem; margin-top: 0.4rem; }
.stop--edit .stop__controls select { flex: 1 1 0; min-width: 0; }
/* Wide enough for two digits and the spinner, and no wider: it sits beside two
   selects that need the rest of the row. */
.stop--edit .stop__riders { flex: 0 0 4.5rem; min-width: 0; }

/* --- An address the check held back ---------------------------------------
   One block per address Google placed badly: which rows it came from, what
   Google actually returned, why that is not good enough, and the two ways out
   -- correct it, or vouch for it. Bordered and inset rather than listed flat,
   because each one is a question that has to be answered individually and a
   flat list reads as something to scroll past. */
.flagged {
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  padding: 0.75rem 0.85rem;
}

.flagged__head { display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; margin-bottom: 0.45rem; }
.flagged__head strong { font-size: 0.85rem; }

/* Google's answer, quoted rather than paraphrased: it is the evidence. */
.flagged__found {
  font-size: 0.85rem;
  padding: 0.4rem 0.55rem;
  border-radius: var(--r-sm);
  background: var(--surface);
  border: 1px solid var(--line);
}
.flagged__found > div:first-of-type { font-weight: 560; }

/* The runners-up, as buttons rather than prose: the address wanted is often
   sitting in second place, and retyping it by hand is what this saves. Stacked
   and left-aligned because a formatted address is a long line, not a chip. */
.flagged__alts { margin-top: 0.5rem; }
.flagged__alt-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
  margin-top: 0.25rem;
}
.flagged__alt-list .btn { max-width: 100%; text-align: left; font-weight: 480; white-space: normal; }

/* One press for the whole card, set apart from the rows it answers so it does
   not read as belonging to the first of them. */
.flagged__all { display: flex; justify-content: flex-end; }

.flagged__why { margin: 0.55rem 0; padding-left: 1.05rem; font-size: 0.82rem; color: var(--text-2); }
.flagged__why li + li { margin-top: 0.3rem; }
.flagged__why strong { color: var(--warn); }

/* The correction field carries the row on its own, so it does not need the
   label spacing the surrounding form fields use. */
.flagged .field { margin-top: 0.2rem; }

/* --- Map canvas ----------------------------------------------------------- */

.canvas { position: relative; min-width: 0; background: var(--bg-tint); }
.canvas__map { position: absolute; inset: 0; }

/* Anything floating over the map: the proposal banner, the legend. */
.overlay {
  position: absolute;
  z-index: 10;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border: 1px solid var(--line);
  box-shadow: var(--sh-3);
}

.banner {
  top: 0.85rem;
  left: 50%;
  transform: translateX(-50%);
  max-width: min(44rem, calc(100% - 2rem));
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.6rem 0.7rem 0.6rem 0.9rem;
  animation: drop 0.2s ease-out;
}
.banner__text { min-width: 0; }
.banner__title { font-size: 0.87rem; font-weight: 620; }
.banner__sub { font-size: 0.78rem; color: var(--muted); }

.legend {
  left: 0.85rem;
  bottom: 1.6rem;
  display: flex;
  gap: 0.85rem;
  padding: 0.45rem 0.7rem;
  font-size: 0.75rem;
  color: var(--muted);
}
.legend[hidden] { display: none; }
.legend span { display: inline-flex; align-items: center; gap: 0.32rem; }
.legend i { width: 0.6rem; height: 0.6rem; border-radius: 50%; display: inline-block; }

/* Shown instead of the map when there is no key, no plan, or Maps fails. */
.canvas__blank {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  text-align: center;
  padding: 2rem;
  color: var(--muted);
  background:
    radial-gradient(40rem 20rem at 50% 40%, var(--bg-tint), transparent 70%),
    repeating-linear-gradient(45deg, transparent 0 12px, color-mix(in srgb, var(--line) 40%, transparent) 12px 13px);
}
.canvas__blank h3 { color: var(--text); }
.canvas__blank p { font-size: 0.87rem; max-width: 26rem; }

/* Numbered pins. The number is the driving order, which is the one thing the
   map has to say that a list cannot. */
.pin {
  display: grid;
  place-items: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50% 50% 50% 2px;
  transform: rotate(45deg);
  background: var(--accent);
  color: #fff;
  border: 2px solid #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  font: 700 0.72rem/1 var(--font);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.18s;
}
.pin > span { transform: rotate(-45deg); }
.pin.is-school { background: var(--ok); }
.pin.is-depot { background: #64748b; }
.pin.is-on { transform: rotate(45deg) scale(1.35); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); z-index: 5; }

/* Plain dots, for the address book on the map. No number, because there is no
   order to say: the colour is the whole message -- placed, held, or not looked
   at yet. */
.dot {
  width: 0.72rem;
  height: 0.72rem;
  border-radius: 50%;
  background: var(--ok);
  /* Black rather than the pins' white: a dot is a tenth the size of a numbered
     pin, and a white ring on pale roadmap or bright satellite imagery reads as
     part of the map. The dark ring is what makes the colour inside it a colour. */
  border: 2px solid #000;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.18s;
}
.dot.is-suspect { background: var(--warn); }
.dot.is-pending { background: #64748b; }
.dot.is-on { transform: scale(1.8); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); z-index: 5; }

/* One address in the map screen's rail. A holder around the row rather than the
   row itself, because a saved row replaces itself and the id has to outlive it. */
.maprow.is-on { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.maprow__note {
  padding: 0 1.1rem 0.7rem 3.4rem;
  margin-top: -0.4rem;
  font-size: 0.8rem;
  color: var(--warn);
}

/* --- Popover menu --------------------------------------------------------- */

.menu {
  position: fixed;
  z-index: 60;
  min-width: 12rem;
  padding: 0.3rem;
  border-radius: var(--r);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--sh-3);
  animation: drop 0.12s ease-out;
}
.menu__item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.45rem 0.55rem;
  border: 0;
  border-radius: var(--r-xs);
  background: none;
  font: inherit;
  font-size: 0.86rem;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.menu__item:hover { background: var(--surface-2); }
.menu__item svg { color: var(--muted); }
.menu__item--danger { color: var(--danger); }
.menu__item--danger svg { color: var(--danger); }
.menu__sep { height: 1px; margin: 0.3rem 0.2rem; background: var(--line); }

/* --- Modals --------------------------------------------------------------- */

.modal {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(86vh, 56rem);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--sh-3);
  overflow: hidden;
}
.modal--wide { width: min(56rem, calc(100vw - 2rem)); }
.modal[open] { display: flex; flex-direction: column; animation: pop 0.18s cubic-bezier(0.22, 1, 0.36, 1); }
.modal::backdrop {
  background: rgba(8, 11, 17, 0.45);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

@keyframes pop {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

.modal__head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1.05rem 1.15rem 0.85rem;
  border-bottom: 1px solid var(--line);
}
.modal__head div { flex: 1 1 auto; min-width: 0; }
.modal__head .sub { font-size: 0.83rem; color: var(--muted); margin-top: 0.15rem; }

.modal__body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 1.15rem; }
.modal__foot {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  justify-content: flex-end;
  padding: 0.85rem 1.15rem;
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.modal__foot .spacer { flex: 1 1 auto; }

/* --- Toasts --------------------------------------------------------------- */

.toasts {
  position: fixed;
  z-index: 80;
  right: 1rem;
  bottom: 1rem;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column-reverse;
  gap: 0.5rem;
  max-width: min(26rem, calc(100vw - 2rem));
  pointer-events: none;

  /* It is a popover so that it can rise above an open modal dialog, which the
     top layer puts beyond the reach of any z-index. That brings the browser's
     own popover furniture -- a border, padding, a background, and inset: 0 --
     none of which belongs on a stack of floating cards. */
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
  inset: auto 1rem calc(1rem + env(safe-area-inset-bottom)) auto;
  width: auto;
  height: auto;
  color: inherit;
}
/* A popover is display:none until it is shown, and the host is only shown when
   there is something in it. Nothing else needs the empty box on the page. */
.toasts:not(:popover-open):empty { display: none; }
.toast {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.7rem 0.8rem;
  border-radius: var(--r);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--sh-3);
  font-size: 0.86rem;
  pointer-events: auto;
  animation: slidein 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.toast svg { flex: none; margin-top: 0.1rem; }
.toast__body { flex: 1 1 auto; min-width: 0; }
.toast__body ul { margin: 0.3rem 0 0; padding-left: 1rem; color: var(--muted); }
.toast--ok svg { color: var(--ok); }
.toast--warn svg { color: var(--warn); }
.toast--danger svg { color: var(--danger); }
.toast.is-going { animation: fadeout 0.2s ease-in forwards; }

@keyframes slidein {
  from { opacity: 0; transform: translateX(16px); }
  to { opacity: 1; transform: none; }
}
@keyframes fadeout {
  to { opacity: 0; transform: translateX(16px); }
}

/* --- Loading -------------------------------------------------------------- */

.spinner {
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  animation: spin 0.7s linear infinite;
  flex: none;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  height: 100%;
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- Narrow screens ------------------------------------------------------- */

/* The console is a desk tool, but a head of transport does open it on a tablet
   in a car park. Below this the workspace stops being two columns and becomes
   map-then-detail, which keeps every control reachable without inventing a
   second navigation model for one screen size. */
@media (max-width: 60rem) {
  .crumb span, .sep { display: none; }
  .crumb { padding: 0 0.45rem; }
  select.pillselect { max-width: 11rem; }

  .workspace {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 45vh minmax(0, 1fr);
  }
  .canvas { grid-row: 1; }
  .sidebar { grid-row: 2; border-right: 0; border-top: 1px solid var(--line); }
  .banner { top: 0.6rem; }

  /* The school page collapses the other way round: the section rail becomes a
     strip along the top that scrolls sideways, because it is navigation and
     putting navigation below the thing it navigates reads as a footnote. */
  .workspace--sections { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .workspace--sections .sidebar {
    grid-row: 1;
    border-top: 0;
    border-bottom: 1px solid var(--line);
  }
  .workspace--sections .sectionnav {
    display: flex;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding: 0;
  }
  .workspace--sections .sectionnav .rowitem {
    width: auto;
    flex: none;
    white-space: nowrap;
    border-top: 0;
  }
  .workspace--sections .sectionnav .rowitem[aria-current='page'] {
    box-shadow: inset 0 -3px 0 var(--accent);
  }
}

@media (max-width: 40rem) {
  .appbar { gap: 0.35rem; padding: 0 0.5rem; }
  .appbar__title { font-size: 0.88rem; }
  .sheet { padding: 1.25rem 0.85rem 4rem; }
  .stats { grid-template-columns: 1fr; }
  .seg__opt { padding: 0 0.55rem; font-size: 0.78rem; }
  .seg__long { display: none; }
  .seg__short { display: inline; }
}
