/*
 * RTL-first. Every directional property is logical (inline-start/end, not
 * left/right), so adding an English locale later is a `dir` attribute change
 * rather than a stylesheet rewrite.
 */

:root {
  --bg: #0f1417;
  --surface: #171e23;
  --surface-2: #1e272d;
  --border: #2a363e;
  --text: #e6edf1;
  --muted: #8ba0ad;
  --accent: #2f9e6e;
  --danger: #d9483b;
  --warn: #d9922b;
  --selected: #1d2c26;
  --radius: 10px;
  /* Cairo is a progressive enhancement. The fallbacks all ship with Arabic
     coverage on their platforms, so the UI is legible if it never loads. */
  --font: 'Cairo', 'Segoe UI', Tahoma, 'Noto Sans Arabic', 'Geeza Pro', system-ui, sans-serif;
}

/*
 * Light theme. Dispatchers in a control room want the dark one; anyone
 * checking a truck on a phone in Libyan daylight wants this.
 *
 * Only the tokens change - every rule below is written against them, so the
 * two themes cannot drift apart.
 */
:root[data-theme='light'] {
  --bg: #eef2f5;
  --surface: #ffffff;
  --surface-2: #f4f7f9;
  --border: #cfdae1;
  --text: #14212a;
  --muted: #566c7a;
  --accent: #1f7a52;
  --danger: #c23b2e;
  --warn: #a76d16;
  --selected: #e3f2ea;
}

/* Alarm and status tints are alpha over the surface, so they work in both. */
:root[data-theme='light'] .pill-ok { color: #14603f; }
:root[data-theme='light'] .pill-danger { color: #9b2f24; }
:root[data-theme='light'] .pill-warn { color: #8a5a10; }

* { box-sizing: border-box; }

/*
 * The `hidden` attribute gets its display:none from the UA stylesheet, which
 * ANY author rule setting display will beat - so `.modal { display: grid }`
 * silently defeated `hidden` and left the unlock dialog permanently on top of
 * the page. Make the attribute authoritative.
 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  /* The page must never scroll sideways; panes scroll inside themselves. */
  overflow-x: hidden;
}

/* Latin numerals for technical data, even in Arabic text. */
body { font-variant-numeric: lining-nums tabular-nums; }

h1, h2, h3 { margin: 0 0 .5rem; font-weight: 700; }
h2 { font-size: 1.05rem; }
h3 { font-size: .9rem; margin-top: 1.5rem; color: var(--muted); }

.muted { color: var(--muted); font-size: .85rem; }
.ltr-inline { direction: ltr; display: inline-block; unicode-bidi: isolate; }

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

.btn {
  font-family: var(--font);
  font-size: .9rem;
  font-weight: 600;
  padding: .55rem 1.1rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  transition: filter .15s, background .15s;
}
.btn:hover { filter: brightness(1.12); }
.btn:disabled { opacity: .45; cursor: not-allowed; filter: none; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-danger { background: var(--danger); color: #fff; }
.btn-ghost { background: transparent; color: var(--muted); border-color: var(--border); }
.btn-block { display: block; inline-size: 100%; margin-block: 1rem; }

input[type=password], input[type=text], input[type=search] {
  inline-size: 100%;
  padding: .6rem .8rem;
  margin-block: .35rem .9rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: .9rem;
}
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
label { font-size: .85rem; color: var(--muted); }

/* --- Login -------------------------------------------------------------- */

.login-screen {
  display: grid;
  place-items: center;
  block-size: 100vh;
  padding: 1rem;
}
.login-card {
  inline-size: min(360px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 2rem;
}
.error { color: var(--danger); font-size: .85rem; }

/* --- Layout ------------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .7rem 1.2rem;
  background: var(--surface);
  border-block-end: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: .6rem; font-weight: 700; }
.brand-mark { font-size: 1.2rem; }
.topbar-end { display: flex; align-items: center; gap: .8rem; }

.layout {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  block-size: calc(100vh - 57px);
  max-inline-size: 100%;
}
.layout:has(.detail:not([hidden])) { grid-template-columns: 320px minmax(0, 1fr) 360px; }

/*
 * Grid children default to min-width:auto, so a wide child - MapLibre's canvas
 * being the obvious one - forces its track past 1fr and pushes the whole grid
 * beyond the viewport, clipping the sidebar off the edge of the screen.
 */
.sidebar, .map-pane, .detail { min-inline-size: 0; }

.sidebar {
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  padding: 1rem;
  overflow-y: auto;
}
.sidebar-head { display: flex; align-items: baseline; justify-content: space-between; }

.map-pane { position: relative; }
#map { position: absolute; inset: 0; }

.detail {
  position: relative;
  background: var(--surface);
  border-inline-start: 1px solid var(--border);
  padding: 1.2rem;
  overflow-y: auto;
}
.detail-close {
  position: absolute;
  inset-block-start: .8rem;
  inset-inline-end: .8rem;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1rem;
  cursor: pointer;
}

/* --- Device list -------------------------------------------------------- */

.device-list { list-style: none; margin: 0; padding: 0; }
.device-item {
  padding: .75rem;
  margin-block-end: .5rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--muted);
  border-radius: var(--radius);
  cursor: pointer;
}
.device-item:hover { border-color: var(--accent); }
.device-item.selected { border-color: var(--accent); background: var(--selected); }
.device-item.is-unlocked { border-inline-start-color: var(--danger); }
.device-item.is-locked { border-inline-start-color: var(--accent); }
.device-item.is-offline { border-inline-start-color: var(--border); opacity: .65; }
.device-item .row { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }
.device-item .name { font-weight: 600; }

/* --- Pills and stats ---------------------------------------------------- */

.pill {
  font-size: .72rem;
  font-weight: 600;
  padding: .18rem .55rem;
  border-radius: 999px;
  white-space: nowrap;
}
.pill-ok { background: rgba(47,158,110,.18); color: #57c795; }
.pill-danger { background: rgba(217,72,59,.18); color: #f08279; }
.pill-warn { background: rgba(217,146,43,.18); color: #e8b264; }
.pill-muted { background: var(--surface-2); color: var(--muted); }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .6rem;
  margin-block: 1rem;
}
/* An odd number of stats would otherwise leave a hole beside the last one. */
.stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
.stat {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .6rem .7rem;
}
.stat-label { display: block; font-size: .72rem; color: var(--muted); }
.stat-value { display: block; font-size: 1.05rem; font-weight: 600; margin-block-start: .15rem; }

.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .45rem .9rem; margin: 0; font-size: .85rem; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }
.break { overflow-wrap: anywhere; }

.hw { margin-block-start: 1rem; }
.hw summary {
  cursor: pointer;
  color: var(--muted);
  font-size: .82rem;
  padding-block: .4rem;
}
.hw[open] summary { margin-block-end: .5rem; }

.alarms {
  margin-block: 1rem;
  padding: .7rem .9rem;
  background: rgba(217,72,59,.12);
  border: 1px solid rgba(217,72,59,.4);
  border-radius: var(--radius);
  font-size: .85rem;
}

/* --- Feeds -------------------------------------------------------------- */

.feed { list-style: none; margin: 0; padding: 0; font-size: .82rem; }
.feed li {
  padding: .55rem .7rem;
  margin-block-end: .4rem;
  background: var(--surface-2);
  border-radius: 8px;
  border-inline-start: 3px solid var(--border);
}
.feed li.ok { border-inline-start-color: var(--accent); }
.feed li.bad { border-inline-start-color: var(--danger); }
.feed li.pending { border-inline-start-color: var(--warn); }
/*
 * 'uncertain' is neither a milder failure nor a slower success — the command
 * may have executed and the platform cannot tell. Dashed rather than solid, so
 * an operator scanning the list reads an unfinished edge rather than a colour
 * that looks like progress.
 */
.feed li.uncertain {
  border-inline-start-color: var(--warn);
  border-inline-start-style: dashed;
}
.feed .when { color: var(--muted); font-size: .75rem; }

/*
 * Physical movement, shown apart from the command's status. "No evidence" is
 * deliberately not styled as an error: it is a statement about what we know.
 */
.feed .evidence { font-size: .76rem; margin-block-start: .25rem; }
.feed .evidence.yes { color: var(--accent); }
.feed .evidence.no { color: var(--warn); }
.feed .empty { color: var(--muted); }

/* --- Modal and toast ---------------------------------------------------- */

/*
 * Stacking order, deliberately: pages (45) sit over the console, modals (60)
 * over pages, toasts (70) over everything.
 *
 * Modals used to be 40 - BELOW pages - so "change password" opened behind the
 * devices page and only became visible once that page was closed.
 */
.modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.65);
  display: grid;
  place-items: center;
  z-index: 60;
  padding: 1rem;
}
.modal-card {
  inline-size: min(440px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 1.6rem;
}
.warn {
  background: rgba(217,146,43,.12);
  border: 1px solid rgba(217,146,43,.35);
  border-radius: var(--radius);
  padding: .7rem .9rem;
  font-size: .85rem;
  line-height: 1.7;
}
.modal-actions { display: flex; gap: .6rem; justify-content: flex-start; margin-block-start: 1rem; }

.toast {
  position: fixed;
  inset-block-end: 1.5rem;
  inset-inline-start: 50%;
  transform: translateX(50%);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .7rem 1.2rem;
  font-size: .88rem;
  z-index: 70;
}
.toast.ok { border-color: var(--accent); }
.toast.bad { border-color: var(--danger); }

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

.truck-marker {
  inline-size: 30px;
  block-size: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 15px;
  border: 2px solid #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,.5);
  cursor: pointer;
  background: var(--accent);
}
.truck-marker.unlocked { background: var(--danger); }
.truck-marker.offline { background: #55666f; }

.maplibregl-popup-content {
  background: var(--surface);
  color: var(--text);
  font-family: var(--font);
  border-radius: var(--radius);
  font-size: .85rem;
}
.maplibregl-popup-tip { border-block-start-color: var(--surface) !important; }

/* --- Responsive --------------------------------------------------------- */

@media (max-width: 900px) {
  .layout,
  .layout:has(.detail:not([hidden])) { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .sidebar { max-block-size: 30vh; }
  .detail {
    position: fixed;
    inset: 0;
    z-index: 30;
    border: none;
  }
}

select {
  inline-size: 100%;
  padding: .6rem .8rem;
  margin-block: .35rem .5rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: .9rem;
}

.lock-note {
  margin-block: .6rem 0;
  padding: .6rem .8rem;
  background: rgba(217,146,43,.12);
  border: 1px solid rgba(217,146,43,.35);
  border-radius: var(--radius);
  font-size: .8rem;
  line-height: 1.7;
  color: var(--muted);
}

/* The event log is the audit record, so give it room to breathe. */
#event-list li { padding: .65rem .8rem; }
#event-list .when { margin-block-start: .2rem; }

/* --- Arrival unlock form -------------------------------------------------- */

.arrival-form { margin-block-end: 1rem; }
.arrival-row { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.ltr-input { direction: ltr; text-align: start; }
.btn-xs { font-size: .72rem; padding: .2rem .55rem; }
#arrival-list .row { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }

/* --- Locations page ------------------------------------------------------- */

.page {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: var(--bg);
  padding: 1.5rem;
  overflow-y: auto;
}
.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-block-end: .5rem;
}
.page-intro { max-inline-size: 60ch; line-height: 1.8; }

.location-form {
  max-inline-size: 720px;
  margin-block: 1.2rem;
  padding: 1.2rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.locations-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-inline-size: 720px;
}
.locations-list li {
  padding: .75rem .9rem;
  margin-block-end: .5rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: .85rem;
}
.locations-list .row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
  margin-block-end: .25rem;
}
.locations-list .empty { color: var(--muted); }

/* --- External integration page --------------------------------------------
 *
 * The JSON block is forced LTR and left-aligned inside an otherwise RTL
 * console: it is machine data, and mirroring it puts the braces on the wrong
 * side of what is meant to be pasted somewhere else verbatim.
 */
.integration-endpoints { max-inline-size: 720px; margin-block: 1rem; }
.endpoint-list {
  list-style: none;
  margin: .5rem 0;
  padding: 0;
  font-size: .85rem;
  line-height: 2;
}
.endpoint-list code { direction: ltr; unicode-bidi: isolate; }

.page-head-sub { margin-block-start: 1.5rem; max-inline-size: 720px; }
.integration-controls { display: flex; gap: .4rem; align-items: center; }

.json-view {
  max-inline-size: 720px;
  max-block-size: 45vh;
  overflow: auto;
  margin: 0 0 1.5rem;
  padding: .9rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .78rem;
  line-height: 1.6;
  text-align: left;
  white-space: pre;
  /* Selectable for a manual copy when the clipboard API is unavailable. */
  user-select: text;
}

/* Arrow points where the vehicle is heading; upright circle when stopped. */
.truck-arrow { display: inline-block; font-size: 15px; line-height: 1; transition: transform .3s ease; }
.truck-marker.moving { background: var(--accent); }
/*
 * Stopped is a plain filled circle, with the glyph hidden rather than left
 * upright at .85. At rest the GPS reports whatever direction the vehicle last
 * happened to face, so an arrow there points somewhere meaningless - and it
 * read as "moving" to an operator looking at a parked truck.
 *
 * The Google adapter did this by swapping its symbol between
 * FORWARD_CLOSED_ARROW and CIRCLE. That adapter is gone; the behaviour it
 * established is the one to keep.
 */
.truck-marker:not(.moving) .truck-arrow { display: none; }

/* --- Trip history page ---------------------------------------------------- */

.history-controls {
  display: flex;
  align-items: end;
  gap: .8rem;
  flex-wrap: wrap;
  margin-block: 1rem;
}
.history-controls > div { min-inline-size: 180px; }
.history-controls select { margin-block-end: 0; }
.history-controls p { margin: 0 0 .5rem; }

#history-map {
  block-size: 55vh;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.history-feed { max-inline-size: 720px; }

.checkbox { display: flex; gap: .5rem; align-items: flex-start; font-size: .85rem; color: var(--muted); margin-block: .6rem; line-height: 1.6; }
.checkbox input { margin-block-start: .2rem; }

.settings-actions { margin-block: .8rem; }
#trk-current { margin-block-end: 1rem; }
#trk-current .row { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }

/* A position noticeably behind the vehicle, so nobody reads the marker as live. */
.lagging { color: var(--warn); }

.sublock-unlock { margin-block-start: .5rem; }

/*
 * Command row header: status on one side, the cancel control on the other.
 * The button only appears while a command is still waiting to be delivered,
 * so the row is a plain line of text most of the time.
 */
.cmd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}

/* --- Map marker labels ---------------------------------------------------- */

/*
 * The label sits below the glyph and must stay upright and unrotated while the
 * arrow turns. text-shadow gives the halo that map labels need to stay legible
 * over both roads and satellite imagery.
 *
 * There is deliberately NO `position` on .truck-marker here.
 *
 * It used to carry `position: relative`, to act as the containing block for
 * the absolutely positioned label below. But MapLibre positions its own
 * markers with `.maplibregl-marker { position: absolute }`, an identical
 * specificity to `.truck-marker` - and styles.css loads after maplibre-gl.css,
 * so ours won. That dropped every marker out of absolute positioning and into
 * normal flow, after a canvas several hundred pixels tall, and MapLibre's
 * transform then offset it from there: off the bottom of the map and clipped.
 *
 * The element was in the DOM with the right coordinates and full opacity, and
 * simply never visible.
 *
 * No rule is needed: MapLibre's `position: absolute` is itself a containing
 * block for the label. Do not add one back.
 */
.truck-label {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  margin-block-start: 3px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  color: #fff;
  text-shadow:
     0 0 3px #0f1417, 0 0 3px #0f1417,
     1px 1px 2px #0f1417, -1px -1px 2px #0f1417;
  pointer-events: none;
}

.checkline { display: flex; align-items: center; gap: .5rem; margin-block: .6rem; cursor: pointer; }
.checkline input { inline-size: auto; margin: 0; }

/*
 * Staleness is shown by fading, not hiding: an operator still needs to see
 * roughly where a vehicle was, they simply must not read it as current.
 */
/*
 * Dimmed, not erased. These were .6/.35, which read correctly over the pale
 * street basemap they were tuned against and disappeared entirely over
 * satellite imagery - where an operator reads a missing marker as "no
 * vehicle" rather than "old fix". That is a worse error than the one the
 * fading is here to prevent, and the panel's (موقع قديم) is the
 * authoritative signal in any case.
 */
.truck-marker.aging { opacity: .8; }
.truck-marker.stale { opacity: .6; }

/* Custom history window: only shown when "فترة محددة" is selected. */
.hist-custom { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.hist-custom input[type=datetime-local] {
  inline-size: 100%;
  padding: .5rem .7rem;
  margin-block-start: .35rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  font-size: .85rem;
  color-scheme: dark light;
}

/* Password read result, shown where the read was requested. */
.dev-pw { margin-block-start: .4rem; display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }


