/* Master TRANSPORT theme: the shared Master Series design system (navy + green,
   Samsung Sharp Sans) with Master Transport's own "journey board" component style:
   rounded 16px cards with a soft elevated shadow and a 3px TOP accent, capsule
   buttons, capsule chips with a leading status dot, and the product's signature
   component, the route-line trip timeline.

   Tokens: every --ms-* value is byte-identical to Master Service, Master Wash and
   Master Security. Everything product-specific is --mt-*. --mt-brand-primary and
   --mt-brand-accent default to the family navy and green so a Premier or Elite
   company's colour scheme can override them from app_header.php (one <style> rule
   on :root) without touching any component. */

:root {
    /* Shared Master Series palette: identical across Master Service / Wash / Security / Transport */
    --ms-navy-950: #081b33;
    --ms-navy-900: #0c2340;
    --ms-navy-800: #123156;
    --ms-navy-700: #17406f;
    --ms-navy-600: #1d4f88;
    --ms-navy-500: #2360a5;
    --ms-navy-100: #dbe7f5;
    --ms-navy-50:  #eef4fb;
    --ms-green-900: #0d3f12;
    --ms-green-800: #14541b;
    --ms-green-700: #1b6a24;
    --ms-green-600: #23842e;
    --ms-green-500: #2e9e3a;
    --ms-green-400: #4ab653;
    --ms-green-100: #ddf2df;
    --ms-green-50:  #effaf0;
    --ms-ink: #13202f;
    --ms-muted: #5d6b7c;
    --ms-line: #dde4ec;
    --ms-bg: #f4f7fa;
    --ms-card: #ffffff;
    --ms-danger: #c62828;
    --ms-warn: #e65100;
    --ms-radius: 14px;
    --ms-shadow: 0 2px 14px rgba(12,35,64,.08);
    --ms-font: "Samsung Sharp Sans", "Segoe UI", Arial, sans-serif;

    /* Master Transport product tokens */
    --mt-radius: 16px;          /* cards, panels, modals */
    --mt-radius-sm: 12px;       /* inputs, small surfaces */
    --mt-side-w: 240px;         /* the labelled side navigation */
    --mt-topbar-h: 62px;        /* the white top bar */
    --mt-bottom-h: 64px;        /* the phone bottom navigation (excluding the safe area) */
    --mt-shadow: 0 4px 18px rgba(12,35,64,.07);
    --mt-shadow-lg: 0 16px 44px rgba(12,35,64,.14);
    /* Premier / Elite branding hooks. Dark surfaces (the sidenav, the bottom nav, the
       hero panels) are built on --mt-brand-primary; the route line, the active stop,
       the card accent and the live dots are built on --mt-brand-accent. Buttons keep
       the family green glaze whatever the brand: white text on an unknown colour is
       not a contrast risk worth taking on a Save button. */
    --mt-brand-primary: #0c2340;
    --mt-brand-accent: #23842e;
    --mt-amber: #b45309;
    --mt-amber-bg: #fef3c7;
    --mt-red-bg: #fde8e8;
    /* The scroll box (.scroll-box, and every .table-wrap): the tallest a growing list
       may get before its rows scroll inside it. About ten table rows on a desktop; on a
       phone it leaves the page title and the box's own heading on screen above it. */
    --mt-scroll-max: clamp(260px, 64vh, 680px);
    --mt-scroll-sm: min(360px, 52vh);   /* .scroll-box-sm, and the older .table-scroll */
    --mt-scroll-lg: 80vh;               /* .scroll-box-lg, the odd long reading list */
}
@media (max-width: 700px) {
    :root { --mt-scroll-max: clamp(260px, 60vh, 560px); }
}
/* A phone's vh is its height with the browser bars hidden; svh is the height you
   actually see with them showing, so the box never runs under the address bar. */
@supports (height: 1svh) {
    :root {
        --mt-scroll-max: clamp(260px, 64svh, 680px);
        --mt-scroll-sm: min(360px, 52svh);
        --mt-scroll-lg: 80svh;
    }
    @media (max-width: 700px) {
        :root { --mt-scroll-max: clamp(260px, 60svh, 560px); }
    }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    font-family: var(--ms-font);
    color: var(--ms-ink);
    background: var(--ms-bg);
    font-size: 15.5px;
    line-height: 1.55;
}
h1, h2, h3, h4 { color: var(--ms-navy-900); line-height: 1.25; margin: 0 0 12px; }
h1 { font-size: 26px; font-weight: 800; letter-spacing: -.01em; }
h2 { font-size: 20px; font-weight: 800; }
h3 { font-size: 17px; font-weight: 700; }
h4 { font-size: 15px; font-weight: 700; }
p { margin: 0 0 14px; }
a { color: var(--ms-green-700); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; }
strong, b { font-weight: 700; }

.ic { width: 22px; height: 22px; flex: 0 0 auto; }
.ic-sm { width: 17px; height: 17px; flex: 0 0 auto; }
.ic-lg { width: 30px; height: 30px; flex: 0 0 auto; }

/* Micro-label: the uppercase eyebrow on cards, tables, tiles and the sidenav groups */
.micro {
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--ms-muted);
}

/* The hidden attribute must always win. Any component that sets its own display
   (.alert is flex, .toolbar is flex, the More sheet is a grid) would otherwise
   override the browser default and show a box the script deliberately hid. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------- buttons
   Capsules, per the scope: a fully rounded pill on every size. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: var(--ms-navy-800);
    color: #fff;
    border: 1.5px solid transparent;
    border-radius: 999px;
    padding: 11px 22px;
    font-family: var(--ms-font);
    font-size: 14.5px;
    font-weight: 700;
    line-height: 1.2;
    min-height: 44px;
    max-width: 100%;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { background: var(--ms-navy-900); text-decoration: none; color: #fff; }
/* A capsule never grows past the box it sits in (max-width above). A label that can
   run long (a client's name, a thread title) goes in .btn-label, which is cut with an
   ellipsis instead of pushing the capsule out of its card and the page sideways;
   give the button a title="" with the full words. */
.btn .btn-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The primary action carries the family glaze: a polished face that is brightest
   along the top edge and deepens towards the foot, a hairline of light on the top
   border and a shadow line on the bottom one. The light never goes above green-600,
   so the white label keeps its contrast. The shine that travels across on hover is
   in the motion section at the foot of this file. */
.btn-green {
    position: relative;
    overflow: hidden;
    background-color: var(--ms-green-700);
    background-image: linear-gradient(180deg,
        var(--ms-green-600) 0%,
        var(--ms-green-700) 32%,
        var(--ms-green-800) 100%);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(0,0,0,.18);
}
.btn-green:hover {
    background-color: var(--ms-green-800);
    background-image: linear-gradient(180deg,
        var(--ms-green-700) 0%,
        var(--ms-green-800) 32%,
        var(--ms-green-900) 100%);
}
.btn-outline { background: transparent; border-color: var(--ms-navy-800); color: var(--ms-navy-800); }
.btn-outline:hover { background: var(--ms-navy-50); color: var(--ms-navy-900); }
.btn-ghost { background: transparent; color: var(--ms-navy-800); }
.btn-ghost:hover { background: var(--ms-navy-50); color: var(--ms-navy-900); }
.btn-danger { background: var(--ms-danger); }
.btn-danger:hover { background: #a51f1f; }
.btn-sm { padding: 7px 15px; min-height: 38px; font-size: 13.5px; }
/* A full-width button has nowhere to grow sideways, so its label wraps onto a second
   line instead of being cut off (.btn is nowrap, and .btn-green clips its overflow for
   the shine). min-height is kept from the size class, so a one-line label is unchanged. */
.btn-block { width: 100%; white-space: normal; text-align: center; line-height: 1.25; }
/* Driver screens: 56px, bigger type, made for a thumb in a moving vehicle. */
.btn-big { padding: 15px 28px; min-height: 56px; font-size: 16.5px; }
.btn-big.btn-block { white-space: normal; text-align: center; line-height: 1.25; }
/* A round icon-only button (an edit pencil in a row, a close cross in a sheet).
   Always give it an aria-label. */
.btn-icon { width: 44px; height: 44px; min-height: 44px; padding: 0; border-radius: 50%; }
.btn-icon.btn-sm { width: 38px; height: 38px; min-height: 38px; }
.btn[disabled], .btn[aria-disabled=true] { opacity: .55; cursor: not-allowed; }
.btn .ic { width: 19px; height: 19px; }
.btn-big .ic { width: 24px; height: 24px; }
/* On a touch screen nothing is smaller than a fingertip, whatever the size class. */
@media (hover: none) {
    .btn-sm, .btn-icon.btn-sm { min-height: 44px; }
    .btn-icon.btn-sm { width: 44px; height: 44px; }
}
.btn:focus-visible, .btn-icon:focus-visible { outline: 3px solid var(--ms-green-400); outline-offset: 2px; }

/* ---------------------------------------------------------------- cards
   Signature: white, 16px radius, a soft elevated shadow, and an optional 3px accent
   along the TOP edge (Security draws it on the left; we do not). */
.card {
    background: var(--ms-card);
    border: 1px solid rgba(221,228,236,.7);
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow);
    padding: 20px 22px;
    margin-bottom: 16px;
}
.card-accent { border-top: 3px solid var(--mt-brand-accent); }
.card-navy { border-top: 3px solid var(--ms-navy-700); }
.card-warn { border-top: 3px solid var(--mt-amber); }
.card-danger { border-top: 3px solid var(--ms-danger); }
.card-danger .card-title { color: var(--ms-danger); }
.card-danger .card-title .ic { color: var(--ms-danger); }
.card-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 9px;
    font-weight: 800;
    color: var(--ms-navy-900);
    font-size: 16px;
    margin: 0 0 14px;
}
.card-title .ic { color: var(--ms-green-600); }
.card-title .spacer { flex: 1; }
/* A title row that carries a chip and a link ("Recent messages", "17 new", "Open messages")
   wraps on a phone instead of pushing the capsule past the card and the whole page
   sideways; the capsule keeps to the right edge on whichever line it lands. */
.card-title > .btn { margin-left: auto; }
.card-sub { color: var(--ms-muted); font-size: 13.5px; margin-top: -10px; margin-bottom: 14px; }
.card.compact { padding: 14px 16px; }

/* Dark panel (hero surfaces, the driver's next-trip card) */
.panel-dark {
    background: linear-gradient(160deg, var(--ms-navy-950), var(--mt-brand-primary) 55%, var(--ms-navy-800));
    color: #fff;
    border: 1px solid rgba(255,255,255,.08);
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow-lg);
    padding: 22px;
    margin-bottom: 16px;
}
.panel-dark h1, .panel-dark h2, .panel-dark h3 { color: #fff; }
.panel-dark .micro { color: rgba(255,255,255,.55); }
.panel-dark a { color: var(--ms-green-400); }
.panel-dark .muted { color: rgba(255,255,255,.65); }

/* ---------------------------------------------------------------- KPI board */
.board { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 12px; margin-bottom: 16px; }
/* The number is anchored to the FOOT of the tile so a label that wraps to two lines
   does not push its own number down while its neighbours' stay up. */
.tile {
    background: var(--ms-card);
    border: 1px solid rgba(221,228,236,.7);
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow);
    padding: 14px 16px;
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 92px;
}
.tile .tile-num { font-size: 27px; font-weight: 800; color: var(--ms-navy-900); line-height: 1.15; margin-top: auto; font-variant-numeric: tabular-nums; }
.tile .tile-num.ok { color: var(--ms-green-700); }
.tile .tile-num.warn { color: var(--mt-amber); }
.tile .tile-num.bad { color: var(--ms-danger); }
.tile .micro { display: block; margin-bottom: 4px; padding-right: 30px; }
.tile .ic { position: absolute; top: 14px; right: 14px; color: var(--ms-navy-100); width: 26px; height: 26px; }
.tile .tile-sub { font-size: 12.5px; color: var(--ms-muted); margin-top: 2px; }
a.tile { text-decoration: none; color: inherit; }
a.tile:hover { text-decoration: none; border-color: var(--mt-brand-accent); }
.tile .chip { white-space: normal; max-width: 100%; align-self: flex-start; }
/* A Rand amount as a tile's number: smaller than a count and never broken at the thousands
   space. On a phone the tile takes the whole row (.tile-wide) so the amount has the room. */
.tile .tile-num.money { font-size: 22px; letter-spacing: -.01em; white-space: nowrap; }
@media (max-width: 480px) { .board > .tile.tile-wide { grid-column: 1 / -1; } }

/* ---------------------------------------------------------------- chips
   Capsules with a leading dot. One class per lifecycle state, chosen by the
   *_status_chip() helpers; the live states pulse. */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border-radius: 999px;
    padding: 4px 11px 4px 9px;
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1.3;
    background: var(--ms-navy-50);
    color: var(--ms-navy-800);
    white-space: nowrap;
}
.chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ms-navy-500); flex: 0 0 auto; }
.chip-ok { background: var(--ms-green-50); color: var(--ms-green-800); }
.chip-ok::before { background: var(--ms-green-500); }
.chip-live { background: var(--ms-green-100); color: var(--ms-green-900); }
.chip-live::before { background: var(--ms-green-500); animation: mt-pulse 1.6s ease-in-out infinite; }
.chip-warn { background: var(--mt-amber-bg); color: var(--mt-amber); }
.chip-warn::before { background: var(--mt-amber); }
.chip-bad { background: var(--mt-red-bg); color: var(--ms-danger); }
.chip-bad::before { background: var(--ms-danger); }
.chip-navy { background: var(--ms-navy-100); color: var(--ms-navy-900); }
.chip-navy::before { background: var(--ms-navy-700); }
.chip-muted { background: #eef1f5; color: var(--ms-muted); }
.chip-muted::before { background: #9aa7b5; }
.chip-sm { font-size: 11.5px; padding: 2px 9px 2px 8px; }
.chip .ic-sm { width: 14px; height: 14px; }

/* Live indicator dot */
.live-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--ms-green-500); animation: mt-pulse 1.8s ease-in-out infinite; flex: 0 0 auto; }
@keyframes mt-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(46,158,58,.45); }
    50% { box-shadow: 0 0 0 6px rgba(46,158,58,0); }
}

/* ---------------------------------------------------------------- forms */
.field { margin-bottom: 15px; }
.field label { display: block; font-weight: 700; font-size: 13.5px; margin-bottom: 6px; color: var(--ms-navy-900); }
.field label .req { color: var(--ms-danger); }
/* .hint works standalone too (status lines, inline explanations), not only inside .field */
.hint, .field .hint { display: block; font-size: 12.5px; color: var(--ms-muted); margin-top: 5px; line-height: 1.45; }
.hint.bad, .hint.push-bad { color: var(--ms-danger); }
.hint.good, .hint.push-good { color: var(--ms-green-800); }

.field input[type=text], .field input[type=email], .field input[type=password],
.field input[type=tel], .field input[type=number], .field input[type=date],
.field input[type=time], .field input[type=datetime-local], .field input[type=month],
.field input[type=week], .field input[type=search], .field input[type=url],
.field input[type=color], .field select, .field textarea {
    width: 100%;
    border: 1.5px solid var(--ms-line);
    border-radius: var(--mt-radius-sm);
    padding: 11px 13px;
    font-family: var(--ms-font);
    font-size: 15px;
    color: var(--ms-ink);
    background: #fff;
    min-height: 44px;
    transition: border-color .15s, box-shadow .15s;
}
.field input[type=color] { padding: 4px 6px; width: 64px; cursor: pointer; }
.field input[type=file] { display: block; width: 100%; font-family: var(--ms-font); font-size: 14px; padding: 9px 0; }
/* Text boxes start tall enough to read a paragraph without scrolling, and grow as
   you type (assets/js/app.js). Never smaller than this. */
.field textarea, textarea {
    min-height: 130px;
    resize: vertical;
    line-height: 1.55;
    overflow-y: hidden;
}
.field textarea.tall, textarea.tall { min-height: 220px; }
.field textarea.short, textarea.short { min-height: 88px; }
.field input:focus, .field select:focus, .field textarea:focus {
    outline: none;
    border-color: var(--ms-green-500);
    box-shadow: 0 0 0 3px var(--ms-green-100);
}
.field input[aria-invalid=true], .field select[aria-invalid=true], .field textarea[aria-invalid=true], .field .invalid { border-color: var(--ms-danger); }
.field .err { color: var(--ms-danger); font-size: 12.5px; margin-top: 5px; font-weight: 600; }
/* A select that stands outside a .field (a filter in a toolbar) still looks like the
   controls beside it. Width is deliberately not set: a filter sizes to its options. */
select {
    border: 1.5px solid var(--ms-line);
    border-radius: var(--mt-radius-sm);
    padding: 11px 13px;
    font-family: var(--ms-font);
    font-size: 15px;
    color: var(--ms-ink);
    background: #fff;
    min-height: 44px;
    max-width: 100%;
    transition: border-color .15s, box-shadow .15s;
}
select:focus { outline: none; border-color: var(--ms-green-500); box-shadow: 0 0 0 3px var(--ms-green-100); }
/* minmax(0, 1fr), never a bare 1fr: a bare 1fr track is minmax(auto, 1fr), so a
   select's longest option or a date input's intrinsic width would push the track
   wider than a narrow card and the whole page would scroll sideways. */
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 16px; }
.form-grid .full { grid-column: 1 / -1; }
.form-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 16px; }
.form-grid-3 .full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid, .form-grid-3 { grid-template-columns: minmax(0, 1fr); } }
/* An inline pair (a time next to a date, a quantity next to a unit). */
.field-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.field-row > .field { margin-bottom: 0; }
.field { min-width: 0; }
.field input, .field select, .field textarea { max-width: 100%; }
.check-row { display: flex; gap: 10px; align-items: flex-start; font-size: 14px; margin-bottom: 12px; cursor: pointer; }
.check-row input { width: 20px; height: 20px; margin-top: 2px; accent-color: var(--ms-green-600); flex: 0 0 auto; }
.check-row.boxed { border: 1.5px solid var(--ms-line); border-radius: var(--mt-radius-sm); padding: 12px 14px; }
.check-row.boxed:hover { border-color: var(--ms-navy-500); }
/* A label that IS a component (a tick row, a manifest row) keeps its flex layout inside a
   .field, where the block label rule above would otherwise stack the box over its words
   and bolden them. */
.field label.check-row, .field label.pax-row { display: flex; font-size: inherit; font-weight: inherit; margin-bottom: 0; }
.form-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--ms-line); }
.form-actions .spacer { flex: 1; }
fieldset.set {
    border: 1px solid var(--ms-line);
    border-radius: var(--mt-radius);
    padding: 16px 18px 6px;
    margin: 0 0 16px;
    background: #fcfdfe;
}
fieldset.set legend, .addr-legend {
    padding: 0 8px;
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--ms-navy-800);
}
/* The structured address block from includes/address.php */
.addr-set {
    border: 1px solid var(--ms-line);
    border-radius: var(--mt-radius);
    padding: 16px 18px 6px;
    margin: 0 0 16px;
    background: #fcfdfe;
}
.addr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
.addr-grid .addr-line1, .addr-grid .addr-line2 { grid-column: 1 / -1; }
@media (max-width: 640px) { .addr-grid { grid-template-columns: 1fr; } }

/* Password reveal (assets/js/password.js wraps the input) */
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 50px !important; }
/* 44px like every other touch target (SPEC item 7); the input is 44px tall, so the
   round button fills its height and sits flush with the right edge. */
.pw-eye {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--ms-muted);
    cursor: pointer;
}
.pw-eye:hover { background: var(--ms-navy-50); color: var(--ms-navy-800); }
.pw-eye:focus-visible { outline: 2px solid var(--ms-green-600); outline-offset: 1px; }
.pw-eye[aria-pressed="true"] { color: var(--ms-green-700); }
.pw-suggest { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin: -8px 0 15px; }
.pw-suggest-btn {
    border: 0; background: none; padding: 0;
    font-family: var(--ms-font); font-size: 13px; font-weight: 700;
    color: var(--ms-green-700);
    text-decoration: underline; text-underline-offset: 2px;
    cursor: pointer;
}
.pw-suggest-btn:hover { color: var(--ms-green-800); }
.pw-suggest-btn:focus-visible { outline: 2px solid var(--ms-green-600); outline-offset: 2px; border-radius: 3px; }
.pw-suggest-note { font-size: 12.5px; color: var(--ms-muted); font-weight: 600; }

/* ---------------------------------------------------------------- tables */
/* Every .table-wrap is also a scroll box (see "scroll box" below): short tables look
   exactly as before, a long one stops at --mt-scroll-max and its rows scroll inside. */
.table-wrap {
    overflow-x: auto;
    background-color: var(--ms-card);
    border: 1px solid rgba(221,228,236,.7);
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow);
    margin-bottom: 16px;
}
table.data { width: 100%; border-collapse: collapse; font-size: 14.5px; }
table.data th {
    text-align: left;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--ms-muted);
    background: #f8fafc;
    padding: 11px 14px;
    border-bottom: 1px solid var(--ms-line);
    white-space: nowrap;
}
table.data td { padding: 12px 14px; border-bottom: 1px solid #edf1f6; vertical-align: middle; }
table.data tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover td { background: #fafcfe; }
table.data td.num, table.data th.num { font-variant-numeric: tabular-nums; text-align: right; }
table.data td.actions { text-align: right; white-space: nowrap; }
table.data .sub { display: block; font-size: 12.5px; color: var(--ms-muted); }
.row-link { cursor: pointer; }
tr.row-link:hover td { background: var(--ms-navy-50); }
/* On phones let the table keep readable column widths and scroll sideways inside
   .table-wrap, rather than squeezing every cell into a stack of single words. */
@media (max-width: 700px) {
    table.data { min-width: 620px; }
    table.data th, table.data td { padding: 11px 12px; }
    .table-wrap { position: relative; }
}
/* ---------------------------------------------------------------- scroll box
   THE one way a list that grows over time is kept from making the page longer and
   longer: the list sits in a box with a height cap and its rows scroll inside it.
     <div class="scroll-box"> ...a feed, a timeline, a list... </div>
     <div class="table-wrap"> <table class="data"> ... </table> </div>   (boxed already)
   - Short lists look exactly as they did: only max-height is set, never a height.
   - Scrolls both ways, so a wide table still slides sideways on a phone INSIDE the box.
   - overscroll-behavior stays auto: when the box reaches its end, the wheel or the
     finger carries on scrolling the page. Never contain it, never trap anyone.
   - A table's header row stays pinned at the top of the box, with a hairline under it.
   - Soft shadows at the top and bottom edge say "there is more this way". They are
     CSS only: two card-coloured covers that travel with the content (attachment
     local) sit over two shadows fixed to the box (attachment scroll), so a shadow
     only shows once the cover has scrolled off it. They are backgrounds, drawn under
     the rows, so they never cover a row, a link or a button.
   - Sizes: default --mt-scroll-max; .scroll-box-sm (about 360px, the dashboard-style
     side panels); .scroll-box-lg (about 80% of the screen). .table-scroll is the older
     name of .scroll-box-sm and still works.
   - Opt out: .table-wrap.no-cap (forms and editors: the quote and invoice line items,
     the notification preferences; small fixed panels of 15 rows or fewer that never
     grow) or .scroll-box-off on anything. Print uncaps all and hides the app shell.
   - The box sits on the card colour. On another surface set --mt-scroll-bg to it, or
     the covers show as a pale band.
   - A box whose rows hold no link, button or field gets tabindex="0", role="region"
     and an aria-label, so a keyboard can reach and scroll it (CONVENTIONS.md). app.js
     takes such a box out of the Tab order while it has nothing to scroll.
   - A list only a little taller than its cap (up to 48px over) is not boxed at all:
     app.js adds .scroll-fit, so a short list never shows a scrollbar for a sliver.
   - scroll-padding keeps a control the keyboard moves to clear of the pinned header
     (Shift+Tab backwards through the rows), so focus is never hidden under it. */
.scroll-box, .table-wrap, .table-scroll {
    --mt-scroll-head: 0px;          /* the pinned header's height: the top shadow starts under it */
    --mt-scroll-shade: rgba(12,35,64,.17);
    max-height: var(--mt-scroll-max);
    overflow: auto;
    overscroll-behavior: auto;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: auto;
    scroll-padding-top: calc(var(--mt-scroll-head) + 4px);
    scroll-padding-bottom: 4px;
    scrollbar-width: thin;
    scrollbar-color: #cbd5e1 transparent;
    background-image:
        linear-gradient(var(--mt-scroll-bg, var(--ms-card)) 35%, rgba(255,255,255,0)),
        linear-gradient(rgba(255,255,255,0), var(--mt-scroll-bg, var(--ms-card)) 65%),
        radial-gradient(farthest-side at 50% 0, var(--mt-scroll-shade), rgba(12,35,64,0)),
        radial-gradient(farthest-side at 50% 100%, var(--mt-scroll-shade), rgba(12,35,64,0));
    background-position: 0 var(--mt-scroll-head), 0 100%, 0 var(--mt-scroll-head), 0 100%;
    background-size: 100% 42px, 100% 42px, 100% 14px, 100% 14px;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
}
/* Keeps the card's corners when the box fills a card; any component with its own
   radius (.table-wrap, .card, .manifest) wins, :where() weighs nothing. */
:where(.scroll-box) { border-radius: inherit; }
.scroll-box::-webkit-scrollbar, .table-wrap::-webkit-scrollbar, .table-scroll::-webkit-scrollbar { width: 9px; height: 9px; }
.scroll-box::-webkit-scrollbar-track, .table-wrap::-webkit-scrollbar-track, .table-scroll::-webkit-scrollbar-track,
.scroll-box::-webkit-scrollbar-corner, .table-wrap::-webkit-scrollbar-corner, .table-scroll::-webkit-scrollbar-corner { background: transparent; }
.scroll-box::-webkit-scrollbar-thumb, .table-wrap::-webkit-scrollbar-thumb, .table-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 999px; border: 2px solid var(--mt-scroll-bg, var(--ms-card)); }
.scroll-box::-webkit-scrollbar-thumb:hover, .table-wrap::-webkit-scrollbar-thumb:hover, .table-scroll::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
.scroll-box:focus-visible, .table-wrap:focus-visible, .table-scroll:focus-visible { outline: 2px solid var(--ms-green-600); outline-offset: 2px; }
.scroll-box-sm, .table-scroll { max-height: var(--mt-scroll-sm); }
.scroll-box-lg { max-height: var(--mt-scroll-lg); }
/* The pinned header row. A collapsed border does not travel with a sticky cell, so the
   hairline under the header is an inset shadow that does. */
.scroll-box:has(table.data > thead), .table-wrap:has(table.data > thead), .table-scroll:has(table.data > thead) { --mt-scroll-head: 40px; }
.scroll-box table.data thead th, .table-wrap table.data thead th, .table-scroll table.data thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: #f8fafc;      /* the header colour from table.data th, opaque so rows pass under it */
    border-bottom: 0;
    box-shadow: inset 0 -1px 0 var(--ms-line);
}
/* Opting out: the box keeps its sideways scroll (a wide editor on a phone) but has no
   height cap and no edge shadows. */
.table-wrap.no-cap, .scroll-box-off { max-height: none; background-image: none; }
.scroll-box.scroll-box-off:not(.table-wrap) { overflow: visible; }
/* Set by app.js (mtFitBoxes) on a box whose list is at most 48px taller than the cap: the
   whole list shows, with no scrollbar and no shadows. !important so it also beats an
   inline max-height (the schedule's generated trips). Print uncaps every box anyway. */
.scroll-box.scroll-fit, .table-wrap.scroll-fit, .table-scroll.scroll-fit { max-height: none !important; background-image: none; }
/* Tinted rows in a box are see-through, so the edge shadow drawn under them still shows
   ("more this way"). Over the white card they look the same as the solid tints. */
.scroll-box .pax-row { background-color: transparent; }
.scroll-box .pax-row.is-boarded { background-color: rgba(46,158,58,.075); }
.scroll-box .pax-row.is-off_boarded { background-color: rgba(35,96,165,.075); }
.scroll-box .pax-row.is-not_present { background-color: rgba(250,204,21,.24); }
@media print {
    .scroll-box, .table-wrap, .table-scroll, .scroll-box-sm, .scroll-box-lg {
        max-height: none !important;
        overflow: visible !important;
        background-image: none !important;
    }
    .scroll-box table.data thead th, .table-wrap table.data thead th, .table-scroll table.data thead th { position: static; }
}
/* Key : value rows inside a card (a client profile, a trip summary). The label column is
   as wide as its longest label and never more than a third of the card, so a narrow card
   (the right column of a profile) keeps most of its width for the values. */
.kv { display: grid; grid-template-columns: fit-content(34%) minmax(0, 1fr); gap: 6px 14px; font-size: 14.5px; margin: 0; }
.kv dt { color: var(--ms-muted); font-weight: 600; }
.kv dd { margin: 0; color: var(--ms-ink); min-width: 0; overflow-wrap: anywhere; }
/* On a phone two columns leave a value 90 to 170px: an email or a name breaks mid-word and
   an expiry chip runs past the card. The label sits above its value instead. */
@media (max-width: 480px) {
    .kv { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .kv dt { margin-top: 9px; font-size: 12.5px; }
    .kv dt:first-child { margin-top: 0; }
}
.pager { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; font-size: 13.5px; color: var(--ms-muted); }
.pager .pages { display: flex; gap: 6px; }

/* ---------------------------------------------------------------- layout helpers */
.toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 16px; }
.toolbar .spacer { flex: 1; }
.split { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.grid-main { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 900px) { .grid-2, .grid-3, .grid-main { grid-template-columns: 1fr; } }
/* The 240px sidenav appears at 1024px (app.css), so between 1024 and 1199 the content
   area is narrower than a phone in landscape gets: two columns there leave the main
   column about 430px, too little for a form or a manifest. One column until the
   sidenav has room to spare. */
@media (min-width: 1024px) and (max-width: 1199px) { .grid-main { grid-template-columns: 1fr; } }
/* The same sidenav squeezes three cards to about 230px each, and a key/value grid inside
   them is left with 60px for its values: two columns until the content area has room. */
@media (min-width: 1024px) and (max-width: 1199px) { .grid-3 { grid-template-columns: 1fr 1fr; } }
.stack { display: flex; flex-direction: column; gap: 12px; }
.stack > .card, .stack > .table-wrap { margin-bottom: 0; }
/* A grid or flex child defaults to min-width:auto, so a 620px table inside a card
   inside a grid would force the page wide on a phone. min-width:0 lets the child
   shrink so the TABLE scrolls inside its box and the PAGE stays phone-width. */
.grid-2 > *, .grid-3 > *, .grid-main > *, .board > *, .card, .table-wrap, .stack > * { min-width: 0; }
.muted { color: var(--ms-muted); }
.small { font-size: 13px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.center { text-align: center; }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }
.mt1 { margin-top: 8px; } .mt2 { margin-top: 16px; } .mb1 { margin-bottom: 8px; } .mb2 { margin-bottom: 16px; }
.money { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The search component: same height, radius and focus ring as every input, with
   the magnifier inside it. */
.search-box { position: relative; display: inline-flex; align-items: center; min-width: 240px; flex: 0 1 340px; }
.search-box .ic, .search-box .ic-sm {
    position: absolute; left: 13px;
    color: var(--ms-muted);
    pointer-events: none;
    width: 18px; height: 18px;
}
.search-box input[type=search], .search-box input[type=text] {
    width: 100%;
    border: 1.5px solid var(--ms-line);
    border-radius: 999px;
    padding: 11px 15px 11px 40px;
    font-family: var(--ms-font);
    font-size: 15px;
    color: var(--ms-ink);
    background: #fff;
    min-height: 44px;
    transition: border-color .15s, box-shadow .15s;
}
.search-box input:focus { outline: none; border-color: var(--ms-green-500); box-shadow: 0 0 0 3px var(--ms-green-100); }
.search-box input::-webkit-search-cancel-button { cursor: pointer; }
@media (max-width: 700px) { .search-box { flex: 1 1 100%; min-width: 0; } }

/* ---------------------------------------------------------------- alerts + flashes */
.alert {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    border-radius: var(--mt-radius-sm);
    padding: 12px 15px;
    font-size: 14.5px;
    font-weight: 600;
    margin-bottom: 14px;
    border: 1px solid transparent;
}
.alert .ic { width: 19px; height: 19px; margin-top: 1px; }
.alert > div { min-width: 0; }
.alert-success { background: var(--ms-green-50); color: var(--ms-green-800); border-color: var(--ms-green-100); }
.alert-error { background: var(--mt-red-bg); color: var(--ms-danger); border-color: #f6c8c8; }
.alert-info { background: var(--ms-navy-50); color: var(--ms-navy-800); border-color: var(--ms-navy-100); }
.alert-warn { background: var(--mt-amber-bg); color: var(--mt-amber); border-color: #fde3a7; }
.alert .btn { margin-left: auto; align-self: center; }

/* ---------------------------------------------------------------- modal */
.modal-back {
    position: fixed; inset: 0;
    background: rgba(8,27,51,.55);
    display: none;
    align-items: center; justify-content: center;
    z-index: 300;
    padding: 18px;
}
.modal-back.open { display: flex; }
.modal {
    background: #fff;
    border-radius: var(--mt-radius);
    border-top: 3px solid var(--mt-brand-accent);
    box-shadow: var(--mt-shadow-lg);
    padding: 24px;
    max-width: 560px;
    width: 100%;
    max-height: 88vh;
    overflow-y: auto;
}
.modal.wide { max-width: 760px; }
.modal .card-title { margin-bottom: 10px; }

/* ---------------------------------------------------------------- toast (window.mtToast) */
.mt-toast {
    position: fixed;
    left: 50%;
    bottom: calc(var(--mt-bottom-h) + env(safe-area-inset-bottom, 0px) + 18px);
    transform: translateX(-50%) translateY(8px);
    background: var(--ms-navy-900);
    color: #fff;
    border-radius: 999px;
    padding: 11px 18px;
    font-size: 14px;
    font-weight: 700;
    box-shadow: var(--mt-shadow-lg);
    z-index: 500;
    max-width: calc(100vw - 32px);
    opacity: 0;
    transition: opacity .2s ease, transform .2s ease;
    pointer-events: none;
}
.mt-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.mt-toast.bad { background: var(--ms-danger); }
.mt-toast.ok { background: var(--ms-green-700); }
@media (min-width: 1024px) { .mt-toast { bottom: 28px; } }

/* ---------------------------------------------------------------- misc */
.empty {
    text-align: center;
    color: var(--ms-muted);
    padding: 38px 18px;
}
.empty .ic { width: 42px; height: 42px; color: var(--ms-navy-100); margin-bottom: 8px; }
.empty strong { display: block; color: var(--ms-navy-900); font-size: 16px; margin-bottom: 4px; }
.empty .btn { margin-top: 12px; }
.avatar {
    width: 38px; height: 38px; border-radius: 50%;
    background: var(--ms-navy-800); color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: 14px; flex: 0 0 auto;
    overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar.green { background: var(--ms-green-700); }
.avatar.lg { width: 64px; height: 64px; font-size: 22px; }
.avatar.xl { width: 96px; height: 96px; font-size: 32px; }
/* A vehicle's number plate, written the way a plate is printed */
.plate {
    display: inline-block;
    font-weight: 800;
    letter-spacing: .08em;
    font-variant-numeric: tabular-nums;
    border: 1.5px solid var(--ms-navy-900);
    border-radius: 6px;
    padding: 1px 8px;
    font-size: 12.5px;
    color: var(--ms-navy-900);
    background: #fff;
    white-space: nowrap;
}
/* A vehicle with no photo yet: a compact tile with the vehicle's icon, never an empty
   grey box that reads as a broken image. The photo slot (.veh-photo, drawn by
   people_css() in app/drivers/_shared.php) keeps its photo shape only when there is
   a photo to fill it; fleet/index.php adds .no-photo beside .veh-photo. */
.veh-photo.no-photo, .veh-grid .veh-photo.no-photo {
    aspect-ratio: auto;
    min-height: 84px;
    flex-direction: column;
    gap: 6px;
    padding: 14px;
    color: var(--ms-navy-600);
    background: linear-gradient(160deg, var(--ms-navy-50), #e2eaf3);
}
.veh-photo.no-photo .ic-lg { width: 36px; height: 36px; }
.veh-photo.no-photo .micro { color: var(--ms-navy-500); font-size: 10.5px; }
.suite-line { text-align: center; font-size: 12px; color: var(--ms-muted); padding: 18px 0 8px; line-height: 1.6; }
.suite-line a { color: var(--ms-green-700); }
/* The registration numbers under the suite line: the smallest print in the app. */
.suite-reg { text-align: center; font-size: 10.5px; letter-spacing: .02em; color: var(--ms-muted); opacity: .75; padding: 0 0 10px; }
.qr-chip { background: #fff; border: 1px solid var(--ms-line); border-radius: var(--mt-radius); padding: 12px; display: inline-block; }

/* Star ratings. Display: five star icons, .on filled. Input: radio buttons behind
   44px labels (rate.php), highlighted up to the hovered or chosen star. */
.stars { display: inline-flex; gap: 2px; color: #e8a915; vertical-align: middle; }
.stars .ic, .stars .ic-sm { width: 18px; height: 18px; }
.stars .off { color: #cfd7e0; }
.stars-num { font-weight: 800; color: var(--ms-navy-900); margin-left: 6px; font-variant-numeric: tabular-nums; }
.stars-input { display: inline-flex; flex-direction: row-reverse; gap: 2px; }
.stars-input input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.stars-input label { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; cursor: pointer; color: #cfd7e0; border-radius: 50%; }
.stars-input label .ic { width: 30px; height: 30px; }
.stars-input input:checked ~ label, .stars-input label:hover, .stars-input label:hover ~ label { color: #e8a915; }
.stars-input input:focus-visible + label { outline: 2px solid var(--ms-green-600); }

/* Generic timeline (audit rows, a ticket's history). The TRIP timeline is .route-line below. */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { position: relative; padding: 0 0 18px 26px; }
.timeline li::before {
    content: ""; position: absolute; left: 5px; top: 6px;
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--ms-green-500);
}
.timeline li.tl-navy::before { background: var(--ms-navy-500); }
.timeline li.tl-red::before { background: var(--ms-danger); }
.timeline li.tl-grey::before { background: #9aa7b5; }
.timeline li::after {
    content: ""; position: absolute; left: 8.5px; top: 17px; bottom: -2px;
    width: 2px; background: var(--ms-line);
}
.timeline li:last-child::after { display: none; }
.timeline .tl-time { font-size: 12px; color: var(--ms-muted); }

/* ---------------------------------------------------------------- the route line
   THE TRIP TIMELINE. Rendered ONLY by trip_timeline_html() (includes/trips.php) on
   every screen (office view, driver screen, client portal, public quote), and
   appended to by assets/js/live.js from the same server-rendered <li> markup.

   Markup contract:
     <ol class="route-line">
       <li class="rl-item rl-boarded" data-event-id="123">
         <div class="rl-body">
           <div class="rl-top">
             <span class="rl-time">06:42</span>
             <span class="rl-tag recorded">Recorded</span>      (or .rl-tag.estimated)
           </div>
           <div class="rl-label">Thando M. boarded at Home</div>
           <div class="rl-detail">Sipho N. (driver)</div>        (optional)
         </div>
       </li>
     </ol>
   Kinds and their markers, per the spec:
     rl-checkin     navy dot            rl-pickup     hollow green ring
     rl-boarded     solid green dot     rl-checkpoint small grey dot
     rl-offboarded  solid navy dot      rl-arrived / rl-completed  flag
     rl-cancelled   red dot             rl-note       dashed grey ring
     rl-departed    green dot (small)   rl-eta        dashed amber ring (an estimate)
     rl-incident    hollow red ring     rl-position   tiny grey dot (a breadcrumb)
     rl-planned     a stop not reached yet (faded, dashed line after it)
   Every marker is drawn by ::before and the route by ::after, so the PHP renderer
   emits no marker element at all. */
.route-line { list-style: none; margin: 0; padding: 4px 0 0; }
.rl-item { position: relative; padding: 0 0 18px 34px; min-height: 30px; }
.rl-item::after {
    content: ""; position: absolute; left: 8px; top: 20px; bottom: -4px;
    width: 2px; background: var(--mt-brand-accent); opacity: .55;
}
.rl-item:last-child::after { display: none; }
.rl-item::before {
    content: ""; position: absolute; left: 2px; top: 5px; z-index: 1;
    width: 14px; height: 14px; border-radius: 50%;
    background: #fff; border: 2.5px solid var(--ms-navy-500);
}
.rl-checkin::before { background: var(--ms-navy-600); border-color: var(--ms-navy-600); }
.rl-pickup::before { background: #fff; border-color: var(--ms-green-500); }
.rl-boarded::before { background: var(--ms-green-500); border-color: var(--ms-green-500); box-shadow: 0 0 0 3px rgba(46,158,58,.18); }
.rl-departed::before { background: var(--ms-green-600); border-color: var(--ms-green-600); width: 12px; height: 12px; left: 3px; top: 6px; }
.rl-checkpoint::before, .rl-position::before { width: 9px; height: 9px; left: 4.5px; top: 8px; background: #9aa7b5; border-color: #9aa7b5; }
.rl-position::before { width: 7px; height: 7px; left: 5.5px; top: 9px; background: #c3ccd6; border-color: #c3ccd6; }
.rl-offboarded::before { background: var(--ms-navy-800); border-color: var(--ms-navy-800); }
.rl-cancelled::before, .rl-no_show::before { background: var(--ms-danger); border-color: var(--ms-danger); }
.rl-incident::before { background: #fff; border-color: var(--ms-danger); }
.rl-note::before, .rl-message::before { background: #fff; border-color: #b6c1cd; border-style: dashed; border-width: 2px; }
.rl-eta::before { background: var(--mt-amber-bg); border-color: var(--mt-amber); border-style: dashed; border-width: 2px; }
.rl-planned::before { background: var(--ms-bg); border-color: #c3ccd6; }
.rl-planned::after { background: repeating-linear-gradient(180deg, #c3ccd6 0 4px, transparent 4px 8px); opacity: 1; }
.rl-planned .rl-label, .rl-planned .rl-time { color: var(--ms-muted); }
/* The flag: a pole and a pennant drawn as two gradients in the one pseudo-element. */
.rl-arrived::before, .rl-completed::before {
    border: 0; border-radius: 0;
    width: 16px; height: 17px; left: 3px; top: 3px;
    background:
        linear-gradient(var(--ms-navy-900), var(--ms-navy-900)) 1px 0 / 2px 17px no-repeat,
        linear-gradient(var(--ms-green-500), var(--ms-green-500)) 3px 0 / 12px 9px no-repeat;
    background-color: transparent;
}
.rl-completed::before {
    background:
        linear-gradient(var(--ms-navy-900), var(--ms-navy-900)) 1px 0 / 2px 17px no-repeat,
        linear-gradient(var(--ms-navy-800), var(--ms-navy-800)) 3px 0 / 12px 9px no-repeat;
}
.rl-body { min-width: 0; }
.rl-top { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 1px; }
.rl-time { font-weight: 800; font-size: 13.5px; color: var(--ms-navy-900); font-variant-numeric: tabular-nums; }
.rl-date { font-size: 12px; color: var(--ms-muted); font-weight: 600; }
.rl-label { font-weight: 600; font-size: 14.5px; color: var(--ms-ink); }
.rl-detail { font-size: 13px; color: var(--ms-muted); margin-top: 1px; }
.rl-detail a { font-weight: 700; }
/* recorded = a real device position or a real event; estimated = something the
   system calculated. The two must never look alike (scope 7 and 29). */
.rl-tag {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 10.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
    border-radius: 999px; padding: 1px 8px; line-height: 1.5;
    border: 1px solid transparent;
}
.rl-tag.recorded, .rl-tag-recorded { background: var(--ms-green-50); color: var(--ms-green-800); border-color: var(--ms-green-100); }
.rl-tag.estimated, .rl-tag-estimated { background: var(--mt-amber-bg); color: var(--mt-amber); border-style: dashed; border-color: #e7c27a; }
.rl-tag.manual, .rl-tag-manual { background: #eef1f5; color: var(--ms-muted); border-color: var(--ms-line); }
.rl-cancelled .rl-label, .rl-no_show .rl-label { color: var(--ms-danger); }
.rl-incident .rl-label { color: var(--ms-danger); }
.route-line.compact .rl-item { padding-bottom: 10px; }
.route-line.compact .rl-detail { display: none; }
/* Horizontal variant for a summary strip (planned stops across the top of a trip card) */
.route-strip { display: flex; align-items: flex-start; gap: 0; overflow-x: auto; padding: 6px 0 2px; scrollbar-width: none; }
.route-strip::-webkit-scrollbar { display: none; }
.rs-stop { position: relative; flex: 1 0 110px; padding: 20px 8px 0; text-align: center; font-size: 12.5px; color: var(--ms-muted); }
.rs-stop::before { content: ""; position: absolute; top: 4px; left: 50%; margin-left: -6px; width: 12px; height: 12px; border-radius: 50%; background: #fff; border: 2.5px solid var(--ms-green-500); z-index: 1; }
.rs-stop::after { content: ""; position: absolute; top: 9px; left: 50%; right: -50%; height: 2px; background: var(--mt-brand-accent); opacity: .5; }
.rs-stop:last-child::after { display: none; }
.rs-stop.done::before { background: var(--ms-green-500); }
.rs-stop.here::before { box-shadow: 0 0 0 4px rgba(46,158,58,.2); background: var(--ms-green-500); }
.rs-stop.dest::before { border-color: var(--ms-navy-800); }
.rs-stop.dest.done::before { background: var(--ms-navy-800); }
.rs-stop b { display: block; color: var(--ms-navy-900); font-size: 13px; }

/* ---------------------------------------------------------------- the manifest
   <div class="manifest">
     <div class="pax-row is-boarded" data-pax="12">
       <span class="avatar">TM</span>
       <div class="pax-name">Thando Mokoena <span class="pax-meta">Grade 4, pickup: Home</span></div>
       <span class="pax-status"><span class="chip chip-ok">Boarded 06:42</span></span>
       <div class="pax-actions"> ...buttons... </div>
     </div>
   </div> */
.manifest { display: flex; flex-direction: column; gap: 8px; }
.pax-row {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 10px 12px;
    border: 1px solid var(--ms-line);
    border-radius: var(--mt-radius-sm);
    background: #fff;
}
.pax-row .avatar { width: 36px; height: 36px; font-size: 13px; }
.pax-name { flex: 1 1 160px; min-width: 0; font-weight: 700; color: var(--ms-navy-900); }
.pax-meta { display: block; font-weight: 500; font-size: 12.5px; color: var(--ms-muted); }
.pax-status { flex: 0 0 auto; }
.pax-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.pax-row.is-boarded { background: var(--ms-green-50); border-color: var(--ms-green-100); }
.pax-row.is-off_boarded { background: var(--ms-navy-50); border-color: var(--ms-navy-100); }
.pax-row.is-not_present { background: var(--mt-amber-bg); border-color: #f5d78e; }
.pax-row.is-cancelled { opacity: .6; }
.manifest-sum { display: flex; gap: 14px; flex-wrap: wrap; font-size: 13px; color: var(--ms-muted); margin-bottom: 10px; }
.manifest-sum b { color: var(--ms-navy-900); }

/* ---------------------------------------------------------------- the journey map
   .journey-map wraps the inline SVG from trip_map_svg() plus the links row.
   SVG classes the renderer uses (and live.js updates):
     .jm-plan       dashed navy line between the planned stops
     .jm-route      the recorded route polyline (green)
     .jm-stop       a stop marker circle (+ .done once reached, + .dest for the destination)
     .jm-stop-num   the number inside a stop
     .jm-last       the last recorded position (+ .jm-last-ring pulses around it)
     .jm-grid       faint background grid lines
   The <svg> carries data-w, data-h, data-min-lat, data-max-lat, data-min-lng,
   data-max-lng and data-pad so a new position can be projected in the browser. */
.journey-map {
    position: relative;
    background: #eef2f6;
    border: 1px solid var(--ms-line);
    border-radius: var(--mt-radius);
    overflow: hidden;
    margin-bottom: 16px;
}
/* Only the map itself is full width. The "Open in" links under it carry small
   external-link icons (mt_icon('external', 'ic-sm')) that must stay icon-sized, so
   the full-width rule is scoped to the direct child and the link icons are pinned. */
.journey-map > svg { display: block; width: 100%; height: auto; max-height: 460px; }
.journey-map a svg, .jm-links svg { display: inline-block; width: 16px; height: 16px; flex: 0 0 auto; }
.jm-grid { stroke: rgba(12,35,64,.07); stroke-width: 1; }
.jm-plan { fill: none; stroke: var(--ms-navy-500); stroke-width: 2; stroke-dasharray: 6 5; stroke-linecap: round; stroke-linejoin: round; }
.jm-route { fill: none; stroke: var(--ms-green-600); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.jm-stop { fill: #fff; stroke: var(--ms-navy-800); stroke-width: 2.5; }
.jm-stop.done { fill: var(--ms-green-500); stroke: var(--ms-green-700); }
.jm-stop.dest { stroke: var(--ms-navy-900); stroke-width: 3; }
.jm-stop-num { font-family: var(--ms-font); font-size: 10px; font-weight: 800; fill: var(--ms-navy-900); text-anchor: middle; dominant-baseline: central; pointer-events: none; }
.jm-stop.done + .jm-stop-num { fill: #fff; }
.jm-last { fill: var(--ms-green-500); stroke: #fff; stroke-width: 2.5; }
.jm-last-ring { fill: none; stroke: var(--ms-green-500); stroke-width: 2; opacity: .7; animation: mt-ring 1.8s ease-out infinite; transform-origin: center; transform-box: fill-box; }
@keyframes mt-ring { 0% { transform: scale(.6); opacity: .8; } 100% { transform: scale(2.2); opacity: 0; } }
.jm-label { font-family: var(--ms-font); font-size: 11px; font-weight: 700; fill: var(--ms-navy-900); paint-order: stroke; stroke: #eef2f6; stroke-width: 3px; }
.jm-links { display: flex; gap: 6px 14px; flex-wrap: wrap; align-items: center; padding: 10px 14px; background: #fff; border-top: 1px solid var(--ms-line); font-size: 13px; }
.jm-links .micro { margin-right: 4px; }
.jm-links a { font-weight: 700; display: inline-flex; align-items: center; gap: 4px; }
.jm-legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 8px 14px 0; font-size: 12px; color: var(--ms-muted); }
.jm-legend span { display: inline-flex; align-items: center; gap: 6px; }
.jm-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.jm-empty { padding: 30px 18px; text-align: center; color: var(--ms-muted); font-size: 14px; }
.jm-eta { position: absolute; top: 10px; left: 10px; background: rgba(255,255,255,.94); border: 1px solid var(--ms-line); border-radius: 999px; padding: 5px 12px; font-size: 12.5px; font-weight: 700; color: var(--ms-navy-900); box-shadow: var(--mt-shadow); }

/* ---------------------------------------------------------------- the calendar
   Rendered by assets/js/calendar.js (trips/calendar.php) and reused by the driver
   and client trip lists where a month view helps. */
.cal { background: #fff; border: 1px solid rgba(221,228,236,.7); border-radius: var(--mt-radius); box-shadow: var(--mt-shadow); overflow: hidden; margin-bottom: 16px; }
.cal-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px 14px; border-bottom: 1px solid var(--ms-line); }
.cal-toolbar .spacer { flex: 1; }
.cal-title { font-weight: 800; font-size: 17px; color: var(--ms-navy-900); min-width: 160px; }
.cal-views { display: inline-flex; background: var(--ms-navy-50); border-radius: 999px; padding: 3px; }
.cal-views button, .cal-lanesw button {
    border: 0; background: transparent; color: var(--ms-navy-800);
    font-family: var(--ms-font); font-weight: 700; font-size: 13px;
    padding: 7px 14px; min-height: 36px; border-radius: 999px; cursor: pointer;
}
.cal-views button.active, .cal-lanesw button.active { background: var(--ms-navy-900); color: #fff; }
.cal-lanesw { display: inline-flex; background: var(--ms-navy-50); border-radius: 999px; padding: 3px; }
.cal-head { display: grid; grid-template-columns: repeat(7, 1fr); background: #f8fafc; border-bottom: 1px solid var(--ms-line); }
.cal-head span { padding: 8px 6px; font-size: 11px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--ms-muted); text-align: center; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-cell { min-height: 112px; border-right: 1px solid #edf1f6; border-bottom: 1px solid #edf1f6; padding: 6px; min-width: 0; }
.cal-cell:nth-child(7n) { border-right: 0; }
.cal-cell.other { background: #fafbfd; }
.cal-cell.other .cal-day-num { color: #b6c1cd; }
.cal-cell.weekend { background: #fcfcfd; }
.cal-day-num { display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 26px; border-radius: 999px; font-size: 13px; font-weight: 700; color: var(--ms-navy-900); margin-bottom: 4px; padding: 0 6px; }
.cal-cell.today .cal-day-num { background: var(--mt-brand-accent); color: #fff; }
.cal-event {
    display: flex; align-items: center; gap: 5px;
    background: var(--ms-navy-50); color: var(--ms-navy-900);
    border-left: 3px solid var(--ms-navy-500);
    border-radius: 6px; padding: 3px 6px; margin-bottom: 3px;
    font-size: 12px; font-weight: 600; line-height: 1.3;
    text-decoration: none; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    cursor: pointer; min-width: 0;
}
.cal-event:hover { text-decoration: none; background: var(--ms-navy-100); }
.cal-event .ev-time { font-weight: 800; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.cal-event .ev-name { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.cal-event.ev-live { background: var(--ms-green-50); border-left-color: var(--ms-green-500); color: var(--ms-green-900); }
.cal-event.ev-live .ev-time::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ms-green-500); margin-right: 4px; animation: mt-pulse 1.6s ease-in-out infinite; vertical-align: 1px; }
.cal-event.ev-done { background: #f1f4f8; border-left-color: #b6c1cd; color: var(--ms-muted); }
.cal-event.ev-warn { background: var(--mt-red-bg); border-left-color: var(--ms-danger); color: var(--ms-danger); }
.cal-event.ev-cancelled { background: #f1f4f8; border-left-color: #b6c1cd; color: var(--ms-muted); text-decoration: line-through; }
.cal-more { display: block; font-size: 11.5px; font-weight: 700; color: var(--ms-green-700); padding: 2px 6px; }
.cal-conflict { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 800; color: var(--ms-danger); }
/* Week and day: a time column and one column per day */
.cal-week { display: grid; grid-template-columns: 56px repeat(7, 1fr); }
.cal-day { display: grid; grid-template-columns: 56px 1fr; }
.cal-time { font-size: 11px; color: var(--ms-muted); text-align: right; padding: 4px 8px 0 0; border-right: 1px solid #edf1f6; border-bottom: 1px solid #edf1f6; font-variant-numeric: tabular-nums; }
.cal-slot { border-right: 1px solid #edf1f6; border-bottom: 1px solid #edf1f6; min-height: 48px; padding: 3px 4px; min-width: 0; }
.cal-slot.today { background: #f7fcf8; }
.cal-week-head { display: grid; grid-template-columns: 56px repeat(7, 1fr); background: #f8fafc; border-bottom: 1px solid var(--ms-line); }
.cal-week-head span { padding: 8px 4px; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--ms-muted); text-align: center; }
.cal-week-head span.today { color: var(--ms-green-700); }
.cal-week-head span b { display: block; font-size: 15px; color: var(--ms-navy-900); text-transform: none; letter-spacing: 0; }
/* Lanes: one row per driver or vehicle across the hours of one day */
.cal-lanes { display: grid; grid-template-columns: 180px 1fr; }
.cal-lane-head { padding: 10px 12px; border-right: 1px solid var(--ms-line); border-bottom: 1px solid #edf1f6; font-weight: 700; font-size: 13.5px; color: var(--ms-navy-900); display: flex; align-items: center; gap: 8px; min-width: 0; }
.cal-lane-head span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-lane { position: relative; border-bottom: 1px solid #edf1f6; min-height: 52px; background-image: linear-gradient(90deg, #edf1f6 1px, transparent 1px); background-size: calc(100% / 24) 100%; }
.cal-lane .cal-event { position: absolute; top: 8px; height: 36px; margin: 0; display: flex; align-items: center; }
.cal-lane .cal-event.stacked { top: 46px; }
.cal-lane-hours { display: grid; grid-template-columns: repeat(24, 1fr); border-bottom: 1px solid var(--ms-line); background: #f8fafc; }
.cal-lane-hours span { font-size: 10px; color: var(--ms-muted); padding: 5px 0 5px 3px; border-left: 1px solid #edf1f6; font-variant-numeric: tabular-nums; }
.cal-empty { padding: 30px; text-align: center; color: var(--ms-muted); }
.cal-loading { padding: 20px; text-align: center; color: var(--ms-muted); font-size: 13.5px; }
.cal-legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 10px 14px; border-top: 1px solid var(--ms-line); font-size: 12px; color: var(--ms-muted); }
.cal-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }
@media (max-width: 900px) {
    .cal-cell { min-height: 74px; padding: 4px; }
    .cal-event .ev-name { display: none; }
    .cal-week { grid-template-columns: 44px repeat(7, 1fr); }
    .cal-week-head { grid-template-columns: 44px repeat(7, 1fr); }
    .cal-lanes { grid-template-columns: 110px 1fr; }
    .cal-title { min-width: 0; font-size: 15.5px; }
}
@media (max-width: 600px) {
    /* Month: a day cell shows its dots first, then a one-line "+N more" under them.
       calendar.js appends the link before the dots, so the order is set here. */
    .cal-cell { min-height: 58px; padding: 4px 2px; display: flex; flex-direction: column; }
    .cal-cell .cal-day-num { align-self: flex-start; }
    .cal-cell .cal-event { display: none; }
    .cal-cell .cal-dots { display: flex; gap: 3px; flex-wrap: wrap; order: 1; }
    .cal-cell .cal-more { display: block; order: 2; margin-top: 2px; padding: 0; font-size: 10px; line-height: 1.3; letter-spacing: -.03em; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
    .cal-dots i { display: block; width: 7px; height: 7px; border-radius: 50%; background: var(--ms-navy-500); }
    .cal-dots i.ev-live { background: var(--ms-green-500); }
    .cal-dots i.ev-done { background: #b6c1cd; }
    .cal-dots i.ev-warn { background: var(--ms-danger); }
    /* Week: still one column per day, scrolled sideways inside the calendar body
       (the head and the grid share the body, so they scroll together). Hiding the
       other days would leave the hour labels and today's slots flowing under the
       wrong day headers. */
    .cal-body { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }
    .cal-week, .cal-week-head { min-width: 640px; }
}

/* ---------------------------------------------------------------- "Today on the road"
   The dashboard hero. Dark navy, a decorative green route with stops drawn along
   the right edge, live counters and the next departures. */
.hero-road {
    position: relative;
    overflow: hidden;
    background: linear-gradient(150deg, var(--ms-navy-950), var(--mt-brand-primary) 55%, var(--ms-navy-800));
    color: #fff;
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow-lg);
    padding: 22px 24px;
    margin-bottom: 16px;
    border: 1px solid rgba(255,255,255,.08);
}
.hero-road::before {
    content: "";
    position: absolute; right: -20px; top: -30px; bottom: -30px; width: 380px;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 380 320'><path d='M40 310 C 120 240, 90 160, 190 150 S 300 60, 350 10' fill='none' stroke='%234ab653' stroke-width='3' stroke-linecap='round'/><circle cx='40' cy='310' r='8' fill='%234ab653'/><circle cx='150' cy='170' r='8' fill='%230c2340' stroke='%234ab653' stroke-width='3'/><circle cx='262' cy='108' r='8' fill='%230c2340' stroke='%234ab653' stroke-width='3'/><circle cx='350' cy='10' r='8' fill='%234ab653'/></svg>") no-repeat right top / contain;
    opacity: .28;
    pointer-events: none;
}
.hero-road > * { position: relative; z-index: 1; }
.hero-road .micro { color: rgba(255,255,255,.55); }
.hr-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.hr-title { font-size: 22px; font-weight: 800; color: #fff; line-height: 1.2; }
.hr-title .live-dot { margin-right: 8px; vertical-align: 2px; }
.hr-date { color: rgba(255,255,255,.65); font-weight: 700; font-size: 13px; }
.hr-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 10px; margin-bottom: 16px; }
.hr-stat {
    display: flex; flex-direction: column;
    background: rgba(255,255,255,.07);
    border: 1px solid rgba(255,255,255,.12);
    border-radius: var(--mt-radius-sm);
    padding: 12px 14px;
    color: #fff;
    text-decoration: none;
    min-width: 0;
}
a.hr-stat:hover { background: rgba(255,255,255,.12); text-decoration: none; }
.hr-num { font-size: 26px; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; }
.hr-num.live { color: var(--ms-green-400); }
.hr-num.warn { color: #f6c26b; }
.hr-num.bad { color: #ff9d9d; }
.hr-label { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; font-weight: 800; color: rgba(255,255,255,.6); margin-top: 2px; }
.hr-next .micro { display: block; margin-bottom: 4px; }
.hr-trip {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; margin-top: 8px;
    background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.08);
    border-radius: var(--mt-radius-sm);
    color: #fff; text-decoration: none; min-width: 0;
}
a.hr-trip:hover { background: rgba(255,255,255,.12); text-decoration: none; }
.hr-time { font-weight: 800; color: var(--ms-green-400); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.hr-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.hr-sub { display: block; font-size: 12.5px; color: rgba(255,255,255,.6); font-weight: 500; overflow: hidden; text-overflow: ellipsis; }
.hr-trip .chip { flex: 0 0 auto; }
.hr-empty { color: rgba(255,255,255,.65); font-size: 14px; padding: 8px 0 2px; }
.hr-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.hr-actions .btn-outline { border-color: rgba(255,255,255,.45); color: #fff; }
.hr-actions .btn-outline:hover { background: rgba(255,255,255,.1); color: #fff; }
@media (max-width: 700px) {
    .hero-road { padding: 18px 16px; }
    .hero-road::before { width: 220px; opacity: .18; }
    .hr-title { font-size: 19px; }
    .hr-stats { grid-template-columns: repeat(2, 1fr); }
    /* Five counters in two columns: the odd one out takes the whole last row. */
    .hr-stats > .hr-stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------- pricing cards
   Shared by the landing page and the in-app upgrade page, so they cannot drift. */
.price-grid { display: grid; grid-template-columns: 1fr 1.08fr 1fr; gap: 20px; align-items: stretch; }
.price-card {
    background: #fff;
    border: 1px solid rgba(221,228,236,.8);
    border-radius: var(--mt-radius);
    box-shadow: var(--mt-shadow);
    padding: 28px 24px;
    display: flex; flex-direction: column;
    position: relative;
}
.price-card.hot {
    margin-block: -18px;
    background: linear-gradient(165deg, var(--ms-navy-950), var(--ms-navy-800));
    border: 1px solid var(--ms-navy-700);
    padding: 40px 28px 34px;
    box-shadow: 0 26px 60px rgba(8,27,51,.28);
}
.price-card.hot::before, .price-card.current::before {
    content: "Most popular";
    position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
    background: var(--ms-green-500); color: #fff;
    font-size: 11px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
    border-radius: 999px; padding: 5px 15px; white-space: nowrap;
}
.price-card.current::before { content: "Your plan"; background: var(--ms-navy-700); }
.price-card.current { border-color: var(--ms-navy-500); }
.price-card h3 { font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: var(--ms-green-700); margin-bottom: 2px; }
.price-card .price { font-size: 36px; font-weight: 800; color: var(--ms-navy-900); margin: 6px 0 0; letter-spacing: -.02em; line-height: 1.1; }
.price-card .price small { font-size: 14px; font-weight: 600; color: var(--ms-muted); letter-spacing: 0; }
.price-card .annual-line { color: var(--ms-muted); font-size: 13px; margin: 4px 0 18px; }
.price-card.hot h3 { color: var(--ms-green-400); }
.price-card.hot .price { color: #fff; }
.price-card.hot .price small, .price-card.hot .annual-line { color: rgba(255,255,255,.65); }
.pc-lim { margin-bottom: 7px; }
.price-card.hot .pc-lim { color: rgba(255,255,255,.5); }
.pc-specs { display: grid; grid-template-columns: repeat(3, 1fr); background: var(--ms-bg); border: 1px solid var(--ms-line); border-radius: var(--mt-radius-sm); overflow: hidden; margin-bottom: 18px; }
.pc-spec { padding: 12px 8px; text-align: center; border-left: 1px solid var(--ms-line); min-width: 0; }
.pc-spec:first-child { border-left: 0; }
.pc-spec-n { display: block; font-size: 21px; font-weight: 800; color: var(--ms-navy-900); line-height: 1.15; font-variant-numeric: tabular-nums; }
.pc-spec-n.word { font-size: 14px; padding: 4px 0; }
.pc-spec .micro { display: block; margin-top: 2px; font-size: 10px; }
.price-card.hot .pc-specs { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); }
.price-card.hot .pc-spec { border-left-color: rgba(255,255,255,.14); }
.price-card.hot .pc-spec-n { color: #fff; }
.price-card.hot .pc-spec .micro { color: rgba(255,255,255,.55); }
.price-card ul { list-style: none; padding: 0; margin: 0 0 22px; flex: 1; }
.price-card ul li { padding: 7px 0 7px 26px; position: relative; font-size: 14.5px; }
.price-card ul li::before { content: ""; position: absolute; left: 0; top: 11px; width: 14px; height: 14px; background: var(--ms-green-100); border-radius: 50%; }
.price-card ul li::after { content: ""; position: absolute; left: 4px; top: 15px; width: 6px; height: 3.5px; border-left: 2px solid var(--ms-green-700); border-bottom: 2px solid var(--ms-green-700); transform: rotate(-45deg); }
.price-card.hot ul li { color: rgba(255,255,255,.88); }
.price-card.hot ul li::before { background: rgba(74,182,83,.22); }
.price-card.hot ul li::after { border-left-color: var(--ms-green-400); border-bottom-color: var(--ms-green-400); }
.price-card .btn { margin-top: auto; }
.vat-note { text-align: center; color: var(--ms-muted); font-size: 13px; margin-top: 22px; }
@media (max-width: 980px) {
    .price-grid { grid-template-columns: 1fr; gap: 26px; max-width: 480px; margin: 0 auto; }
    .price-card.hot { margin-block: 0; padding: 40px 24px 30px; }
}
@media (max-width: 480px) {
    .price-card { padding: 24px 18px; }
    .price-card.hot { padding: 36px 18px 26px; }
}

/* ---------------------------------------------------------------- AI spend charts
   Weram console only. One series per chart, one hue, no legend; the geometry is
   drawn in PHP (ai_usage_chart_svg, includes/ai_usage.php). */
.aiviz { margin: 6px 0 18px; }
.aiviz-head { font-size: 13.5px; font-weight: 600; color: var(--ms-navy-900); }
.aiviz-sub { font-size: 12.5px; color: var(--ms-muted); margin: 2px 0 6px; }
.aiviz-svg { display: block; width: 100%; height: auto; }
.aiviz-grid { stroke: var(--ms-line); stroke-width: 1; }
.aiviz-axis { stroke: var(--ms-line); stroke-width: 1; }
.aiviz-tick { fill: var(--ms-muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.aiviz-val { fill: var(--ms-ink); font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.aiviz-bar { fill: var(--ms-green-600); }
.aiviz-hit { fill: transparent; }
.aiviz-b:hover .aiviz-bar { fill: var(--ms-green-800); }

/* ---------------------------------------------------------------- UI primitives (design round 30/09/2026)
   The shared pieces of the simpler screens. Every builder uses exactly these class names
   (CONVENTIONS "UI primitives"); a page adds its own look on top in its own stylesheet.

   .sheet        a bottom sheet: <dialog class="sheet" id="x" aria-labelledby="x-title">
                   <span class="sheet-handle" aria-hidden="true"></span>
                   <h2 class="sheet-title" id="x-title">...</h2> ... </dialog>
                 Opened by [data-sheet-open="#x"], closed by [data-sheet-close], the scrim, Escape
                 and Android Back (app.js, window.mtSheet). 20px top radius, at most 88svh tall,
                 scrolls inside. A centred panel from 1024px up.
   .foot-bar     the fixed action bar at the foot of a focus screen, over the safe area:
                   <div class="foot-bar" data-genersys-keep-clear>
                     <p class="foot-why" aria-live="polite">Pick a day</p>      (the reason line)
                     <button class="foot-more" type="button" aria-label="More">...</button>
                     <button class="btn btn-green">Next: when?</button> </div>
                 Above the bottom nav on a phone page that has one; the page keeps room for it.
   .tap-row      a 64px list row: <a class="tap-row" href=".."><span class="tap-ic">icon</span>
                   <span class="tap-main">Title<span class="tap-sub">grey line</span></span>
                   <span class="tap-end">chip or chevron</span></a>. .tap-list groups rows in a card.
   .choice       a 72px card radio: <label class="choice"><input type="radio" name="day_pick"
                   value="today"><span class="choice-main">Today</span><span class="choice-sub">Wed 30/09
                   </span></label>, in .choice-grid; :checked (or .is-on) turns it green.
   .chip-choice  a capsule radio chip: <label class="chip-choice"><input type="radio" name="time_pick"
                   value="07:30"><span>07:30</span></label>, in .chip-choice-row.
   .step-rail    the route line laid sideways: <ol class="step-rail"><li class="is-done"><span>Where
                   </span></li><li class="is-now" aria-current="step"><span>When</span></li>...</ol>
   body.is-focus a page that set $MT_FOCUS = true: no bottom nav, no GenersysAI button, the top bar
                 carries the back arrow, the title and $MT_TOP_EXTRA only. */

/* ---- .sheet */
dialog.sheet {
    position: fixed;
    inset: auto 0 0 0;
    margin: 0;
    width: 100%;
    max-width: 100%;
    max-height: 88vh;
    padding: 6px 16px calc(18px + env(safe-area-inset-bottom, 0px));
    border: 0;
    border-radius: 20px 20px 0 0;
    background: var(--ms-card);
    color: var(--ms-ink);
    box-shadow: 0 -14px 44px rgba(8,27,51,.3);
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 450;               /* the non-modal fallback (no showModal): over the bottom nav and the assistant */
    font-family: var(--ms-font);
}
@supports (height: 1svh) { dialog.sheet { max-height: 88svh; } }
dialog.sheet:not([open]) { display: none; }
dialog.sheet[open] { animation: mt-sheet-up .24s cubic-bezier(.2,.8,.3,1) both; }
dialog.sheet::backdrop { background: rgba(8,27,51,.55); }
@keyframes mt-sheet-up { from { transform: translateY(24px); opacity: .4; } to { transform: none; opacity: 1; } }
/* The scrim app.js puts behind a sheet opened without showModal (an old browser). */
.sheet-scrim { position: fixed; inset: 0; z-index: 449; background: rgba(8,27,51,.55); }
.sheet-handle { display: block; width: 44px; height: 5px; border-radius: 999px; background: #cfd7e0; margin: 6px auto 14px; }
.sheet-title { font-size: 19px; font-weight: 800; color: var(--ms-navy-900); line-height: 1.25; margin: 0 0 14px; }
.sheet-close-row { display: flex; justify-content: center; margin-top: 14px; }
.sheet-close-row .btn { min-width: 160px; }
body.sheet-open { overflow: hidden; }
@media (min-width: 1024px) {
    dialog.sheet {
        inset: 0;
        margin: auto;
        width: min(560px, calc(100vw - 40px));
        height: fit-content;
        max-height: 82vh;
        padding: 18px 22px 20px;
        border-radius: var(--mt-radius);
        box-shadow: var(--mt-shadow-lg);
    }
    .sheet-handle { display: none; }
}

/* ---- .foot-bar */
.foot-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 225;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
    background: rgba(255,255,255,.97);
    border-top: 1px solid var(--ms-line);
    box-shadow: 0 -6px 20px rgba(12,35,64,.08);
}
.foot-bar > .btn, .foot-bar > form { flex: 1 1 0; min-width: 0; }
.foot-bar > form { display: flex; margin: 0; }
.foot-bar > .btn, .foot-bar > form > .btn { min-height: 60px; font-size: 16.5px; white-space: normal; line-height: 1.2; }
.foot-bar > form > .btn { width: 100%; }
.foot-why {
    flex: 1 1 100%;
    order: -1;
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin: 0;
    font-size: 13.5px; font-weight: 700; line-height: 1.3;
    color: var(--mt-amber);
    text-align: center;
}
.foot-why::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--mt-amber); flex: 0 0 auto; }
.foot-why:empty { display: none; }
.foot-more {
    flex: 0 0 60px;
    width: 60px; height: 60px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: 1.5px solid var(--ms-line);
    background: #fff;
    color: var(--ms-navy-800);
    cursor: pointer;
    font-family: var(--ms-font);
    position: relative;
}
.foot-more:hover { background: var(--ms-navy-50); }
.foot-more:focus-visible { outline: 3px solid var(--ms-green-400); outline-offset: 2px; }
.foot-more .ic { width: 24px; height: 24px; }
.foot-more .side-badge { position: absolute; top: -2px; right: -2px; }
/* A page with a foot bar keeps room for it at the foot, so its last row scrolls clear. */
.app-main:has(.foot-bar) { padding-bottom: calc(150px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 1024px) {
    .foot-bar { left: var(--mt-side-w); padding-left: 26px; padding-right: 26px; }
    .foot-bar > .btn, .foot-bar > form { flex: 0 1 360px; }
    .foot-bar { justify-content: flex-end; }
}
/* A phone page that still has the bottom nav: the bar sits on top of it. */
@media (max-width: 1023px) {
    body:not(.is-focus) .foot-bar { bottom: calc(var(--mt-bottom-h) + env(safe-area-inset-bottom, 0px)); padding-bottom: 10px; }
    body:not(.is-focus) .app-main:has(.foot-bar) { padding-bottom: calc(var(--mt-bottom-h) + 150px + env(safe-area-inset-bottom, 0px)); }
}

/* ---- .tap-row */
.tap-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 64px;
    padding: 10px 14px;
    background: #fff;
    border: 1px solid rgba(221,228,236,.85);
    border-radius: var(--mt-radius-sm);
    color: inherit;
    font-family: var(--ms-font);
    font-size: 15px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.tap-row + .tap-row { margin-top: 8px; }
a.tap-row:hover, button.tap-row:hover, label.tap-row:hover { border-color: var(--ms-green-500); text-decoration: none; }
.tap-row:focus-visible { outline: 3px solid var(--ms-green-400); outline-offset: 2px; }
.tap-ic {
    flex: 0 0 40px;
    width: 40px; height: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--ms-navy-50);
    color: var(--ms-navy-800);
}
.tap-ic .ic, .tap-ic .ic-sm { width: 20px; height: 20px; }
.tap-ic.is-green { background: var(--ms-green-50); color: var(--ms-green-700); }
.tap-ic.is-amber { background: var(--mt-amber-bg); color: var(--mt-amber); }
.tap-ic.is-red { background: var(--mt-red-bg); color: var(--ms-danger); }
.tap-ic.is-navy { background: var(--ms-navy-900); color: #fff; }
.tap-main { flex: 1 1 auto; min-width: 0; display: block; font-weight: 700; color: var(--ms-navy-900); line-height: 1.3; overflow-wrap: anywhere; }
.tap-sub { display: block; margin-top: 2px; font-weight: 500; font-size: 13px; color: var(--ms-muted); line-height: 1.4; }
.tap-end { flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; gap: 6px; color: var(--ms-muted); font-size: 13px; font-weight: 700; }
.tap-end .ic, .tap-end .ic-sm { color: #9aa7b5; }
/* Rows inside one card, divided by hairlines instead of each carrying its own border. */
.tap-list { display: flex; flex-direction: column; }
.tap-list > .tap-row { border: 0; border-radius: 0; border-bottom: 1px solid #edf1f6; padding-left: 2px; padding-right: 2px; background: transparent; }
.tap-list > .tap-row:last-child { border-bottom: 0; }
.tap-list > .tap-row + .tap-row { margin-top: 0; }

/* ---- .choice (card radio) and .chip-choice (capsule radio) */
.choice-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 10px; }
.choice {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    min-height: 72px;
    padding: 10px 8px;
    background: #fff;
    border: 1.5px solid var(--ms-line);
    border-radius: 14px;
    color: var(--ms-navy-900);
    font-family: var(--ms-font);
    text-align: center;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    min-width: 0;
}
.choice:hover { border-color: var(--ms-navy-500); }
.choice input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.choice-main { font-weight: 800; font-size: 15.5px; line-height: 1.2; }
.choice-sub { font-size: 12.5px; font-weight: 600; color: var(--ms-muted); }
.choice .ic, .choice .ic-sm { color: var(--ms-navy-600); }
.choice.is-on, .choice:has(input:checked) { border: 2px solid var(--ms-green-600); background: var(--ms-green-50); box-shadow: 0 0 0 3px rgba(46,158,58,.12); }
.choice.is-on .choice-sub, .choice:has(input:checked) .choice-sub { color: var(--ms-green-800); }
.choice:has(input:focus-visible) { outline: 3px solid var(--ms-green-400); outline-offset: 2px; }
.choice.is-dashed { border-style: dashed; }
.chip-choice-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-choice {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px;
    padding: 0 18px;
    background: #fff;
    border: 1.5px solid var(--ms-line);
    border-radius: 999px;
    color: var(--ms-navy-900);
    font-family: var(--ms-font);
    font-size: 15px; font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.chip-choice:hover { border-color: var(--ms-navy-500); }
.chip-choice input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.chip-choice.is-on, .chip-choice:has(input:checked) { border-color: var(--ms-green-600); background: var(--ms-green-50); color: var(--ms-green-900); box-shadow: 0 0 0 3px rgba(46,158,58,.12); }
.chip-choice:has(input:focus-visible) { outline: 3px solid var(--ms-green-400); outline-offset: 2px; }
.chip-choice.is-dashed { border-style: dashed; color: var(--ms-green-700); }

/* ---- .step-rail (the route line laid sideways) */
.step-rail { display: flex; list-style: none; margin: 0 0 16px; padding: 0; counter-reset: none; }
.step-rail > li {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    padding-top: 22px;
    text-align: center;
    font-size: 11.5px; font-weight: 700; line-height: 1.2;
    color: var(--ms-muted);
}
.step-rail > li > * { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the stop */
.step-rail > li::before {
    content: "";
    position: absolute; top: 2px; left: 50%; margin-left: -7px; z-index: 1;
    width: 14px; height: 14px; border-radius: 50%;
    background: #fff; border: 2.5px solid #c3ccd6;
    box-sizing: border-box;
}
/* the road to the next stop: dashed grey ahead, solid green behind */
.step-rail > li::after {
    content: "";
    position: absolute; top: 8px; left: 50%; right: -50%;
    height: 2px;
    background: repeating-linear-gradient(90deg, #c3ccd6 0 5px, transparent 5px 9px);
}
.step-rail > li:last-child::after { display: none; }
.step-rail > li.is-done { color: var(--ms-navy-900); }
.step-rail > li.is-done::before { background: var(--ms-green-500); border-color: var(--ms-green-500); }
.step-rail > li.is-done::after { background: var(--ms-green-500); }
.step-rail > li.is-now { color: var(--ms-navy-900); font-weight: 800; }
.step-rail > li.is-now::before { background: var(--ms-green-500); border-color: var(--ms-green-500); box-shadow: 0 0 0 5px rgba(46,158,58,.22); }
.step-rail a { color: inherit; text-decoration: none; }

/* ---- body.is-focus */
body.is-focus .bottom-nav, body.is-focus .gen-fab, body.is-focus .gen-scrim { display: none !important; }
body.is-focus .topbar-search, body.is-focus .live-chip, body.is-focus .top-utils { display: none !important; }
body.is-focus .topbar-co, body.is-focus .topbar-sep { display: none; }
body.is-focus .topbar-mark { display: none !important; }
body.is-focus .topbar-back { display: inline-flex; }
/* The close cross of a flow's first step turns into the arrow on the later steps (.is-arrow). */
.topbar-back.has-x .tb-arrow, .topbar-back.has-x.is-arrow .tb-x { display: none; }
.topbar-back.has-x.is-arrow .tb-arrow { display: inline-block; }
body.is-focus .topbar-extra { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
@media (max-width: 1023px) {
    body.is-focus .app-main { padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px)); }
    body.is-focus .app-main:has(.foot-bar) { padding-bottom: calc(150px + env(safe-area-inset-bottom, 0px)); }
}

/* ---------------------------------------------------------------- motion
   Movement is a cue, never decoration: things lift when you can click them, new
   panels settle in, and anything live keeps a slow pulse. All of it is short, and
   all of it is switched off for prefers-reduced-motion. */
.card, .tile, .btn, .chip, .sub-pills a, .side a, .live-chip, .price-card, .drive-btn,
.more-item, .hr-stat, .hr-trip, .pax-row, .cal-event, .top-btn, .acct-btn, .bottom-nav a {
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, background-color .15s ease, color .15s ease;
}
.card:hover { box-shadow: 0 8px 26px rgba(12,35,64,.10); }
a.tile:hover, .drive-btn:not([disabled]):hover, .more-item:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 28px rgba(12,35,64,.13);
}
.btn:not([disabled]):hover { transform: translateY(-1px); }
.btn:not([disabled]):active { transform: translateY(0); }
.sub-pills a:hover { transform: translateY(-1px); }
table.data tbody tr { transition: background-color .13s ease; }

/* Panels settle in on load, staggered so the eye follows the page down */
@keyframes mt-rise { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
.card, .tile, .panel-dark, .hero-road, .table-wrap, .cal, .drive-hero { animation: mt-rise .34s ease both; }
/* A transformed element becomes the containing block for its position:fixed children,
   so a container that holds a dialog does not animate (the modal would be trapped). */
.card:has(.modal-back), .tile:has(.modal-back), .panel-dark:has(.modal-back),
.hero-road:has(.modal-back), .table-wrap:has(.modal-back), .drive-hero:has(.modal-back) { animation: none; }
.board .tile:nth-child(2) { animation-delay: .03s; }
.board .tile:nth-child(3) { animation-delay: .06s; }
.board .tile:nth-child(4) { animation-delay: .09s; }
.board .tile:nth-child(5) { animation-delay: .12s; }
.board .tile:nth-child(6) { animation-delay: .15s; }
.board .tile:nth-child(7) { animation-delay: .18s; }
.board .tile:nth-child(8) { animation-delay: .21s; }
.board .tile:nth-child(9) { animation-delay: .24s; }
.board .tile:nth-child(10) { animation-delay: .27s; }
@keyframes mt-count { 0% { transform: translateY(4px); opacity: .55; } 100% { transform: none; opacity: 1; } }
.tile-num, .hr-num { display: inline-block; animation: mt-count .45s ease both; }
@keyframes mt-slide { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.alert { animation: mt-slide .28s ease both; }
@keyframes mt-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes mt-pop { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; transform: none; } }
.modal-back.open { animation: mt-fade .18s ease both; }
.modal-back.open .modal { animation: mt-pop .24s cubic-bezier(.2,.8,.3,1) both; }
/* A timeline entry that has just arrived (live.js) slides in from the route */
@keyframes mt-rl-in { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
.rl-item.rl-new { animation: mt-rl-in .35s ease both; }

/* The shine on the primary action: one soft band of light crosses the face when you
   point at the button or reach it with the keyboard, then waits off the left edge
   again. It travels once and never loops. */
.btn-green::after {
    content: "";
    position: absolute;
    top: -1px; bottom: -1px;
    left: -55%;
    width: 40%;
    background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.14) 50%, rgba(255,255,255,0) 100%);
    transform: translateX(0) skewX(-18deg);
    transition: transform 0s;
    pointer-events: none;
}
.btn-green:not([disabled]):hover::after,
.btn-green:not([disabled]):focus-visible::after {
    transform: translateX(430%) skewX(-18deg);
    transition: transform .62s cubic-bezier(.3,.72,.32,1);
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
    a.tile:hover, .drive-btn:hover, .more-item:hover, .btn:hover, .sub-pills a:hover { transform: none; }
    .btn-green:not([disabled]):hover::after,
    .btn-green:not([disabled]):focus-visible::after { transform: translateX(0) skewX(-18deg); }
    .live-dot, .chip-live::before, .jm-last-ring, .cal-event.ev-live .ev-time::before { animation: none; }
}

/* ---------------------------------------------------------------- print (shell) */
@media print {
    body { background: #fff; color: #000; }
    .no-print { display: none !important; }
    /* The app shell never prints: the fixed top bar and bottom nav would otherwise be
       drawn over every page of a printed list and cover its rows (print.css does the
       same for the documents). */
    .side, .topbar, .bottom-nav, .banner-stack, .more-sheet, .acct-drop,
    .gen-fab, .gen-panel, .gen-scrim, .mt-toast, dialog.sheet, .sheet-scrim, .foot-bar { display: none !important; }
    .app-main { margin: 0 !important; padding: 0 !important; max-width: none !important; min-height: 0 !important; }
    .card, .tile, .table-wrap, .alert, .panel-dark, .hero-road, .cal { animation: none !important; box-shadow: none; }
    .card { border: 1px solid #ccc; break-inside: avoid; }
    a { color: inherit; text-decoration: none; }
}
