/* TrafficDesk — operational interface.
   Hand-written: this server has no Node, so there is no build step.
   Dense by design. Traffic reads this for eight hours; the data gets the
   pixels and the chrome gets out of the way. */

:root{
  --bg:#eef0ee; --surface:#fff; --surface-2:#f5f6f4; --surface-3:#e7eae7;
  --ink:#141c24; --ink-2:#43505c; --muted:#6d7a84;
  --line:#d3d8d4; --line-2:#e4e8e4;
  --accent:#0c6870; --accent-2:#e2efef; --accent-ink:#fff;
  --ok:#3f6b34; --ok-bg:#e7f0e3;
  --warn:#8f6310; --warn-bg:#fbf1dc;
  --bad:#9c3722; --bad-bg:#f8e6e1;
  --mono:"JetBrains Mono",ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  --row:28px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:13px;line-height:1.45;-webkit-font-smoothing:antialiased}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code,.mono,.num{font-family:var(--mono);font-variant-numeric:tabular-nums}

/* ---------- shell ---------- */
.app{display:grid;grid-template-columns:190px 1fr;min-height:100vh}
.side{background:#12202a;color:#c3ced4;display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
.side .brand{padding:14px 16px;border-bottom:1px solid #1f3240;display:flex;align-items:center;gap:9px}
.side .brand img{height:24px}
.side .brand b{color:#fff;font-size:14px;letter-spacing:-.2px}
.side nav{padding:8px 0;flex:1;overflow-y:auto}
.side nav a{display:block;padding:7px 16px;color:#a9b6bd;font-size:13px;border-left:2px solid transparent}
.side nav a:hover{background:#182935;color:#fff;text-decoration:none}
.side nav a.on{background:#182935;color:#fff;border-left-color:var(--accent)}
.side nav .grp{padding:12px 16px 4px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:#5f7180}
.side .me{padding:10px 16px;border-top:1px solid #1f3240;font-size:12px}
.side .me b{color:#fff;display:block}
.side .me a{color:#7f8f9a;font-size:11px}
.main{min-width:0;display:flex;flex-direction:column}

/* ---------- top bar ---------- */
.top{background:var(--surface);border-bottom:1px solid var(--line);padding:0 16px;height:44px;display:flex;align-items:center;gap:14px;position:sticky;top:0;z-index:20}
.top h1{font-size:15px;font-weight:600;margin:0;letter-spacing:-.2px}
.top .sub{color:var(--muted);font-size:12px}
.top .right{margin-left:auto;display:flex;gap:8px;align-items:center}
.page{padding:14px 16px 40px;flex:1}

/* ---------- collapsing the sidebar ---------- */
/* Only on a wide screen. Below 820px the sidebar is already a horizontal
   strip, and hiding that leaves a guide on a phone with no way to sign out —
   which is why the strip exists at all. */
.navtoggle{display:none}
@media (min-width:821px){
  .navtoggle{display:inline-flex;align-items:center;justify-content:center;
    width:26px;height:26px;margin-inline-start:-4px;padding:0;border:1px solid var(--line);
    background:var(--surface);color:var(--muted);border-radius:2px;cursor:pointer;
    font-size:13px;line-height:1;font-family:inherit}
  .navtoggle:hover{background:var(--surface-2);color:var(--ink)}
  /* One track, not a zero-width one: display:none takes the sidebar out of
     the grid altogether, so a 0 1fr leaves the main content sitting in the
     empty first column instead of filling the screen. */
  .nav-closed .app{grid-template-columns:1fr}
  .nav-closed .side{display:none}
}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;border:1px solid var(--line);background:var(--surface);
  color:var(--ink);border-radius:2px;font-size:12.5px;font-family:inherit;cursor:pointer;line-height:1.4}
.btn:hover{background:var(--surface-2);text-decoration:none}
.btn.pri{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:500}
.btn.pri:hover{filter:brightness(1.08)}
.btn.danger{border-color:var(--bad);color:var(--bad)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn.sm{padding:3px 8px;font-size:11.5px}
/* Buttons under each other, filling their column. Four of them in a row widen
   a grid past the screen and put the actions behind a sideways scroll. */
.btn-stack{display:flex;flex-direction:column;gap:3px}
.btn-stack .btn{justify-content:center;padding-left:6px;padding-right:6px}
/* A column heading that sorts. It has to read as the heading it replaced, so
   it borrows the heading's type and colour and shows its hand on hover. */
.sort{background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;
  letter-spacing:inherit;text-transform:inherit;cursor:pointer;text-align:inherit}
.sort:hover{text-decoration:underline}
.sort span{font-size:9px;margin-inline-start:3px;vertical-align:middle}

/* ---------- forms ---------- */
label{display:block;font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:3px}
input[type=text],input[type=password],input[type=date],input[type=search],input[type=number],select,textarea{
  width:100%;padding:5px 8px;border:1px solid var(--line);border-radius:2px;background:var(--surface);
  color:var(--ink);font-family:inherit;font-size:13px}
input:focus,select:focus,textarea:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
input[type=file]{font-size:12px}
.field{margin-bottom:12px}
.err{color:var(--bad);font-size:11.5px;margin-top:3px}

/* ---------- filter bar ---------- */
.filters{background:var(--surface);border:1px solid var(--line);padding:9px 11px;margin-bottom:10px;
  display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end}
.filters .f{min-width:120px}
.filters .f.wide{min-width:220px;flex:1}
.filters label{margin-bottom:2px}

/* ---------- tables: the point of the whole product ---------- */
.tw{background:var(--surface);border:1px solid var(--line);overflow-x:auto}
table.grid{width:100%;border-collapse:collapse;font-size:12.5px}
table.grid th{position:sticky;top:0;z-index:5;background:var(--surface-3);color:var(--ink-2);
  text-align:left;font-weight:600;font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
  padding:6px 9px;border-bottom:1px solid var(--line);white-space:nowrap}
table.grid td{padding:0 9px;height:var(--row);border-bottom:1px solid var(--line-2);white-space:nowrap;vertical-align:middle}
table.grid tbody tr:hover{background:var(--accent-2)}
table.grid tbody tr:nth-child(even){background:var(--surface-2)}
table.grid tbody tr:nth-child(even):hover{background:var(--accent-2)}
table.grid td.n{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums}
table.grid td.c{text-align:center}
table.grid td.k{font-family:var(--mono);font-size:12px}
table.grid td.grow{white-space:normal;min-width:180px}
table.grid .dim{color:var(--muted)}
.empty{padding:28px;text-align:center;color:var(--muted);font-size:13px}

/* ---------- chips ---------- */
.chip{display:inline-block;padding:1px 6px;border-radius:2px;font-size:10.5px;font-family:var(--mono);
  letter-spacing:.03em;border:1px solid var(--line);background:var(--surface-2);color:var(--ink-2);white-space:nowrap}
.chip.ok{background:var(--ok-bg);border-color:#bcd3b3;color:var(--ok)}
.chip.warn{background:var(--warn-bg);border-color:#e6d2a4;color:var(--warn)}
.chip.bad{background:var(--bad-bg);border-color:#e8bfb4;color:var(--bad)}
.chip.acc{background:var(--accent-2);border-color:#a9cfd0;color:var(--accent)}

/* ---------- cards / stats ---------- */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;background:var(--line);
  border:1px solid var(--line);margin-bottom:12px}
.cards>div{background:var(--surface);padding:9px 12px}
.cards dt{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.cards dd{margin:3px 0 0;font-size:19px;font-weight:600;font-family:var(--mono);font-variant-numeric:tabular-nums}
.cards dd small{font-size:11px;color:var(--muted);font-weight:400}
.panel{background:var(--surface);border:1px solid var(--line);margin-bottom:12px}
.panel>h2{margin:0;padding:8px 12px;font-size:12px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ink-2);background:var(--surface-3);border-bottom:1px solid var(--line);font-weight:600}
.panel .body{padding:11px 12px}
.panel .body p{margin:0 0 8px}

/* ---------- notices ---------- */
.note{padding:8px 11px;border:1px solid var(--line);border-left:3px solid var(--muted);
  background:var(--surface);margin-bottom:10px;font-size:12.5px}
.note.ok{border-left-color:var(--ok);background:var(--ok-bg)}
.note.warn{border-left-color:var(--warn);background:var(--warn-bg)}
.note.bad{border-left-color:var(--bad);background:var(--bad-bg)}
.note b{display:block;margin-bottom:2px}

/* ---------- login ---------- */
.login{min-height:100vh;display:grid;place-items:center;background:#12202a;padding:20px}
.login .box{background:var(--surface);border:1px solid var(--line);width:100%;max-width:330px;padding:24px}
.login .brand{display:flex;align-items:center;gap:10px;margin-bottom:4px}
.login .brand img{height:30px}
.login h1{font-size:17px;margin:0 0 2px;letter-spacing:-.3px}
.login p.lead{color:var(--muted);font-size:12.5px;margin:0 0 18px}
.login .btn{width:100%;justify-content:center;padding:7px}

/* ---------- misc ---------- */
.pager{display:flex;gap:8px;align-items:center;padding:8px 2px;font-size:12px;color:var(--muted)}
.pager .nums{margin-left:auto;display:flex;gap:4px}
.bar{height:4px;background:var(--surface-3);border:1px solid var(--line);width:90px;display:inline-block;vertical-align:middle}
.bar i{display:block;height:100%;background:var(--accent)}
.right{text-align:right}
.spread{display:flex;gap:10px;align-items:center}
.spread .pull{margin-left:auto}
.wrap-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}

/* ---------- phone ---------- */
@media (max-width:820px){
  .app{grid-template-columns:1fr}
  .side{position:static;height:auto;flex-direction:row;flex-wrap:wrap;align-items:center}
  .side nav{display:flex;flex-wrap:wrap;padding:0;flex:1 100%;border-top:1px solid #1f3240}
  .side nav .grp{display:none}
  .side nav a{border-left:none;border-bottom:2px solid transparent;padding:8px 12px}
  .side nav a.on{border-left:none;border-bottom-color:var(--accent)}
  .side .me{flex:1 100%;border-top:1px solid #1f3240}
  .filters .f,.filters .f.wide{min-width:calc(50% - 4px)}
  .page{padding:10px}
}

/* ------------------------------------------------------------------
   Field screens — a guide reads these one-handed, on a phone, outside,
   at four in the morning. Cards, big type, nothing to zoom into.
   ------------------------------------------------------------------ */

.field { max-width: 720px; margin: 0 auto; }

.field .daybar {
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: 0; z-index: 3;
    background: var(--bg, #fff); padding: 10px 0; border-bottom: 1px solid var(--line, #e3e6ea);
}
.field .daybar b { font-size: 17px; flex: 1; }
.field .daybar button {
    min-width: 44px; min-height: 44px; font-size: 18px;
    border: 1px solid var(--line, #e3e6ea); background: #fff; border-radius: 8px; cursor: pointer;
}

.runcard { border: 1px solid var(--line, #e3e6ea); border-radius: 10px; margin: 12px 0; overflow: hidden; }
.runcard > header {
    background: #254060; color: #fff; padding: 10px 12px;
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
.runcard > header .code { font-size: 17px; font-weight: 700; }
.runcard > header .bus { margin-left: auto; font-size: 13px; opacity: .85; }

.runcard .meta {
    display: flex; flex-wrap: wrap; gap: 6px 16px;
    padding: 10px 12px; font-size: 13px; border-bottom: 1px solid var(--line, #e3e6ea);
}
.runcard .meta span b { display: block; font-size: 11px; color: var(--muted, #6b7280); font-weight: 400; }

.runcard ol { list-style: none; margin: 0; padding: 0; }
.runcard ol li {
    display: flex; gap: 12px; align-items: baseline;
    padding: 10px 12px; border-bottom: 1px solid var(--line, #eef0f3);
}
.runcard ol li .at { font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; min-width: 56px; }
.runcard ol li .where { flex: 1; font-size: 14px; }
.runcard ol li .n { font-size: 14px; color: var(--muted, #6b7280); }

.runcard .say { padding: 10px 12px; }
.runcard .say textarea {
    width: 100%; min-height: 68px; font: inherit; font-size: 15px;
    padding: 8px; border: 1px solid var(--line, #e3e6ea); border-radius: 8px; resize: vertical;
}
.runcard .say button { min-height: 44px; margin-top: 8px; }
.runcard .said { padding: 8px 12px; font-size: 13px; color: var(--muted, #6b7280); }
.runcard .said i { font-style: normal; color: #254060; }

/* Always reachable, at every width. */
.top .who { display: flex; align-items: center; gap: 8px; }
.top .who span { font-size: 12px; color: var(--muted, #6b7280); }
.top .who button {
    font: inherit; font-size: 12px; cursor: pointer;
    background: #fff; border: 1px solid var(--line, #e3e6ea);
    border-radius: 6px; padding: 6px 10px; min-height: 32px;
}
.side .me form button {
    font: inherit; font-size: 12px; cursor: pointer;
    background: none; border: 0; padding: 0; color: inherit; text-decoration: underline;
}

@media (max-width: 640px) {
    /* The sidebar becomes a strip you scroll sideways rather than vanishing:
       hiding it left a guide on a phone with no way to sign out. */
    .app { display: block; }
    .app > .side {
        position: static; width: auto; height: auto;
        display: flex; flex-direction: row; align-items: center; gap: 4px;
        overflow-x: auto; white-space: nowrap; padding: 8px 10px;
    }
    .side .brand { margin: 0 6px 0 0; padding: 0 6px 0 0; border-bottom: 0; }
    .side .brand b { display: none; }
    .side nav { display: flex; flex: 0 0 auto; padding: 0; gap: 4px; align-items: center; overflow: visible; }
    .side nav .grp { display: none; }
    .side nav a { padding: 8px 12px; min-height: 40px; display: flex; align-items: center; }
    .side .me { display: none; }
    .main { margin: 0; }
    .top { height: auto; min-height: 44px; padding: 6px 12px; flex-wrap: wrap; gap: 6px 10px; }
    .top .sub { display: none; }
    .field { padding: 0 12px 40px; }
    .runcard ol li .at { min-width: 50px; }
}

/* ------------------------------------------------------------------
   Arabic. English stays the default and every printed document stays
   English; this only turns the screen around for whoever chose it.
   ------------------------------------------------------------------ */

[dir="rtl"] { text-align: right; }
[dir="rtl"] .app { direction: rtl; }
[dir="rtl"] .side nav a { border-left: 0; border-right: 2px solid transparent; }
[dir="rtl"] .side nav a.on { border-right-color: var(--accent); }
[dir="rtl"] .top .right { margin-left: 0; margin-right: auto; }
[dir="rtl"] .runcard > header .bus { margin-left: 0; margin-right: auto; }

/* Numbers, times, flight codes and hotel names stay left-to-right inside an
   Arabic page: a pickup at 23:45 for flight SU 901 is not an Arabic phrase,
   and mirroring it makes it unreadable. */
[dir="rtl"] .at,
[dir="rtl"] .n,
[dir="rtl"] td.n,
[dir="rtl"] .runcard ol li .at,
[dir="rtl"] .chip,
[dir="rtl"] input[type="date"],
[dir="rtl"] input[type="time"],
[dir="rtl"] input[type="number"] { direction: ltr; text-align: left; }

[dir="rtl"] .grid th, [dir="rtl"] .grid td { text-align: right; }
[dir="rtl"] .grid th.right, [dir="rtl"] .grid td.n { text-align: left; }

.langpick { margin-top: 14px; text-align: center; }
.langpick select { font-size: 12px; padding: 4px 8px; }

/* A hotel name, a bus number or a time is not an Arabic phrase. <bdi> isolates
   each one so "Jaz Mirabel Beach 5*" keeps its star at the end and "37 / 50"
   does not read as "50 / 37" on an Arabic page. */
bdi { unicode-bidi: isolate; }

/* ── Arabic typography ────────────────────────────────────────────────────
   Arabic was falling through the Latin stack to whatever serif the browser
   keeps for the script, at a size and a tracking chosen for capitals. Three
   things had to change, and only the third is the font.

   1. letter-spacing. Arabic letters join. Tracking pulls them apart, so the
      uppercase labels, table heads and chips were rendering as loose strings
      of disconnected glyphs. Tracking is removed outright on an Arabic page.
   2. size and leading. The small caps sizes (10px, 10.5px, 11px) carry
      Latin fine and Arabic not at all — the marks above and below the line
      collide. Everything small goes up a point and the page breathes at 1.75.
   3. the face itself. IBM Plex Sans Arabic reads at small sizes and its
      digits sit at the same weight as the Latin ones, which matters in a
      table that mixes 'الغردقة' with 'SU 901'. It loads only on the Arabic
      page; the fallbacks below are the good system faces, in the order each
      platform actually has one. */

[dir="rtl"] {
  --sans: "IBM Plex Sans Arabic", -apple-system, BlinkMacSystemFont, "Segoe UI",
          "Noto Sans Arabic", "Noto Naskh Arabic UI", "Geeza Pro", Tahoma, Arial, sans-serif;
  font-size: 13.5px;
}

[dir="rtl"] body { font-size: 13.5px; line-height: 1.75; }

/* Nothing on an Arabic page is tracked, and nothing is upper-cased: Arabic
   has no case, so text-transform only ever hurt the Latin words mixed in.
   !important because the tracked rules above are class selectors and a bare
   universal one loses to them; this only ever fires on an Arabic page. */
[dir="rtl"] * { letter-spacing: normal !important; }
[dir="rtl"] .side nav .grp,
[dir="rtl"] label,
[dir="rtl"] table.grid th,
[dir="rtl"] .cards dt,
[dir="rtl"] .panel > h2 { text-transform: none; }

/* The small sizes, one point up each. */
[dir="rtl"] .side nav .grp { font-size: 11.5px; }
[dir="rtl"] label { font-size: 12px; }
[dir="rtl"] table.grid { font-size: 13px; }
[dir="rtl"] table.grid th { font-size: 12px; font-weight: 600; }
[dir="rtl"] .cards dt { font-size: 11.5px; }
[dir="rtl"] .chip { font-size: 11.5px; padding: 1px 7px; }
[dir="rtl"] .side .me { font-size: 12.5px; }
[dir="rtl"] .side .me a { font-size: 12px; }
[dir="rtl"] .top .sub { font-size: 12.5px; }
[dir="rtl"] .btn { font-size: 13px; }
[dir="rtl"] .btn.sm { font-size: 12.5px; }
[dir="rtl"] .err { font-size: 12.5px; }
[dir="rtl"] .pager { font-size: 12.5px; }
[dir="rtl"] .panel > h2 { font-size: 13px; }

/* A chip holding an Arabic word has no business in a code font; a chip
   holding a flight code still does, and those carry .k. */
[dir="rtl"] .chip { font-family: var(--sans); }
[dir="rtl"] .chip.k, [dir="rtl"] td.k, [dir="rtl"] .mono { font-family: var(--mono); }

/* Rows were sized for a 13px Latin line. Arabic ascenders and descenders
   need the extra two pixels or the grid clips them. */
[dir="rtl"] { --row: 32px; }
[dir="rtl"] table.grid td { line-height: 1.6; }
