:root {
  --brand-cyan: #17ABCB;
  --brand-cyan-dark: #128AA6;
  --brand-ink: #101820;
  --bg: #f4f7f9;
  --panel: #ffffff;
  --panel2: #eef2f5;
  --text: #101820;
  --muted: #64748b;
  --line: #d7dfe6;
  --ok: #16a34a;
  --ok-bg: #dcfce7;
  --bad: #dc2626;
  --bad-bg: #fee2e2;
  --warn: #d97706;
  --warn-bg: #fef3c7;
  /* RUN COLOURS — several runs on one map have to stay apart at a glance.
     Here rather than in the screen because they are brand-level choices, and
     because JavaScript reads them back with getComputedStyle: a palette
     hardcoded in a script is a second set of colours nobody would find. Chosen
     to differ in lightness as well as hue, so they survive a colour-blind
     reader and a printed page. */
  --run-1: #17ABCB;
  --run-2: #B45309;
  --run-3: #7C3AED;
  --run-4: #15803D;
  --run-5: #BE123C;
  --run-6: #0F172A;
  --radius: 12px;
  --shadow: 0 1px 3px rgba(16,24,32,.08), 0 1px 2px rgba(16,24,32,.04);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg); color: var(--text); line-height: 1.5;
  padding: 0 16px 40px; max-width: 900px; margin: 0 auto;
}
.brandbar {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 0; margin-bottom: 8px; border-bottom: 2px solid var(--brand-cyan);
}
.brandbar img { height: 40px; width: auto; display: block; }
.wordmark { font-size: 30px; font-weight: 800; letter-spacing: -0.5px; line-height: 1; }
.wordmark .top { color: var(--brand-cyan); }
.wordmark .gas { color: var(--brand-ink); }
.brandbar .screen-name { margin-left: auto; font-size: 14px; font-weight: 600; color: var(--muted); }

/* WHO IS SIGNED IN — added to the brand bar by /assets/session.js on every
   screen. `margin-left:auto` puts it at the right unless a .screen-name has
   already claimed that, in which case it simply follows it. */
.session-bar { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.session-bar .session-who { font-size: 14px; font-weight: 600; color: var(--brand-ink); }
/* A PIN session says so. "Why can I not see customers?" has exactly one answer
   on a truck phone, and it should not be a mystery. */
.session-bar .session-pin {
  font-size: 11px; font-weight: 800; letter-spacing: 0.5px;
  background: var(--brand-cyan); color: var(--brand-ink);
  border-radius: 999px; padding: 2px 8px;
}
h1 { font-size: 22px; margin: 16px 0 4px; color: var(--brand-ink); }
.sub { color: var(--muted); font-size: 14px; margin-bottom: 18px; }
.panel {
  background: var(--panel); border-radius: var(--radius); padding: 20px;
  margin-bottom: 16px; border: 1px solid var(--line); box-shadow: var(--shadow);
}
label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
/* Every text-ish input type gets the same treatment, so a form built from
   emails, phones and times looks like one form rather than three.

   ⚠️ `password` WAS MISSING UNTIL LOGIN EXISTED, and the list being explicit is
   why nobody noticed: there had never been a password field in TopGas, so the
   sign-in screen rendered its password box completely unstyled at desktop —
   19px tall, 13px text, no border — while looking fine on a phone, where the
   screen's own mobile block covered it. Found by MEASURING in a real browser,
   not by reading the CSS. If a new input type is ever added, add it here rather
   than styling it inside one screen. */
select, textarea,
input[type=text], input[type=email], input[type=tel], input[type=password],
input[type=time], input[type=date], input[type=number], input[type=search] {
  width: 100%; padding: 14px; font-size: 18px; border-radius: 8px;
  border: 1px solid var(--line); background: #fff; color: var(--text);
  font-family: inherit;
}
select:focus, textarea:focus,
input[type=text]:focus, input[type=email]:focus, input[type=tel]:focus, input[type=password]:focus,
input[type=time]:focus, input[type=date]:focus, input[type=number]:focus, input[type=search]:focus {
  outline: none; border-color: var(--brand-cyan); box-shadow: 0 0 0 3px rgba(23,171,203,.15);
}
textarea { resize: vertical; min-height: 80px; line-height: 1.5; }
.row { display: flex; gap: 10px; align-items: flex-end; }
.row > div { flex: 1; }
button {
  padding: 14px 20px; font-size: 16px; font-weight: 600; border: none;
  border-radius: 8px; cursor: pointer; color: #fff; background: var(--brand-cyan);
  transition: background .15s, opacity .15s; white-space: nowrap;
}
button:hover { background: var(--brand-cyan-dark); }
button:disabled { opacity: .4; cursor: not-allowed; }
button.green { background: var(--ok); }
button.red { background: var(--bad); }
button.ghost { background: #fff; color: var(--brand-ink); border: 1px solid var(--line); }
button.ghost:hover { background: var(--panel2); }
.btn-full { width: 100%; margin-top: 12px; }
.hidden { display: none !important; }
/* ==========================================================================
   PHONES — shared responsive rules
   ==========================================================================
   The office works on a desktop, but drivers, owners and staff away from the
   desk open these screens on a phone. Everything below applies ONLY at narrow
   widths: the desktop layouts above are untouched, and there is no separate
   mobile page to keep in step.
   
   Rules that any screen would want live here so future screens inherit them.
   A screen with its own layout (an order row, a filter bar) keeps its own
   overrides in its own file — but it must still clear the three bars below.

   THE THREE THINGS THAT MATTER ON A PHONE
   ---------------------------------------
   1. 16px MINIMUM on anything you can type into. iOS Safari zooms the page in
      when you focus a smaller control and does not zoom back out, which leaves
      the user scrolled sideways into a form they now have to pan around.
   2. 44x44px MINIMUM on anything you tap. A checkbox drawn at 18px is a real
      target of 18px; missing it repeatedly on a list of orders is how a wrong
      order gets confirmed.
   3. NOTHING WIDER THAN THE SCREEN. A page that scrolls sideways hides the
      right-hand end of every row, and the office cannot tell what it is
      missing.
   ========================================================================== */

/* Helpers any screen can use, defined at every width so they are predictable. */
.mobile-only { display: none; }

@media (max-width: 640px) {
  /* The sign-out button is a real tap target like everything else, and the
     name is trimmed rather than allowed to push the bar into a second line.

     ⚠️ THE TRIMMING ONLY WORKS IF NOTHING ELSE IN THE BAR CAN SHRINK. A flex
     item's default is to shrink below its content, so on a 375px phone the
     brand bar squeezed EVERY item instead of just the name: the wordmark broke
     across two lines as "TopG / as" and the screen name as "Driv / er". The
     name has a max-width and an ellipsis precisely so it is the one thing that
     gives; the rest must hold their size for that to mean anything. */
  .brandbar .wordmark,
  .brandbar .screen-name,
  .session-bar .session-pin,
  .session-bar .session-out { flex: 0 0 auto; white-space: nowrap; }
  .session-bar .session-out { min-height: 44px; font-size: 16px; }
  /* 40vw is 150px of a 375px screen, which does not leave room for the brand,
     the screen name, the PIN badge and a 44px-high Sign out. */
  .session-bar .session-who { max-width: 26vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .brandbar { gap: 8px; }
  .session-bar { gap: 6px; min-width: 0; }

  /* Long values — an email, an address, an order reference — break rather than
     push the page sideways. */
  body { padding: 0 12px 40px; overflow-wrap: anywhere; }

  /* The header is the same on every screen and is pure chrome: on a 667px-tall
     phone it must not cost a fifth of the viewport before any content. */
  .brandbar { padding: 9px 0; margin-bottom: 4px; }
  .wordmark { font-size: 22px; }
  .brandbar img { height: 30px; }
  h1 { font-size: 20px; margin: 10px 0 2px; }
  .sub { font-size: 15px; margin-bottom: 10px; }
  .panel { padding: 14px; }

  /* 16px, everywhere something can be typed into. A screen with its own
     smaller override must raise it in its own mobile block — this cannot
     reach past a later stylesheet. */
  select, textarea,
  input[type=text], input[type=email], input[type=tel],
  input[type=time], input[type=date], input[type=number], input[type=search] {
    font-size: 16px;
  }

  /* Tap targets. min-height rather than height, so a button with two lines of
     label still grows. */
  button, .btn, a.btn > button { min-height: 44px; }

  /* A checkbox or radio is drawn small and tapped large: the box grows a
     little, and the label beside it becomes part of the target. */
  input[type=checkbox], input[type=radio] { width: 22px; height: 22px; }
  .tap-target { min-height: 44px; min-width: 44px; display: inline-flex;
    align-items: center; justify-content: center; }

  /* Side-by-side form fields stack. */
  .row { flex-direction: column; align-items: stretch; gap: 12px; }

  .mobile-only { display: block; }
  .desktop-only { display: none !important; }
  /* Words that only make sense inline on a desktop — "wanted 17 Aug" reads
     well in a row, and reads twice under a WANTED label. */
  .desktop-only.inline { display: none !important; }

  /* ---- one record per card, label above value -------------------------
     A list built as a wide row of columns turns into an unreadable ragged
     wrap on a phone. Screens mark each field with data-label; here that label
     is printed above the value, so the row reads as a card rather than a pile
     of unexplained fragments. Costs nothing on desktop, where the attribute
     renders nothing at all. */
  [data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 12px; font-weight: 700; letter-spacing: .03em;
    text-transform: uppercase; color: var(--muted);
    margin-bottom: 1px;
  }

  /* ---- the action bar for a selection ---------------------------------
     Sticking it to the TOP is a desktop idea: there, the list and the buttons
     are on screen together. On a phone the buttons scroll off while you are
     still ticking rows, and you have to scroll back up to use them. So while
     something is selected the bar sits at the bottom of the viewport, over the
     list, within thumb reach; with nothing selected it says nothing worth a
     permanent strip of a small screen, so it goes away entirely. */
  .actionbar { display: none; }
  .actionbar.has-selection {
    display: flex;
    position: fixed; top: auto; left: 0; right: 0; bottom: 0; z-index: 40;
    margin: 0; border-radius: 0;
    border: 0; border-top: 1px solid var(--line);
    box-shadow: 0 -2px 12px rgba(16,24,32,.14);
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  }
  /* Compact: the count and the note wrap to their own lines, the buttons share
     one row, and the spacer that pushes them apart on a desktop is dropped.
     A bar three rows tall would cover a third of the list it belongs to. */
  .actionbar.has-selection { flex-wrap: wrap; gap: 8px; }
  .actionbar.has-selection > span:not(.count):not(.why) { display: none; }
  .actionbar.has-selection .count { flex: 1 1 100%; font-size: 14px; }
  .actionbar.has-selection .why { flex: 1 1 100%; font-size: 12px; margin: 0; }
  .actionbar.has-selection button { flex: 1 1 0; min-height: 46px; }

  /* Room to scroll the last row clear of the bar. */
  body.selection-open { padding-bottom: 150px; }
}

/* ===========================================================================
   CORRECTING AN ORDER — the editor shared by /orders and /orders/review.

   The markup is built once, in /assets/orders-shared.js, because the same
   correction is made from both screens: from the order record, and from the
   review queue where the fixable problems actually surface. Its look lives
   here for the same reason — two copies of these rules would drift, and the
   screen that got the fix would be whichever somebody was looking at.

   Self-contained on purpose: neither order screen styles a textarea, a date
   field or a number field today, so the editor cannot rely on inheriting one.
   =========================================================================== */
.order-editor {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 10px; padding: 13px 15px; margin-top: 10px;
}
.order-editor h4 {
  font-size: 12px; font-weight: 800; letter-spacing: .03em; color: var(--muted);
  text-transform: uppercase; margin-bottom: 9px;
}
.order-editor select, .order-editor input, .order-editor textarea {
  font-family: inherit; font-size: 15px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--text);
}
.order-editor .editline { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.order-editor .editline select { flex: 1; min-width: 0; }
.order-editor .editline input[type="number"] { width: 92px; }
.order-editor .editfields {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; margin-top: 12px;
}
.order-editor .editfield label {
  display: block; font-size: 12px; font-weight: 700; color: var(--muted); margin-bottom: 4px;
}
.order-editor .editfield.grow { flex: 1 1 260px; }
.order-editor textarea { width: 100%; min-height: 62px; resize: vertical; }
/* The whole strip toggles the box, so the label is part of the target. */
.order-editor .checkline {
  display: flex; align-items: center; gap: 8px; font-size: 14px; padding-bottom: 9px;
}
.order-editor .checkline input { padding: 0; width: 18px; height: 18px; }
.order-editor .editactions {
  display: flex; gap: 9px; flex-wrap: wrap; align-items: center; margin-top: 13px;
}
.order-editor .editnote { font-size: 12px; color: var(--muted); margin-top: 9px; }
.order-editor .editerr {
  background: var(--bad-bg); color: var(--bad); border-radius: 8px;
  padding: 9px 11px; font-size: 13px; margin-top: 10px;
}

@media (max-width: 640px) {
  /* 16px, or iOS zooms in on focus and never zooms back out. The rule above is
     more specific than the shared one in the block further up this file, so it
     has to be raised here rather than left to inherit. */
  .order-editor select, .order-editor input, .order-editor textarea { font-size: 16px; }
  /* And a real tap target. The shared 44px rule covers buttons only, so a
     product picker came out at 42 — close enough to look right and not close
     enough to hit while holding a phone in one hand. */
  .order-editor select, .order-editor input:not([type="checkbox"]) { min-height: 44px; }
  /* One control per line: a product name and a quantity side by side leave the
     name three characters wide. */
  .order-editor .editline { flex-wrap: wrap; }
  .order-editor .editline select { flex: 1 1 100%; }
  .order-editor .editline input[type="number"] { flex: 1 1 auto; width: auto; }
  .order-editor .editline button { flex: 1 1 auto; }
  .order-editor .editfield { flex: 1 1 100%; }
  .order-editor .checkline { min-height: 44px; padding-bottom: 0; }
  .order-editor .checkline input { width: 22px; height: 22px; }
  .order-editor .editactions button { flex: 1 1 auto; }
}

/* ===========================================================================
   TABS — one long record broken into tabs.

   Built here rather than on the screen that needed it first, because the
   customer record is not the only long scroll in this system: the site record,
   the order record and the fleet screens all get there eventually, and four
   copies of a tab strip would drift the way two copies of a bulk action would.
   The mechanism is /assets/tabs.js; this is what it looks like.

   THE PANELS INSIDE A TAB ARE NOT TOUCHED. A tab hides a WRAPPER around
   whatever panels belong to it, so a panel keeps its own show/hide rules (the
   contacts panel is hidden until a new customer has been saved, and that is a
   different question from which tab it is on) and every input stays in the
   page — which is what lets one Save button still save a field edited on a tab
   you are no longer looking at.
   =========================================================================== */
.tabs {
  display: flex; gap: 2px; align-items: stretch;
  border-bottom: 1px solid var(--line);
  margin: 0 0 16px;
}
/* Buttons, so they are reachable by keyboard for nothing. The theme's button
   is a solid cyan block, which is right for an action and wrong for a tab, so
   every inherited property is stated again rather than left to chance. */
.tabs .tab {
  background: transparent; color: var(--muted);
  font-size: 15px; font-weight: 700; padding: 11px 16px;
  border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  border-radius: 8px 8px 0 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
}
.tabs .tab:hover { background: var(--panel2); color: var(--text); }
.tabs .tab.on {
  background: var(--panel); color: var(--brand-ink);
  border-bottom-color: var(--brand-cyan);
}
.tabs .tab:focus-visible { outline: 2px solid var(--brand-cyan); outline-offset: -2px; }

/* THE INDICATOR IS THE POINT OF THE BADGE, not the decoration.
   A tab hides what is on it, so an overdue follow-up behind a tab nobody
   clicked is a follow-up nobody does. Grey is a count ("4 contacts"), amber
   and red are "something here needs you". */
.tabs .tab .badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  font-size: 12px; font-weight: 800; line-height: 1;
  background: var(--panel2); color: var(--muted);
}
.tabs .tab .badge.warn { background: var(--warn-bg); color: var(--warn); }
.tabs .tab .badge.bad  { background: var(--bad-bg);  color: var(--bad); }
.tabs .tab.on .badge { background: var(--panel2); }
.tabs .tab.on .badge.warn { background: var(--warn-bg); }
.tabs .tab.on .badge.bad  { background: var(--bad-bg); }

.tabpanel[hidden] { display: none; }

@media (max-width: 640px) {
  /* THE STRIP SCROLLS SIDEWAYS, THE PAGE DOES NOT. Four tabs wrapping into a
     ragged second row is how a tab strip stops reading as a row of choices;
     letting the page itself go wider hides the right-hand end of everything
     else on it. So the overflow is contained here and nowhere else.

     The negative margin bleeds the strip to the screen edges, so the last tab
     scrolls fully out rather than being clipped by the body's padding, and the
     sticky background covers the full width. */
  .tabs {
    position: sticky; top: 0; z-index: 25;
    background: var(--bg);
    overflow-x: auto; flex-wrap: nowrap;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    margin-left: -12px; margin-right: -12px; padding: 0 12px;
    /* The strip carries its background a few pixels up past its own top edge.
       A pinned layer and the content scrolling under it do not land on the
       same device pixel, and the one-pixel hairline that leaves shows the
       panel behind — a white line flickering above the tabs at every scroll
       position, which reads as a rendering fault rather than a design. */
    box-shadow: 0 -6px 0 var(--bg);
  }
  .tabs::-webkit-scrollbar { display: none; }
  /* Stuck to the top on a phone, because the tab you want is otherwise a long
     scroll back up a site list. It costs 44px of a small screen permanently,
     which is the trade: the save bar is already pinned to the bottom for the
     same reason. */
  .tabs .tab {
    flex: 0 0 auto; min-height: 44px; font-size: 15px; padding: 10px 14px;
    white-space: nowrap;
  }
}

/* ===========================================================================
   A TICK AND ITS LABEL — .checkline

   Shared because the same bug has now been found twice, on two screens, by
   the first real browser check either of them had: a screen sets
   `input, select { min-height: 44px }` for the phone rules, that is a FLOOR
   rather than a height, and it beats the 22px the tick box was given. The box
   renders 22 wide by 44 tall — a stretched rectangle — everywhere.

   THE 44px TARGET IS THE LABEL, NOT THE BOX. A tick drawn 44px square reads as
   a button; wrapping it in a label makes the whole line tappable, which is the
   same answer the order rows use. So the label carries the height and the box
   keeps its own size — including a min-height of its own, or the floor above
   wins again.

   A screen adds its own spacing on top; it should not restate these.
   =========================================================================== */
.checkline { display: flex; align-items: center; gap: 8px; }
/* ⚠️ `flex: none` MATTERS AND WAS ADDED AFTER A MEASUREMENT. A flex item may
   shrink by default, so a long label squeezed the tick box from 22px down to
   15px — found on the role editor, where a permission's description is a whole
   sentence. It is invisible on a short label, which is why it survived this
   long, and it makes the smallest target on the screen smaller still. */
.checkline input[type='checkbox'] { width: 18px; height: 18px; padding: 0; flex: none; }

@media (max-width: 640px) {
  .checkline { min-height: 44px; }
  /* The attribute selector out-specifies a screen's bare `input` rule, so this
     holds wherever the label is used, whatever order the files load in. */
  .checkline input[type='checkbox'] { width: 22px; height: 22px; min-height: 22px; }
  /* THE 44px TARGET IS THE LABEL, and it only is one if the label FILLS the
     row. Left as text it was 20px tall inside a 44px row, so the bottom half of
     every tick line did nothing — the row looked like a target and was not. */
  .checkline label { flex: 1; display: flex; align-items: center; min-height: 44px; margin-bottom: 0; }
}

/* ===========================================================================
   ADDRESS SEARCH (LINZ) — public/assets/address-picker.js

   Shared for the same reason the tab strip is: a site and a depot are the same
   question, and a second copy would eventually disagree about what a near miss
   looks like. The markup comes from addressFieldHtml(), so the classes are the
   component's and belong beside it rather than on whichever screen was written
   first.

   The list hangs UNDER the address box rather than beside it: on a phone there
   is nowhere beside it, and one layout that works at both widths beats two
   that each work at one.
   =========================================================================== */
.addr-field { position: relative; }
.addr-results {
  position: absolute; z-index: 30; left: 0; right: 0; top: 100%;
  margin-top: 4px; max-height: 292px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 6px 20px rgba(16,24,32,.16);
}
.addr-opt {
  display: block; width: 100%; text-align: left; background: transparent;
  color: var(--text); font-size: 15px; font-weight: 400; white-space: normal;
  border: 0; border-radius: 0; padding: 11px 14px; cursor: pointer;
}
.addr-opt + .addr-opt { border-top: 1px solid var(--line); }
.addr-opt:hover, .addr-opt:focus { background: var(--panel2); }
.addr-opt .a1 { display: block; font-weight: 600; }
.addr-opt .a2 { display: block; font-size: 13px; color: var(--muted); margin-top: 1px; }
.addr-note { padding: 11px 14px; font-size: 14px; color: var(--muted); }
/* Near misses, not answers — see the note in the picker's search(). */
.addr-warn {
  background: var(--warn-bg); color: var(--warn); font-weight: 600;
  border-bottom: 1px solid var(--warn);
}
.addr-warn b { color: var(--warn); }
/* What the record will say once saved, stated before it is saved. */
.addr-state { font-size: 13px; margin-top: 6px; }
.addr-state.matched   { color: var(--ok); font-weight: 600; }
.addr-state.unmatched { color: var(--warn); font-weight: 600; }
.addr-state .why { color: var(--muted); font-weight: 400; }

@media (max-width: 640px) {
  /* A real target per address, and 16px so picking one does not zoom the form
     in on the way past. */
  .addr-opt { font-size: 16px; min-height: 44px; padding: 12px 14px; }
  .addr-opt .a2, .addr-state, .addr-note { font-size: 14px; }
}
