/* Control room dashboard. Sits open all day on a wall-mounted monitor, so it is
   dark, dense, and readable from across a room. */

@import url('../shared/tokens.css');

html, body { height: 100%; }
body { display: flex; flex-direction: column; overflow: hidden; }

/* --- Sign in ------------------------------------------------------------- */

.signin {
  flex: 1;
  display: grid;
  place-items: center;
  padding: var(--space-6);
}

/* Both first-sign-in and ordinary sign-in are .signin panels, and only one is
   ever wanted. Hiding by default and revealing the right one avoids the flash
   of the wrong screen that setting display from script alone would give. */
.signin[hidden] {
  display: none;
}
.signin-card {
  width: min(26rem, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.brand { font-size: var(--text-2xl); font-weight: 700; letter-spacing: -0.02em; margin: 0; }
.brand span { color: var(--accent); }
.lede { color: var(--text-muted); margin: 0; font-size: var(--text-sm); line-height: 1.5; }

.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label { font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--text-muted); font-weight: 700; }

.button-primary {
  background: var(--accent-strong); color: #fff;
  min-height: 44px; font-weight: 600;
}
.button-primary:hover:not(:disabled) { background: var(--accent); }

.notice {
  padding: var(--space-3); border-radius: var(--radius-sm);
  font-size: var(--text-sm); background: var(--status-stale-soft); color: var(--status-stale);
}
.notice:empty { display: none; }

/* --- Shell --------------------------------------------------------------- */

.app { flex: 1; display: none; grid-template-rows: auto 1fr; min-height: 0; }
.app[data-active] { display: grid; }

.topbar {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
}
.topbar .brand { font-size: var(--text-lg); }
.company { color: var(--text-muted); font-size: var(--text-sm); }
.spacer { flex: 1; }

.connection {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: var(--space-2) var(--space-3); border-radius: 999px;
}
.connection::before { content: ''; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; }
.connection[data-state='open'] { background: var(--status-available-soft); color: var(--status-available); }
.connection[data-state='connecting'] { background: var(--status-busy-soft); color: var(--status-busy); }
.connection[data-state='offline'],
.connection[data-state='closed'] { background: var(--status-stale-soft); color: var(--status-stale); }

.button-ghost {
  border: 1px solid var(--border); background: transparent;
  color: var(--text-muted); padding: var(--space-2) var(--space-3); font-size: var(--text-sm);
}

.workspace { display: grid; grid-template-columns: 1fr 24rem; min-height: 0; }
@media (max-width: 60rem) { .workspace { grid-template-columns: 1fr; } }

#map { min-height: 0; background: var(--surface-base); }

.panel {
  border-left: 1px solid var(--border);
  background: var(--surface-raised);
  display: flex; flex-direction: column; min-height: 0;
}

.panel-section { padding: var(--space-4); border-bottom: 1px solid var(--border); }

/* The fleet takes whatever height is left and scrolls within it, so the
   dispatch form and the live jobs stay put as the roster grows. */
.panel-section-grow { flex: 1; min-height: 8rem; display: flex; flex-direction: column; }
.panel-section-grow .fleet { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }

/* Jobs are capped so a busy night cannot push the fleet off screen. The list
   scrolls within its own bounds rather than bleeding past the panel edge, which
   previously clipped the status badge off the right of every card. */
#jobs { max-height: 16rem; overflow-y: auto; overflow-x: hidden; }
.panel-title {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-muted); font-weight: 700; margin: 0 0 var(--space-3);
}

/* --- Fleet list ---------------------------------------------------------- */

.fleet { flex: 1; overflow-y: auto; min-height: 0; }

.driver-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: none; border-bottom: 1px solid var(--border);
  background: transparent; text-align: left; border-radius: 0;
}
.driver-row:hover { background: var(--surface-overlay); }
.driver-row[data-selected] {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.driver-row[data-availability='offline'] { opacity: 0.55; }

.dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; }
.driver-row[data-availability='available'] .dot { background: var(--status-available); }
.driver-row[data-availability='busy'] .dot { background: var(--status-busy); }
.driver-row[data-availability='on_break'] .dot { background: var(--status-break); }
.driver-row[data-availability='offline'] .dot { background: var(--status-offline); }

/* A break is dimmed, but less than offline: the driver is coming back, and the
   office should still be able to read where they are. */
.driver-row[data-availability='on_break'] { opacity: 0.8; }

.driver-name { font-weight: 600; font-size: var(--text-sm); }
.driver-meta { font-size: var(--text-xs); color: var(--text-muted); font-family: var(--font-mono); }

/* Staleness is stated, never implied. A marker shown as live when its fix is
   minutes old is worse than no marker at all. */
.stale-flag {
  font-size: var(--text-xs); font-weight: 700;
  color: var(--status-stale); background: var(--status-stale-soft);
  padding: 2px var(--space-2); border-radius: 999px;
}

/* --- Dispatch form ------------------------------------------------------- */

.dispatch-form { display: flex; flex-direction: column; gap: var(--space-3); }
.selected-driver {
  font-size: var(--text-sm); padding: var(--space-3);
  border-radius: var(--radius-sm); background: var(--surface-overlay);
  border: 1px dashed var(--border-strong); color: var(--text-muted);
}
.selected-driver[data-chosen] {
  border-style: solid; border-color: var(--accent); color: var(--text);
}

.fare-input { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.job-card {
  display: grid; gap: var(--space-1);
  padding: var(--space-3); border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
  min-width: 0;
}
.job-card > * { min-width: 0; }
.job-card-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
.job-status {
  flex: none;
  font-size: var(--text-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; padding: 2px var(--space-2); border-radius: 999px;
  background: var(--surface-overlay); color: var(--text-muted);
}
.job-status[data-status='accepted'],
.job-status[data-status='arrived'],
.job-status[data-status='on_board'] { background: var(--status-busy-soft); color: var(--status-busy); }
.job-status[data-status='offered'] { background: var(--accent-soft); color: var(--accent); }
.job-status[data-status='completed'] { background: var(--status-available-soft); color: var(--status-available); }
.job-status[data-status='cancelled'],
.job-status[data-status='rejected'],
.job-status[data-status='timed_out'] { background: var(--status-stale-soft); color: var(--status-stale); }

.job-route { color: var(--text-muted); font-size: var(--text-xs); }
.job-journey {
  color: var(--text);
  font-size: var(--text-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.track-link { color: var(--accent); font-size: var(--text-xs); text-decoration: none; }
.track-link:hover { text-decoration: underline; }

.empty { color: var(--text-muted); font-size: var(--text-sm); padding: var(--space-4); text-align: center; }

/* --- Map markers --------------------------------------------------------- */

.marker {
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  border: 2px solid var(--surface-base);
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: transform 120ms ease;
}
.marker:hover { transform: scale(1.15); }
.marker[data-availability='available'] { background: var(--status-available); }
.marker[data-availability='busy'] { background: var(--status-busy); }
.marker[data-availability='on_break'] { background: var(--status-break); }
.marker[data-availability='offline'] { background: var(--status-offline); }
.marker[data-stale='true'] { border-color: var(--status-stale); border-style: dashed; }
.marker[data-selected='true'] { box-shadow: 0 0 0 4px var(--accent); }

.marker-arrow {
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 11px solid var(--surface-base);
}

.mapboxgl-popup-content {
  background: var(--surface-overlay) !important;
  color: var(--text) !important;
  border-radius: var(--radius-sm) !important;
  font-family: var(--font);
  font-size: var(--text-sm);
}
.mapboxgl-popup-tip { border-top-color: var(--surface-overlay) !important; }

.map-fallback {
  height: 100%; display: grid; place-items: center;
  padding: var(--space-6); text-align: center; color: var(--text-muted);
}

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

.combobox { position: relative; }

/* A resolved field is visibly different from one holding unconfirmed text,
   because the difference decides whether a job can be sent at all. */
.combobox input[data-resolved] {
  border-color: var(--status-available);
  box-shadow: inset 3px 0 0 var(--status-available);
}

.suggestions {
  display: none;
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  max-height: 17rem;
  overflow-y: auto;
  background: var(--surface-overlay);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
}
.suggestions[data-open] { display: block; }

.suggestion {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.suggestion[data-active] { background: var(--accent-soft); }

.suggestion-name { font-size: var(--text-sm); font-weight: 600; }
.suggestion-context {
  font-size: var(--text-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The line under each field. It states what is resolved rather than leaving
   the dispatcher to infer it from the absence of an error. */
.field-status {
  margin: 0;
  font-size: var(--text-xs);
  min-height: 1rem;
}
.field-status[data-state='idle'] { color: var(--text-muted); }
.field-status[data-state='pending'] { color: var(--status-busy); }
.field-status[data-state='resolved'] {
  color: var(--status-available);
  font-family: var(--font-mono);
}
.field-status[data-state='error'] { color: var(--status-stale); }

.pickup-pin {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--surface-base);
  box-shadow: var(--shadow);
}
.destination-pin {
  width: 18px;
  height: 18px;
  border-radius: 3px;
  background: var(--status-available);
  border: 3px solid var(--surface-base);
  box-shadow: var(--shadow);
}

/* --- Jobs waiting on the office ------------------------------------------ */

/* A refused or lapsed job belongs to nobody. It stays on screen, and looks
   unlike the jobs that are running fine, until a dispatcher moves it on. */
.job-card[data-attention] {
  background: var(--status-stale-soft);
  box-shadow: inset 3px 0 0 var(--status-stale);
}

.reassign { display: flex; gap: var(--space-2); margin-top: var(--space-1); }

.reassign-driver {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2);
  background: var(--surface-input);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

.reassign-send {
  background: var(--accent-strong);
  color: #fff;
  font-size: var(--text-sm);
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
}
.reassign-send:hover:not(:disabled) { background: var(--accent); }

.reassign-empty {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-style: italic;
}

/* A count on the heading, so a refused job at the bottom of a busy list is not
   something a dispatcher has to notice for themselves. */
.attention-count {
  margin-left: var(--space-2);
  padding: 1px var(--space-2);
  border-radius: 999px;
  background: var(--status-stale);
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0;
}
.attention-count:empty { display: none; }

/* --- View tabs ----------------------------------------------------------- */

/* One attribute on the shell decides which screen is up. Both screens set their
   own display, so the hidden attribute cannot do it — an author rule beats the
   user agent's [hidden] { display: none } and leaves them stacked. */
.app[data-view='history'] .workspace { display: none; }
.app:not([data-view='history']) .history { display: none; }

.views { display: flex; gap: var(--space-1); margin-left: var(--space-2); }

.view-tab {
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
}
.view-tab:hover { color: var(--text); background: var(--surface-overlay); }
.view-tab[data-active] {
  color: var(--text);
  background: var(--accent-soft);
  border-color: var(--accent);
}

/* --- Shift report -------------------------------------------------------- */

.history {
  min-height: 0;
  transition: opacity 120ms ease;
  display: flex;
  flex-direction: column;
  padding: var(--space-4);
  gap: var(--space-4);
  overflow-y: auto;
}

.history-bar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.range-presets { display: flex; gap: var(--space-1); flex-wrap: wrap; }

.range-preset {
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
}
.range-preset:hover { color: var(--text); border-color: var(--border-strong); }

.range-dates { display: flex; gap: var(--space-3); }
.range-date { display: flex; flex-direction: column; gap: var(--space-1); }
.range-date span {
  font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--text-muted); font-weight: 700;
}
.range-date input { font-family: var(--font-mono); }

/* The number the fleet owner came for, sized so it reads from across a desk. */
.history-headline {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--status-available);
  white-space: nowrap;
}
.history-headline:empty { display: none; }

.range-label {
  margin-left: var(--space-2);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  color: var(--text-muted);
}

.history-body { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }

.history-panel {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  min-width: 0;
}

/* A wide table scrolls inside its own panel rather than pushing the page
   sideways, which on a narrow screen would hide the earnings column — the one
   column nobody can do without. */
.history-panel > div { overflow-x: auto; }

.report-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.report-table th {
  text-align: left;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  font-weight: 700;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.report-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.report-table tbody tr:last-child td { border-bottom: none; }
.report-table tbody tr:hover { background: var(--surface-overlay); }

.report-table .mono { font-family: var(--font-mono); white-space: nowrap; }
.report-table th.number,
.report-table td.number { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.report-table .earnings { font-weight: 600; }
.report-table .journey { color: var(--text-muted); min-width: 14rem; }

.totals-row td {
  border-top: 1px solid var(--border-strong);
  border-bottom: none;
  font-weight: 700;
  background: var(--surface-overlay);
}
.totals-row .earnings { color: var(--status-available); }

.hint {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.5;
}

#history-more { margin-top: var(--space-3); width: 100%; }

/* While a new window is loading, the figures on screen belong to the old one.
   Dimming them says so, rather than letting them read as current. */
.history[data-loading] { opacity: 0.45; pointer-events: none; }

/* --- Calling a job off ---------------------------------------------------- */

.cancel { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-2); }

/* Understated until it is asked for. Calling a job off is not what a dispatcher
   is mostly doing, and a red button on every card would make the panel read as
   a list of problems. */
.cancel-open {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-3);
}
.cancel-open:hover { color: var(--danger); border-color: var(--danger); }

.cancel[data-confirming] { flex-wrap: wrap; }

.cancel-reason {
  flex: 1;
  min-width: 0;
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-2);
}

.cancel-confirm {
  background: var(--danger-strong);
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 600;
  padding: var(--space-1) var(--space-3);
}
.cancel-confirm:hover:not(:disabled) { background: var(--danger); }

.cancel-keep {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-3);
}
.cancel-keep:hover { color: var(--text); border-color: var(--border-strong); }
