/* The whole application stylesheet.
 *
 * It used to live inside BASE_STYLE in templates_shared.py, which meant
 * 46KB of identical CSS was written into every page and re-sent on every
 * navigation -- inline styles cannot be cached, so a phone paid for it on
 * every single tap. Out here the browser fetches it once and revalidates
 * with a 304 after that, and the link carries a ?v= built from this
 * file's own contents so an edit is picked up immediately rather than
 * waiting out a cache.
 *
 * Edit this file directly. templates_shared.py now only links to it.
 */
  :root{
  /* ------------------------------------------------------------------
     Tinted states: a card that is wrong, and a card that is nearly wrong.

     These were literals -- #fff6f6, #fffcf2, #e0a0a0 -- repeated across
     Follow-ups, Away From Zone, STA progress, Fleet Triage, Profitability and
     Inventory Breakdown. Fine on the five light themes and unreadable on the
     two dark ones: the card kept the theme's light text and took a near-white
     background of its own, so an alert -- the one card on the page that has to
     be read -- was the only one you could not. Same failure the --panel token
     was added for.

     Tokens, so a state that means "this is wrong" looks wrong in every theme
     and a new tinted thing has somewhere to get its colour from.
     ------------------------------------------------------------------ */
    --tint-red:#fff6f6;--tint-red-line:#e0a0a0;--tint-red-ink:#a52a2a;
    --tint-amber:#fffcf2;--tint-amber-line:#e4cf8f;
    --chip-amber:#f6e3c4;--chip-amber-ink:#7a5310;
    /* Green and blue, added for the same reason red and amber were: pages
       were painting 'this is fine' and 'this is information' with literal
       light hexes, which read as invisible the moment the ink went pale. */
    --tint-green:#eef7f1;--tint-green-line:#cfe3d8;--tint-green-ink:#1f7a4d;
    --tint-blue:#eef6ff;--tint-blue-line:#c9e0f7;--tint-blue-ink:#0b4f8a;

    --navy:#0e1c30;--navy-2:#152a45;--amber:#f2994a;--amber-dark:#d97b2c;
    --mist:#f5f7fa;--line:#e6e9ee;--ink:#141d29;--ink-soft:#5b6779;--surface:#ffffff;--panel:#eef2f7;
    --radius:12px;--shadow:0 1px 3px rgba(14,28,48,.07);--shadow-md:0 8px 24px rgba(14,28,48,.10);
    /* Body/heading font stacks -- var-ized so the two premium themes below
       can swap in Fraunces/Inter without touching every template. Default
       here is 'inherit' for --font-display, meaning headings just pick up
       whatever --font-body resolves to (i.e. no visual change) unless a
       theme below overrides it. */
    --font-body:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:inherit;
    /* --navy has always done two jobs: it paints the dark chrome (header,
       top nav, dropdowns) and it is also the strong ink for card headings,
       figures, links and active tabs -- 93 places across 20 files. On a
       light theme one value serves both. On a dark one they want opposite
       values, and the ink half disappears into the page.
       So the ink half now has a name. It defaults to var(--navy), which
       resolves per-element against whatever --navy the active theme set --
       so navy/forest/plum/premium/premium-dark compute exactly what they
       computed before this existed. A dark theme overrides it on its own. */
    --ink-strong:var(--navy);
  }

  /* Color themes -- see THEME_CHOICES/users.theme and NAV_HTML's theme
     picker. Each block only overrides the variables above; every rule
     elsewhere in this stylesheet that's written in terms of var(--x)
     picks the change up automatically. 'navy' (the :root defaults above)
     is deliberately not repeated here -- it's the fallback if data-theme
     is missing/unrecognized. Status/semantic colors (pills, zone badges,
     map markers, urgency colors, etc.) are deliberately NOT themed here --
     those carry meaning (red = urgent, green = good) independent of the
     chosen color scheme, so they stay fixed across all themes. */
  html[data-theme="forest"]{
    --navy:#0f2e22;--navy-2:#164634;--amber:#3fa66b;--amber-dark:#2f8353;
    --mist:#f3f8f5;--line:#dfeae4;--ink:#132019;--ink-soft:#59685f;--surface:#ffffff;--panel:#ecf3ef;
  }
  html[data-theme="plum"]{
    --navy:#2a1a3d;--navy-2:#3c2857;--amber:#a865c9;--amber-dark:#8a4bab;
    --mist:#f7f4fa;--line:#e8e0ee;--ink:#1e1528;--ink-soft:#6a5f75;--surface:#ffffff;--panel:#f1ecf6;
  }
  /* Premium (light) -- warmer neutrals, deepened navy, muted brass/gold
     accent instead of bright amber, Fraunces/Inter pairing. Same variable
     set as forest/plum above; --shadow/--shadow-md are left at the :root
     default (light-on-light shadows work fine here, unlike the dark
     variant below). */
  html[data-theme="premium"]{
    --navy:#0b1526;--navy-2:#14233d;--amber:#b08d57;--amber-dark:#96784a;
    --mist:#f7f5f1;--line:#ece8e0;--ink:#151d29;--ink-soft:#6b7280;--surface:#ffffff;--panel:#f7f5f1;
    --font-body:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:'Fraunces',Georgia,serif;
  }
  /* Premium (dark) -- near-black page, raised (not white) card surface,
     brighter gold accent, off-white (not pure white) body text. Shadows
     don't read against a near-black background, so --shadow/--shadow-md
     are redefined as a subtle 1px light ring instead of a drop shadow --
     elevation via edge, not via cast shadow. */
  html[data-theme="premium-dark"]{
    --tint-red:#2a1611;--tint-red-line:#5c2f22;--tint-red-ink:#f0a08a;--tint-amber:#241b0d;--tint-amber-line:#5d451c;--chip-amber:#3a2c12;--chip-amber-ink:#e8c48a;--tint-green:#10241c;--tint-green-line:#255440;--tint-green-ink:#7fd4a6;--tint-blue:#101f2c;--tint-blue-line:#25455f;--tint-blue-ink:#8ec5ee;
    --navy:#0a0f1a;--navy-2:#0a0f1a;--amber:#c9a968;--amber-dark:#b3924f;
    --mist:#0a0f1a;--line:#1e2836;--ink:#edeff3;--ink-soft:#8a93a3;--surface:#111726;--panel:#161d2c;
    --shadow:0 0 0 1px rgba(255,255,255,.06);
    --shadow-md:0 0 0 1px rgba(255,255,255,.08);
    --font-body:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:'Fraunces',Georgia,serif;
  }
  /* Daylight -- built for the phone in a yard at noon and the desk second.
     Deep marine carries the chrome AND the links/figures (--navy does both
     jobs in this stylesheet); hazard yellow is the accent, which is what
     makes the nav underline and the badges readable against a marine bar.
     Nothing here is new machinery: same nine variables as forest/plum, so
     every var(--x) rule picks it up and nothing else had to change.
     System font stack on purpose -- no webfont fetch on a phone in a yard,
     and Segoe UI / Roboto are what these devices already have. */
  html[data-theme="daylight"]{
    --navy:#0b3a63;--navy-2:#0f4f8a;--amber:#f2b705;--amber-dark:#d19c00;
    --mist:#eef2f5;--line:#dbe3e9;--ink:#0c1418;--ink-soft:#55646f;--surface:#ffffff;--panel:#e9eef2;
    --font-body:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:inherit;
  }
  /* Four rules in this stylesheet hardcode a colour that assumes the amber
     accent is orange. On yellow they either lose contrast or fight the
     marine chrome, so they are corrected here -- scoped to the theme, so
     navy/forest/plum/premium are untouched. */
  html[data-theme="daylight"] header .mark.mark-fallback{color:#231404;}
  html[data-theme="daylight"] input:focus{
    border-color:var(--navy-2);box-shadow:0 0 0 3px rgba(15,79,138,.18);
  }
  html[data-theme="daylight"] .topnav a{color:#c6d8ec;}
  html[data-theme="daylight"] .nav-theme-option.active{background:rgba(242,183,5,.18);}

  /* Yard Board -- the desk counterpart to Daylight. Charcoal ground, one
     signal amber, and the semantic colours re-cut so they read as light
     marks on a dark field instead of dark marks on a light one.

     The nine variables are only half the job here, and this is the part
     premium-dark never finished: sixty rules in this stylesheet hardcode a
     colour that assumes a light page -- table headers at #eef1f6, the
     expiry row tints, the pills, the zone badges. Swap the variables alone
     and you get a dark page wearing light-grey furniture. They are all
     corrected below, every one scoped to this theme, so navy/forest/plum/
     premium/premium-dark are untouched: additions only, nothing shared is
     edited.

     --navy is the awkward one. It paints the header gradient AND doubles as
     the ink for links, figures and active states (see .veh-link, .stat-num,
     .tab.active). Those two jobs want opposite values on a dark ground, so
     the variable stays dark for the chrome and the ink-role usages are
     re-pointed below -- figures to amber, links to a cool light blue. */
  html[data-theme="yardboard"]{
    --tint-red:#2a1611;--tint-red-line:#5c2f22;--tint-red-ink:#f2a892;--tint-amber:#241b0d;--tint-amber-line:#5d451c;--chip-amber:#3a2c12;--chip-amber-ink:#f0cf92;--tint-green:#10241c;--tint-green-line:#255440;--tint-green-ink:#7fd4a6;--tint-blue:#101f2c;--tint-blue-line:#25455f;--tint-blue-ink:#8ec5ee;
    --navy:#0e1519;--navy-2:#1b2329;--amber:#e2a33c;--amber-dark:#c98a28;
    --mist:#141a1e;--line:#2b363e;--ink:#eef3f6;--ink-soft:#93a3ae;--surface:#1e272d;--panel:#1a232a;
    --ink-strong:#cfe0ea;
    --shadow:0 0 0 1px rgba(255,255,255,.05);
    --shadow-md:0 0 0 1px rgba(255,255,255,.08);
    --font-body:"Barlow",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:"Barlow Semi Condensed","Barlow",system-ui,-apple-system,"Segoe UI",sans-serif;
  }
  /* Figures take the condensed display face -- they are the thing being
     read across a room, and condensed buys width for a longer number. */
  html[data-theme="yardboard"] .stat-num,
  html[data-theme="yardboard"] .count-pill{font-family:var(--font-display);letter-spacing:.01em;}
  /* 1. --navy in its ink role: figures take the accent, links go cool. */
  html[data-theme="yardboard"] .stat-num,
  html[data-theme="yardboard"] .pk-shot-status.good{color:#7fd4a6;}
  html[data-theme="yardboard"] .pk-shot-status.failed{color:#f2a892;}
  html[data-theme="yardboard"] .veh-link,
  html[data-theme="yardboard"] .job-ref-link,
  html[data-theme="yardboard"] .tab.active,
  html[data-theme="yardboard"] .carpark-section h2,
  html[data-theme="yardboard"] .auth-card h1,
  html[data-theme="yardboard"] .link-action:hover{color:#9fc9e8;}
  html[data-theme="yardboard"] .stat-card.active{
    border-color:var(--amber);box-shadow:0 0 0 2px var(--amber) inset;
  }
  html[data-theme="yardboard"] .tow-route-grid .tow-route-option:hover,
  html[data-theme="yardboard"] .doc-dropzone:hover{border-color:var(--amber);}

  /* 2. Table furniture. */
  html[data-theme="yardboard"] th,
  html[data-theme="yardboard"] th.sticky-col,
  html[data-theme="yardboard"] td.sticky-col,
  html[data-theme="yardboard"] .media-thumb .doc-icon{background:#232d34;}
  /* The inventory table's own header rule carries an id, so it outranks the
     bare th above and would leave a light band across the top of the table. */
  html[data-theme="yardboard"] #invTable thead tr:first-child th{background:#232d34;}
  /* Column headings sit at --ink-soft, which on charcoal is a mid grey --
     the shade a disabled control wears, so the whole header row reads as
     switched off. They are buttons; every one of them is clickable. Given
     a near-white they read as available, and the amber is then free to
     mean "this is the column you are sorted by". */
  html[data-theme="yardboard"] .sort-btn,
  html[data-theme="yardboard"] th.plain{color:#d5dfe6;}
  html[data-theme="yardboard"] .sort-btn:hover,
  html[data-theme="yardboard"] .sort-btn.sorted{color:var(--amber);background:#2b363e;}
  html[data-theme="yardboard"] tr:hover td{background:#243038;}
  html[data-theme="yardboard"] .table-scroll::-webkit-scrollbar-thumb{background:#3a4750;}
  html[data-theme="yardboard"] .table-scroll::-webkit-scrollbar-thumb:hover{background:#4a5962;}

  /* 3. State on the edge, not across the row.

        The light themes tint the whole row for an expiring car. On a
        charcoal ground a tint either disappears or turns into a highlight
        bar, and once three states are tinting rows the table stops having
        a background at all. So here the row keeps the page's own surface
        and the state is carried by the rail the stylesheet already draws
        in td:first-child -- widened to 4px and brightened, which is
        readable while scrolling at speed in a way a tint is not. */
  html[data-theme="yardboard"] tr.expiry-yellow td,
  html[data-theme="yardboard"] tr.expiry-red td,
  html[data-theme="yardboard"] tr.row-towing{background:transparent;}
  html[data-theme="yardboard"] tr.expiry-yellow:hover td,
  html[data-theme="yardboard"] tr.expiry-red:hover td,
  html[data-theme="yardboard"] tr.row-towing:hover td{background:#243038;}
  html[data-theme="yardboard"] tr.expiry-yellow td:first-child{border-left:4px solid #e6b23c;}
  html[data-theme="yardboard"] tr.expiry-red td:first-child{border-left:4px solid #ef8a83;}
  html[data-theme="yardboard"] tr.row-towing td:first-child{box-shadow:inset 4px 0 0 0 #6ea8f0;}

  /* 3b. The inventory table stripes its even rows light (that rule lives in
        inventory_routes.py's own <style>, not here) and paints the selected
        row pale blue. Both assume a light page. The whole point of moving
        state onto the rail is that every row then looks the same, so the
        stripe goes entirely -- one ground, and the only thing varying down
        the table is the 4px mark at the front.

        The selectors are matched rule for rule because the originals carry
        an id and three pseudo-classes; a shorter selector here would lose
        the specificity fight and silently do nothing. */
  html[data-theme="yardboard"] #invTable tbody tr:not(.expiry-yellow):not(.expiry-red):nth-child(even) td{
    background:transparent;
  }
  html[data-theme="yardboard"] #invTable tbody tr:not(.expiry-yellow):not(.expiry-red):hover td,
  html[data-theme="yardboard"] #invTable tbody tr:hover td{background:#243038;}
  html[data-theme="yardboard"] #invTable tbody tr.row-selected td{background:#1d3549 !important;}

  /* 3c. Zone tabs. Their rules also live in inventory_routes.py, where the
        selected tab is coloured var(--navy) -- which on this theme is the
        near-black that paints the header, so the selected tab reads as
        unreadable dark text on a dark page. The accent does that job here:
        selected is amber, and its count chip inverts to match. */
  /* Safety net for the same class of bug elsewhere: a link with no class
     at all is one nothing styled, so it is still on the browser default.
     Only classless links are touched -- anything with a class keeps
     whatever rule already owns it. */
  html[data-theme="yardboard"] a:not([class]){color:#9fc9e8;}
  html[data-theme="yardboard"] .zone-tab{color:#93a3ae;}
  html[data-theme="yardboard"] .zone-tab:hover{color:#e8eff3;}
  html[data-theme="yardboard"] .zone-tab.active{color:var(--amber);border-bottom-color:var(--amber);}
  html[data-theme="yardboard"] .zone-tab-count{background:#2b363e;color:#adbcc6;}
  html[data-theme="yardboard"] .zone-tab.active .zone-tab-count{background:var(--amber);color:#23180a;}

  /* 3d. The last light fills on the inventory page, all declared in that
        page's own <style> rather than here: the EPZ next-step badges, the
        recommended row in the yard-comparison table, and two warning
        boxes. Nothing in a row is allowed to come out white. */
  html[data-theme="yardboard"] .epz-badge-pending{background:#2e2617;color:#e6b23c;}
  html[data-theme="yardboard"] .epz-badge-sent{background:#1c2c3a;color:#8fc0ea;}
  html[data-theme="yardboard"] .epz-compare-table tr.epz-compare-recommended td{background:#1b2b22;}
  html[data-theme="yardboard"] .delete-block-box{background:#33201f;border-color:#5c3330;}
  html[data-theme="yardboard"] .job-cat-photo-req{background:#33201f;color:#eda594;border-color:#5c3330;}

  /* 4. Status colour, re-cut for a dark field. Same meanings -- red is
        still late, green is still settled -- just lightened enough to
        clear the ground instead of sinking into it. */
  html[data-theme="yardboard"] .pill-yellow,
  html[data-theme="yardboard"] .stat-card.stat-yellow .stat-num{color:#e6b23c;}
  html[data-theme="yardboard"] .pill-red,
  html[data-theme="yardboard"] .stat-card.stat-red .stat-num,
  html[data-theme="yardboard"] .link-action-danger{color:#ef8a83;}
  html[data-theme="yardboard"] .link-action-danger:hover{color:#f7b3ad;}
  html[data-theme="yardboard"] .pill-green,
  html[data-theme="yardboard"] .stat-card.stat-green .stat-num{color:#6ec48b;}
  html[data-theme="yardboard"] .stat-card.active.stat-yellow{box-shadow:0 0 0 2px #e6b23c inset;}
  html[data-theme="yardboard"] .stat-card.active.stat-red{box-shadow:0 0 0 2px #ef8a83 inset;}
  html[data-theme="yardboard"] .stat-card.active.stat-green{box-shadow:0 0 0 2px #6ec48b inset;}
  html[data-theme="yardboard"] .zone-non_epz{background:#22303f;color:#a8c8e8;}
  html[data-theme="yardboard"] .zone-epz{background:#33231f;color:#eda594;}
  html[data-theme="yardboard"] .vendor-location-tag,
  html[data-theme="yardboard"] .vendor-back-btn:hover{color:#c0a5e8;}

  /* 5. Messages, badges and the danger button. */
  html[data-theme="yardboard"] .flash{background:#2c2317;color:#eac68e;}
  html[data-theme="yardboard"] .flash.error{background:#33201f;border-color:#5c3330;color:#f2ada5;}
  html[data-theme="yardboard"] .btn-danger{color:#ef8a83;border-color:#5c3330;}
  html[data-theme="yardboard"] .btn-danger:hover{background:#33201f;}
  html[data-theme="yardboard"] .default-badge{background:#2c2317;color:#e0b070;}
  html[data-theme="yardboard"] .tow-route-gap{background:#33201f;color:#f0a99f;border-color:#5c3330;}
  html[data-theme="yardboard"] header .mark,
  html[data-theme="yardboard"] .auth-card .mark{background:#e8eef2;}

  *{box-sizing:border-box;}
  html{font-size:14px;}
  /* The hidden attribute only sets display:none at the very bottom of the
     cascade, so any class that sets a display -- .btn does -- silently beats
     it. Anything toggled with el.hidden then stays on screen. Making it
     !important once here is far safer than remembering it at every call
     site, and there is no legitimate reason to show a hidden element. */
  [hidden]{display:none !important;}
  body{
    font-family:var(--font-body);
    margin:0;background:var(--mist);color:var(--ink);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;
  }
  h1,h2,h3{font-family:var(--font-display);}
  /* form controls don't inherit font by default in most browsers -- without this
     they render in the browser's UI font, which is what made buttons/inputs look
     oversized and out of step with the rest of the page */
  button, input, select, textarea{font-family:inherit;}

  header{
    background:linear-gradient(120deg,var(--navy) 0%,var(--navy-2) 100%);
    color:#fff;padding:16px 28px;display:flex;justify-content:space-between;align-items:center;
    box-shadow:0 2px 12px rgba(14,28,48,.18);
  }
  header .brand{display:flex;align-items:center;gap:12px;}
  header .mark{
    height:32px;width:auto;display:block;background:#fff;border-radius:8px;padding:3px 6px;object-fit:contain;
  }
  header .mark.mark-fallback{
    width:30px;height:30px;padding:0;display:flex;align-items:center;justify-content:center;
    background:linear-gradient(135deg,var(--amber),var(--amber-dark));font-weight:800;font-size:12px;color:#fff;
  }
  header h1{font-size:15px;margin:0;font-weight:700;letter-spacing:.2px;}
  header .sub{font-size:11px;color:#9fb0c7;font-weight:500;}
  header .dbpath{font-size:11px;color:#8ea0b8;font-family:Consolas,monospace;}

  .topnav{background:var(--navy-2);padding:0 24px;display:flex;gap:2px;box-shadow:0 2px 8px rgba(14,28,48,.12);}
  .topnav a{
    color:#aebdd2;text-decoration:none;font-size:12.5px;font-weight:700;letter-spacing:.2px;
    padding:12px 14px;display:inline-block;border-bottom:2px solid transparent;transition:color .15s ease,border-color .15s ease;
  }
  .topnav a:hover{color:#fff;}
  .topnav a.active{color:#fff;border-bottom-color:var(--amber);}
  .nav-badge{background:var(--amber);color:#3a2a00;font-size:10px;font-weight:800;padding:1px 6px;border-radius:20px;margin-left:5px;}

  /* ------------------------------------------------------------------
     On a phone.

     Everything above was drawn for a desk. At 390px -- the width of the
     iPhone most of the yard carries -- the header ate 90px of a screen
     that has 844, and the database path in it could not wrap, so it made
     every page wider than the screen and they all scrolled sideways.

     The path goes: nobody standing next to a car needs a server path.
     The header shrinks. And the nav becomes one button -- see below.
     ------------------------------------------------------------------ */
  .nav-burger{display:none;}

  @media (max-width:640px){
    header{padding:10px 14px;gap:10px;}
    header .mark{height:26px;}
    header h1{font-size:14px;}
    header .sub{font-size:10px;}
    header .dbpath{display:none;}

    /* ----------------------------------------------------------------
       The nav on a phone: one button that opens the whole thing.

       This bar holds six groups and a person's name. There is no width at
       which that fits across 390px, and the two obvious ways out are both
       wrong. Letting it wrap costs three rows of a screen that has twelve.
       Scrolling it sideways HIDES THINGS -- which is what shipped, and
       what broke navigation: overflow-x:auto silently computes overflow-y
       to auto as well, so the bar became a clipping box and every dropdown
       that opens beneath it (theme, log out, every menu) was cut off at
       the bar's own bottom edge. Nothing looked broken. The menus simply
       did not appear, and there was no way off whatever page you were on.

       So: closed, the bar is one Menu button. Open, it is a column of
       everything, each group expanding in place when tapped. Nothing is
       positioned outside the flow, nothing can be clipped, and the page
       scrolls if the list runs long.
       ---------------------------------------------------------------- */
    .topnav{flex-wrap:wrap;padding:0 6px;}
    .topnav > *:not(.nav-burger){display:none;}
    .nav-burger{
      display:flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;
      color:#dbe4f0;font-size:13.5px;font-weight:700;letter-spacing:.2px;padding:13px 10px;
      font-family:inherit;
    }
    .nav-burger:hover{color:#fff;}
    .nav-burger-lines{display:inline-block;width:16px;height:11px;position:relative;}
    .nav-burger-lines::before,.nav-burger-lines::after,.nav-burger-lines span{
      content:"";position:absolute;left:0;right:0;height:2px;background:currentColor;border-radius:2px;
    }
    .nav-burger-lines::before{top:0;}
    .nav-burger-lines span{top:4.5px;}
    .nav-burger-lines::after{bottom:0;}

    .topnav.open{flex-direction:column;align-items:stretch;padding-bottom:10px;}
    /* Stretched to full width by the column, the button would otherwise
       centre its own label and sit oddly above a left-aligned list. */
    .topnav.open .nav-burger{justify-content:flex-start;}
    .topnav.open > *{display:flex;}
    .topnav.open > a{padding:12px 14px;font-size:14px;border-bottom:none;}
    .topnav.open .nav-dropdown{flex-direction:column;align-items:stretch;}
    .topnav.open .nav-dropdown-toggle{
      justify-content:space-between;width:100%;padding:12px 14px;font-size:14px;border-bottom:none;
    }
    /* Static, not absolute -- an absolutely positioned menu is exactly
       what got clipped. Each group opens in place and pushes the rest
       down, the way a phone menu should. */
    .topnav.open .nav-dropdown-menu{
      position:static;display:none;min-width:0;box-shadow:none;border:none;
      background:rgba(255,255,255,.05);border-radius:8px;margin:0 6px 6px 14px;padding:4px 0;
    }
    .topnav.open .nav-dropdown.open .nav-dropdown-menu{display:flex;}
    .topnav.open .nav-dropdown-menu a{padding:11px 14px;font-size:13.5px;}
    .topnav.open .user-menu{
      margin-left:0;padding:6px 0 0;border-top:1px solid rgba(255,255,255,.10);margin-top:6px;
    }
    .topnav.open .nav-user-picker{width:100%;}
    .topnav.open .nav-help{width:100%;padding:10px 0;}
    .topnav.open .nav-theme-option-form,
    .topnav.open .nav-theme-option-form button,
    .topnav.open .nav-logout-option{width:100%;text-align:left;}
    .wrap{padding-left:14px;padding-right:14px;}
  }

  /* ------------------------------------------------------------------
     The bottom tab bar.

     Operations have three destinations, and a menu that holds three
     things is not a menu -- it is three buttons somebody has hidden. So
     on the three screens they live in, the top nav is replaced on phones
     by a fixed bar in the bottom third, where a thumb actually reaches
     while the other hand is holding a plate photo.

     Only on phones, and only on those screens: at a desk the top nav is
     the right control and this bar is hidden. The .has-tabbar class is
     set by the bar's own one-line script, so nothing here fires on a page
     that does not have one.
     ------------------------------------------------------------------ */
  .tabbar{display:none;}
  @media (max-width:640px){
    /* The tab bar replaces the nav's LINKS, not the nav. Hiding the whole
       bar here took the Menu button with it, and with it the only way to
       log out or change the theme from the three screens operations spend
       their day on. Below 640px the bar is already just that button (see
       .nav-burger), so it costs one row and is the way out of anywhere. */
    .has-tabbar .topnav > *:not(.nav-burger){display:none;}
    .has-tabbar .topnav.open > *{display:flex;}
    .has-tabbar .tabbar{
      display:flex;position:fixed;left:0;right:0;bottom:0;z-index:60;
      background:var(--surface);border-top:1px solid var(--line);
      box-shadow:0 -2px 14px rgba(14,28,48,.10);
      /* The home indicator on a modern iPhone sits over the bottom ~20px.
         Without this the third tab is under it and cannot be pressed. */
      padding-bottom:max(8px,env(safe-area-inset-bottom));
    }
    .has-tabbar .wrap{padding-bottom:96px;}
    .tab{
      flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
      padding:9px 4px 5px;text-decoration:none;color:var(--ink-soft);
      font-size:11px;font-weight:700;letter-spacing:.2px;position:relative;
      -webkit-tap-highlight-color:transparent;
    }
    .tab svg{width:23px;height:23px;display:block;stroke:currentColor;fill:none;
      stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;}
    .tab.active{color:var(--ink-strong);}
    /* The active tab gets a bar above it rather than a filled pill: on a
       small screen a filled shape reads as a button you have not pressed
       yet, which is the opposite of what it means. */
    .tab.active::before{
      content:"";position:absolute;top:0;left:22%;right:22%;height:3px;
      background:var(--amber);border-radius:0 0 3px 3px;
    }
    .tab-badge{
      position:absolute;top:4px;left:calc(50% + 6px);
      background:var(--amber);color:#3a2a00;font-size:10px;font-weight:800;
      padding:0 5px;border-radius:20px;line-height:16px;min-width:16px;text-align:center;
    }
  }

  /* Grouped top-nav dropdowns (Operations / Admin) -- click-to-open rather
     than hover-only (see NAV_HTML's toggle script), so this needs no :hover
     rule of its own; .nav-dropdown.open is toggled by JS. */
  .nav-dropdown{position:relative;display:flex;align-items:center;}
  .nav-dropdown-toggle{
    background:none;border:none;font:inherit;cursor:pointer;
    color:#aebdd2;text-decoration:none;font-size:12.5px;font-weight:700;letter-spacing:.2px;
    padding:12px 14px;display:inline-flex;align-items:center;gap:4px;
    border-bottom:2px solid transparent;transition:color .15s ease,border-color .15s ease;
  }
  .nav-dropdown-toggle:hover{color:#fff;}
  .nav-dropdown.active>.nav-dropdown-toggle{color:#fff;border-bottom-color:var(--amber);}
  .nav-caret{font-size:9px;opacity:.75;transition:transform .15s ease;position:relative;top:1px;}
  .nav-dropdown.open .nav-caret{transform:rotate(180deg);}
  /* An open menu floats over whatever is on the page behind it, and both
     are dark-on-dark once a dark theme is on -- so it needed an edge of
     its own. A warm hairline down the sides and along the foot, plus a
     faint glow outside it, says where the menu stops without a heavy
     border. The top is deliberately left open: that edge is against the
     nav bar the menu belongs to, and drawing it there would cut the menu
     off from the item that opened it. */
  .nav-dropdown-menu{
    display:none;flex-direction:column;position:absolute;top:100%;left:0;min-width:210px;
    background:var(--navy-2);border-radius:0 0 10px 10px;
    border:1px solid var(--amber);border-top:0;
    box-shadow:0 12px 28px rgba(14,28,48,.4), 0 0 0 3px rgba(242,153,74,.13);
    padding:6px 0;z-index:80;
  }
  .nav-dropdown.open .nav-dropdown-menu{display:flex;}
  /* Opens flush against the right edge of its toggle instead of the left --
     used by the theme picker (see NAV_HTML), which now lives inside
     .user-menu near the far-right edge of the bar, where a left-anchored
     menu would run off the edge of the window. */
  .nav-dropdown-menu.nav-dropdown-menu-right{left:auto;right:0;}
  /* A heading inside a dropdown. The Reports menu is the first with enough
     in it to need grouping -- reconciling somebody else's list against ours
     and generating one of our own are different jobs, and a flat list of
     both reads as one job done six ways. Not a link and not focusable: it is
     a label, and tabbing onto something that cannot be pressed is a small
     lie about what the menu holds. */
  .nav-dropdown-heading{
    font-size:9.5px;font-weight:800;letter-spacing:.9px;text-transform:uppercase;
    color:rgba(255,255,255,.42);padding:9px 15px 4px;user-select:none;
  }
  .nav-dropdown-heading + a{margin-top:0;}
  .nav-dropdown-menu .nav-dropdown-heading ~ .nav-dropdown-heading{
    margin-top:4px;border-top:1px solid rgba(255,255,255,.10);padding-top:10px;
  }
  /* A section with nothing in it yet. Shown rather than hidden, because the
     shape of the menu is itself information: it says where the next thing
     goes. */
  .nav-dropdown-soon{
    font-size:11.5px;color:rgba(255,255,255,.35);padding:2px 15px 8px;
    font-style:italic;user-select:none;
  }
  /* --- Collect a Car / Photo & Video / Locate, the screens operations live
     in on a phone. Moved here from the page's own <head> so a phone that
     reloads these all day fetches them once. --- */

  .pk-wrap{max-width:520px;margin:0 auto;padding:16px;}
  .pk-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
    box-shadow:var(--shadow-md);padding:18px;margin-bottom:16px;}
  .pk-title{font-size:11px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--ink-soft);margin:0 0 4px;}
  .pk-lead{font-size:13px;color:var(--ink-soft);margin:0 0 16px;line-height:1.5;}
  /* The plate is the one thing on this page that must be right, so it is the
     biggest thing on it and it is monospaced -- a transposed character in a
     plate is invisible in a proportional font. */
  .pk-plate-input{
    font-family:var(--mono, ui-monospace, monospace);font-size:30px;font-weight:800;letter-spacing:3px;
    text-align:center;text-transform:uppercase;padding:14px 10px;
  }
  .pk-shot{width:100%;border-radius:10px;display:block;background:#0e1c30;}
  /* The photograph of the car, and the one control on it. The plate reader
     that used to draw a box on this picture is gone -- the number is typed
     now -- so the wrap is a frame and nothing more. */
  .pk-shot-wrap{position:relative;margin-bottom:10px;}
  /* Sits on the picture rather than under it, so removing a photo is where
     the photo is. Dark plate behind it because it lands on whatever the
     camera happened to catch. */
  .pk-shot-clear{position:absolute;top:8px;right:8px;font:inherit;font-size:12px;font-weight:700;
    color:#fff;background:rgba(14,28,48,.78);border:1px solid rgba(255,255,255,.28);
    border-radius:999px;padding:6px 12px;cursor:pointer;}
  .pk-shot-clear:active{background:rgba(14,28,48,.94);}
  .pk-shot-status{font-size:12.5px;color:var(--ink-soft);min-height:18px;margin:6px 0 0;line-height:1.5;}
  .pk-shot-status.good{color:#1f7a4d;}
  .pk-shot-status.failed{color:#8a2c14;}
  /* --- Picking photographs, with a line under each one ---------------
     Shared by the arrival screen and the office's Create Job page (see
     templates_shared.MEDIA_PICKER_JS), so a picture chosen at a car and a
     picture chosen at a desk look and behave the same. */
  .mediapick .mp-bar{display:flex;gap:8px;flex-wrap:wrap;margin:4px 0 0;}
  .mediapick .mp-btn{flex:1;min-width:150px;font-size:13px;padding:9px 12px;}
  /* The camera, live in the page. Sits above the pictures already taken, so
     the next one appears under it and the shutter stays where the thumb is. */
  .mediapick .mp-cam{margin-top:10px;border:1px solid var(--line);border-radius:10px;
    overflow:hidden;background:#0e1c30;}
  .mediapick .mp-cam video{display:block;width:100%;max-height:340px;object-fit:cover;background:#0e1c30;}
  .mediapick .mp-cam-bar{display:flex;gap:8px;padding:8px;}
  .mediapick .mp-cam-bar .btn{flex:1;margin:0;font-size:13px;padding:10px 12px;}
  .mediapick .mp-note{font-size:12px;color:var(--tint-red-ink);margin:8px 0 0;}
  .mediapick .mp-list{display:flex;flex-direction:column;gap:8px;margin-top:10px;}
  /* Thumbnail, then the caption taking whatever is left, then the way to
     take it off again. One row per picture, so what is written belongs
     unmistakably to the picture beside it rather than to the pile. */
  .mediapick .mp-item{display:flex;align-items:center;gap:9px;}
  .mediapick .mp-thumb{flex:none;width:52px;height:52px;border-radius:8px;overflow:hidden;
    border:1px solid var(--line);background:var(--panel);
    display:flex;align-items:center;justify-content:center;}
  .mediapick .mp-thumb img{width:100%;height:100%;object-fit:cover;display:block;}
  .mediapick .mp-thumb-video{font-size:18px;color:var(--ink-soft);}
  .mediapick .mp-caption{flex:1;min-width:0;font-size:13px;padding:9px 10px;}
  .mediapick .mp-drop{flex:none;font:inherit;font-size:14px;line-height:1;color:var(--ink-soft);
    background:none;border:1px solid var(--line);border-radius:8px;padding:9px 11px;cursor:pointer;}
  .mediapick .mp-drop:hover{color:var(--ink);border-color:var(--ink-soft);}
  .pk-btn-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:10px;}
  .pk-btn-row .btn{flex:1;min-width:130px;text-align:center;}
  .pk-loc{font-size:12px;color:var(--ink-soft);margin-top:6px;}
  .pk-collecting{display:flex;flex-direction:column;gap:10px;}
  .pk-row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
    border:1px solid var(--line);border-radius:10px;padding:12px 14px;background:var(--surface);}
  .pk-row-plate{font-family:var(--mono, ui-monospace, monospace);font-size:16px;font-weight:800;color:var(--ink);}
  .pk-row-meta{font-size:12px;color:var(--ink-soft);}

  /* A heading inside a dropdown: not a link, and never mistaken for one.
     The menus are grouped by the question being asked rather than left as
     one long list, and a group with no label is just a gap. */
  .nav-group{
    padding:9px 14px 4px;font-size:9.5px;font-weight:800;letter-spacing:.6px;
    text-transform:uppercase;color:#8a97a8;pointer-events:none;
  }
  .nav-dropdown-menu .nav-group:first-child{padding-top:6px;}
  /* The rule between "Waiting on somebody" and "On the ground" was a
     white hairline, which read as a different kind of line from the amber
     edge round the menu -- two dividers, two vocabularies, in one small
     panel. Same warm shade, softer than the edge: it separates sections
     inside the menu rather than marking where the menu ends. */
  .nav-dropdown-menu a + .nav-group{
    margin-top:5px;border-top:1px solid rgba(242,153,74,.42);
  }
  .nav-dropdown-menu a{
    color:#aebdd2;text-decoration:none;font-size:12.5px;font-weight:600;letter-spacing:.2px;
    padding:10px 16px;display:flex;align-items:center;justify-content:space-between;
    white-space:nowrap;border-bottom:none;
  }
  .nav-dropdown-menu a:hover{color:#fff;background:rgba(255,255,255,.07);}
  .nav-dropdown-menu a.active{color:#fff;background:rgba(242,153,74,.14);}

  /* Theme picker -- reuses .nav-dropdown/.nav-dropdown-menu (see above),
     just with <form><button> rows instead of <a> links since picking a
     theme is a POST (see set_theme()), not navigation. */
  .nav-theme-option-form{margin:0;}
  .nav-theme-option{
    width:100%;background:none;border:none;cursor:pointer;font:inherit;text-align:left;
    color:#aebdd2;font-size:12.5px;font-weight:600;letter-spacing:.2px;
    padding:10px 16px;display:flex;align-items:center;justify-content:flex-start;
    gap:8px;white-space:nowrap;
  }
  .nav-theme-option:hover{color:#fff;background:rgba(255,255,255,.07);}
  .nav-theme-option.active{color:#fff;background:rgba(242,153,74,.14);}
  .theme-swatch{
    width:12px;height:12px;border-radius:50%;display:inline-block;flex-shrink:0;
    border:1px solid rgba(255,255,255,.3);
  }
  .theme-swatch-navy{background:#0e1c30;}
  .theme-swatch-forest{background:#164634;}
  .theme-swatch-plum{background:#3c2857;}
  .theme-swatch-premium{background:#14233d;}
  .theme-swatch-premium-dark{background:#111726;}
  .theme-swatch-daylight{background:#0f4f8a;}
  .theme-swatch-yardboard{background:#1b2329;}

  .wrap{max-width:none;margin:24px 0 56px;padding:0 28px;animation:fadeIn .35s ease;}
  @keyframes fadeIn{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:translateY(0);}}

  .stat-row{display:flex;gap:10px;margin-bottom:14px;flex-wrap:wrap;}
  .stat-card{
    flex:1;min-width:112px;background:var(--surface);border-radius:var(--radius);padding:8px 12px;
    box-shadow:var(--shadow);border:1px solid var(--line);transition:transform .15s ease,box-shadow .15s ease;
    display:block;text-decoration:none;color:inherit;cursor:pointer;
  }
  .stat-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-md);}
  .stat-card.active{border-color:var(--navy);box-shadow:0 0 0 2px var(--navy) inset;}
  .stat-num{display:block;font-size:17px;font-weight:800;color:var(--ink-strong);line-height:1.2;}
  .stat-label{font-size:9px;color:var(--ink-soft);font-weight:700;text-transform:uppercase;letter-spacing:.4px;}
  /* Same amber-on-white as .pill-yellow was: 4.35:1, just under. */
  .stat-card.stat-yellow .stat-num{color:#806000;}
  .stat-card.stat-red .stat-num{color:#b3261e;}
  .stat-card.active.stat-yellow{box-shadow:0 0 0 2px #9a7300 inset;}
  .stat-card.active.stat-red{box-shadow:0 0 0 2px #b3261e inset;}
  /* Green for the settled half of a pair -- sold, scheduled. The other cards
     on this row are warnings, so a third warning colour would say "another
     problem" about a number that is the opposite of one. */
  .stat-card.stat-green .stat-num{color:#1e7a4f;}
  .stat-card.active.stat-green{box-shadow:0 0 0 2px #1e7a4f inset;}
  .stat-filter-note{margin:-6px 0 14px;font-size:12.5px;color:var(--ink-soft);}
  /* This link had no colour of its own, so it fell back to the browser's
     default link blue -- #0000EE, which is nearly black against a dark
     page. It is an action, so it gets the strong ink and an underline. */
  .stat-filter-note a{margin-left:4px;color:var(--ink-strong);text-decoration:underline;}
  /* A column filter is allowed to hide an overdue car, but not quietly --
     see filter_inventory_rows(). */
  .filter-overdue-warning{color:#8a2c14;background:#fdf1ec;border:1px solid #f0cdc0;
    border-radius:6px;padding:6px 10px;margin:-8px 0 14px;}

  /* A pasted list that came back short says which terms found nothing --
     see terms_not_found(). Amber rather than red: the search worked, it just
     did not find everything, and that is a fact to read rather than a fault
     to fix. */
  .search-miss-warning{color:#7a5210;background:#fdf6e7;border:1px solid #efdcb3;
    border-radius:6px;padding:6px 10px;margin:0 0 10px;}

  .legend{display:flex;gap:18px;align-items:center;font-size:12px;color:var(--ink-soft);margin-bottom:12px;}
  .legend .dot{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:6px;vertical-align:-1px;}
  .legend .dot.yellow{background:#f5c745;}
  .legend .dot.red{background:#e0554f;}

  .toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;gap:12px;flex-wrap:wrap;}
  .toolbar form{display:flex;gap:8px;align-items:center;}
  input[type=text], input[type=search], input[type=number], input[type=date], select{
    height:34px;padding:0 12px;border:1.5px solid var(--line);border-radius:8px;font-size:13px;background:var(--surface);color:var(--ink);
    transition:border-color .15s ease,box-shadow .15s ease;
  }
  input:focus{outline:none;border-color:var(--amber);box-shadow:0 0 0 3px rgba(242,153,74,.15);}
  input[type=search]{width:280px;}
  button, .btn{
    height:34px;padding:0 14px;border-radius:8px;border:1.5px solid var(--line);background:var(--surface);color:var(--ink);
    font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;
    display:inline-flex;align-items:center;justify-content:center;gap:6px;line-height:1;vertical-align:middle;
    transition:transform .12s ease,box-shadow .12s ease,background .15s ease,border-color .15s ease;
  }
  button:hover, .btn:hover{transform:translateY(-1px);box-shadow:var(--shadow);}
  /* A button the page has switched off has to look switched off. Nothing
     styled :disabled before, so a button waiting on a field read as a
     working button that had stopped responding -- the not-allowed cursor
     and the title are the only explanation the user gets, so the state has
     to be visible before they reach for it. */
  button:disabled, .btn:disabled, button[disabled], .btn[disabled]{
    opacity:.45;cursor:not-allowed;
  }
  button:disabled:hover, .btn:disabled:hover, button[disabled]:hover, .btn[disabled]:hover{
    transform:none;box-shadow:none;
  }
  /* White on the accent measured 2.2:1 on the default theme -- and no better
     on any of the others (2.2 to 3.9, all under the 4.5 a button label of
     this size needs). Every one of them clears it comfortably with dark
     text, so the fill keeps its colour and the label changes instead: the
     brand is the amber, not the white. Hover lifts the fill rather than
     darkening it, so the same dark label stays readable on every theme
     (darkening took plum below 4.5). */
  .btn-primary{background:var(--amber);border-color:var(--amber);color:#231404;box-shadow:0 4px 14px rgba(242,153,74,.3);}
  .btn-primary:hover{background:var(--amber);filter:brightness(1.06);}
  /* Premium themes use a muted gold accent instead of bright amber -- the
     glow color below is re-tinted to match, and the dark variant flips the
     button text to a dark navy (matching the mockup) since white-on-gold
     reads worse than white-on-bright-orange did. */
  html[data-theme="premium"] .btn-primary,
  html[data-theme="premium-dark"] .btn-primary{box-shadow:0 4px 14px rgba(176,141,87,.35);}
  html[data-theme="premium-dark"] .btn-primary{color:#0a0f1a;}
  .btn-danger{color:#b3261e;border-color:#f3c9c6;}
  .btn-danger:hover{background:#fdecea;}
  .count{color:var(--ink-soft);font-size:12.5px;font-weight:600;}

  .table-scroll{overflow-x:auto;overflow-y:hidden;border-radius:var(--radius);box-shadow:var(--shadow-md);
     -webkit-overflow-scrolling:touch;
     /* Keeps a horizontal swipe inside the table instead of letting iOS take
        it as a back-navigation gesture or rubber-band the whole page --
        the difference between a table you can flick through on an iPad and
        one that keeps throwing you off the screen. */
     overscroll-behavior-x:contain;}
  /* A visible scrollbar matters more here than tidiness: on a touch screen
     with no cursor, a thin permanent bar is the only thing telling you there
     is more table to the right. */
  .table-scroll::-webkit-scrollbar{height:10px;}
  .table-scroll::-webkit-scrollbar-track{background:var(--mist);}
  .table-scroll::-webkit-scrollbar-thumb{background:#c3ccd8;border-radius:999px;border:2px solid var(--mist);}
  .table-scroll::-webkit-scrollbar-thumb:hover{background:#a9b5c4;}

  /* --- Vehicle Inventory on a touch screen ---------------------------------
     The plate is what every row is identified by, so it stays put while the
     rest of the table scrolls sideways -- without this you scroll right and
     lose track of which car you are looking at, which is most of what makes a
     wide table unusable on a tablet. Scoped to #invTable so no other table on
     the site is affected.
     Backgrounds are deliberately left to the existing row rules
     (tr:hover td, tr.expiry-yellow td and friends) -- those are more specific
     than these, so a sticky cell still takes its row's colour. */
  /* The `left` offsets are measured and applied by setupStickyColumns() rather
     than hard-coded: the group's width changes with login state (no checkbox
     column for a logged-out view), with the viewport (the running-number
     column drops below 900px), and whenever someone drags one of them wider. */
  /* Deliberately NOT prefixed with #invTable. An id would give these rules a
     higher specificity than the row-state rules below (tr:hover td,
     tr.expiry-red td), so a pinned cell would keep its plain background while
     the rest of its row went red -- the row would appear to be two different
     rows. At this specificity the row rules win, which is what we want: the
     pinned cell should look like part of its row, and only fall back to the
     surface colour when the row has no colour of its own. */
  /* --- The header stays put while the rows go by -------------------------
     A hundred rows in and nobody can remember which column is which; the
     answer had been to scroll back up, read the header, and scroll down
     again to the row you were looking at.
     .table-scroll only scrolls sideways (overflow-y is hidden), so the
     vertical scrolling is the page's own and top:0 pins to the viewport --
     nothing above this table is sticky, so there is nothing to sit under.
     The two header rows stack: the filter row's offset is the height of the
     row above it, which is measured in the browser rather than guessed,
     because that height changes with the theme and the font.
     A th that is already sticky sideways stays sticky sideways -- one
     position:sticky holds both, which is why the pinned first columns keep
     working and their corner cells stay put in both directions at once. */
  #invTable thead th{position:sticky;top:0;z-index:7;}
  #invTable thead tr.filter-row th{top:var(--inv-head-h, 34px);z-index:7;}
  /* Above the pinned columns (z-index 5 and 6), so a header cell passes over
     a plate rather than under it. The corner cells -- sticky in both
     directions at once -- go higher still or the body's pinned column would
     draw over them on a sideways scroll. */
  #invTable thead th.sticky-col,
  #invTable thead th:first-child{z-index:9;}
  /* Sticky cells need their own background: the row's is painted behind
     them, and without this the rows scroll visibly through the header. */
  #invTable thead tr:first-child th{background:#eef1f6;}
  #invTable thead tr.filter-row th{background:var(--surface);}
  /* One line under the whole header block, drawn on the bottom row so it
     travels with it. A border on a sticky cell is not painted reliably in
     every engine; a shadow always is. */
  #invTable thead tr.filter-row th{box-shadow:0 1px 0 var(--line);}
  #invTable thead tr.filter-row th.sticky-col.sticky-last{box-shadow:0 1px 0 var(--line), 1px 0 0 var(--line);}

  td.sticky-col{position:sticky;z-index:3;background:var(--surface);}
  th.sticky-col{position:sticky;z-index:5;background:#eef1f6;}
  thead tr.filter-row th.sticky-col{background:var(--surface);}
  /* Only the last of the group carries the edge, so the columns under it
     visibly pass beneath a single boundary rather than three. */
  .sticky-col.sticky-last{box-shadow:1px 0 0 var(--line);}

  /* The running-number column is the least useful thing on a small screen and
     it is 54px of sticky width, so it goes first when space is short. */
  @media (max-width:900px){
    #invTable th[data-col-key="__serial"],
    #invTable td[data-col-key="__serial"]{display:none;}
  }
  /* A finger is not a mouse pointer: 6px of drag handle is unhittable. Widen
     it wherever the pointer is coarse, and stop the drag from being read as a
     scroll of the container underneath. */
  .col-resizer{touch-action:none;}
  @media (pointer:coarse){
    .col-resizer{width:24px;}
    #invTable td{padding-top:14px;padding-bottom:14px;}
    .filter-row input{height:34px;font-size:13px;}
  }

  /* ------------------------------------------------------------------
     Forty-four pixels.

     Every button in this app was 34px tall, which is comfortable with a
     mouse and too small for a thumb: Apple asks for 44pt, Google for
     48dp, and neither is advice for a phone held in one hand in a car
     park. It went unnoticed because nothing looked wrong -- it failed
     only as a mis-tap, and on Collect a Car the row below is a different
     car's Arrived button.

     Scoped to pointer:coarse so a desk keeps its denser layout, and put
     here rather than on each component so every screen gets it at once,
     including the ones nobody has looked at yet.
     ------------------------------------------------------------------ */
  @media (pointer:coarse){
    .btn, button.btn, .job-big-btn, .sp-btn, .tab, .zone-tab, .job-tab, .sp-tab,
    .link-action, .nav-burger, .pk-arrive-ask, .pk-arrive-no, .pk-arrive-yes{
      min-height:44px;
    }
    /* Buttons that hold only an icon or a couple of characters need the
       width too -- a 44px-tall target 20px wide is still a miss. */
    .btn, button.btn, .link-action, .camera-icon-btn, .job-icon-btn{
      min-width:44px;
    }
    .btn, button.btn{
      display:inline-flex;align-items:center;justify-content:center;
    }
    input, select, textarea{min-height:44px;}
    textarea{min-height:88px;}
    /* A checkbox cannot grow its own hit area, so grow the box itself and
       let the padding around it do the rest. */
    input[type=checkbox], input[type=radio]{
      width:22px;height:22px;
    }
    .checkbox-row{min-height:44px;}
    /* Anything that is a link acting as a control in a list gets room
       around it rather than a larger font, which would reflow the row. */
    td .link-action, td a.link-action{
      display:inline-flex;align-items:center;padding:0 10px;
    }
    /* Back links are navigation, and on a form six screens long the back
       link is the way out of it. */
    a.back{display:inline-flex;align-items:center;min-height:44px;}
  }
  /* table-layout:fixed + width:100% means the table always fits the page --
     columns share the available width evenly by default. Dragging a column
     handle (see .col-resizer / setupColumnResize) reassigns that space; if a
     resize pushes the table wider than its container, .table-scroll scrolls. */
  /* Every other table on the site sizes its columns to their content and
     overflows into .table-scroll, rather than squeezing everything to fit the
     viewport. Scrolling sideways is far less annoying than dragging twelve
     columns wider one at a time to read them -- which is what fixed layout
     forced on anyone using a tablet. The Vehicle Inventory table keeps
     table-layout:fixed below, because it has a real per-column width map and
     drag-to-resize handles. */
  table{width:100%;border-collapse:collapse;background:var(--surface);table-layout:auto;}
  td{padding:10px 14px;text-align:left;font-size:13px;border-bottom:1px solid var(--line);vertical-align:middle;
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     /* Lowest-specificity background there is, so every row-state rule below
        (hover, expiry colours, job flash) still wins -- but a pinned cell is
        never transparent, so nothing scrolls visibly underneath it. */
     background:var(--surface);}
  /* A cap rather than no limit: one long remark should wrap, not stretch its
     column to a thousand pixels and push everything else off screen. It caps
     the WIDTH, not the content -- with white-space allowed to wrap, a value
     longer than this runs onto a second line rather than being cut off. */
  .table-scroll table td, .table-scroll table th{max-width:420px;}
  /* The first column identifies the row, so it stays put while the rest
     scrolls. No background here on purpose -- td/th already have one, and
     setting it at this specificity would override the row-state colours. */
  .table-scroll table td:first-child{position:sticky;left:0;z-index:2;}
  .table-scroll table th:first-child{position:sticky;left:0;z-index:6;}
  /* :not(.sticky-col) keeps this off the Vehicle Inventory table, whose own
     pinned group draws a single edge after the plate rather than after the
     checkbox. :not([colspan]) keeps it off "no rows yet" cells, which span the
     whole table and would otherwise draw a stray line down the far right. */
  .table-scroll table tr > :first-child:not(.sticky-col):not([colspan]){box-shadow:1px 0 0 var(--line);}
  /* A filter input's intrinsic width (about twenty characters) would otherwise
     become the minimum width of every column that has one. */
  .table-scroll .filter-row input{min-width:0;}
  /* Vehicle Inventory used to be table-layout:fixed, with max-width:0 on
     every cell -- which is what made a column truncate to an ellipsis
     rather than grow. It squeezed twelve columns into the viewport and hid
     the difference; a plate could read in full while the badge beside it
     ("Waiting to be Towed to BNLEAT") was cut off mid-word, and nothing on
     screen said anything had been dropped.
     Auto layout now: columns size to what is in them, long values wrap
     instead of being clipped, and the table scrolls sideways in
     .table-scroll when the total is wider than the page. Scrolling is a
     nuisance you can see; a truncated cell is one you cannot. The inline
     widths from LIST_COLUMN_WIDTHS and the drag handles still apply -- under
     auto layout they act as a starting size rather than a hard cap. */
  #invTable{table-layout:auto;}
  #invTable td, #invTable th{
    max-width:420px;overflow:visible;text-overflow:clip;white-space:normal;
    /* A long unbroken value (a chassis number, a URL in a remark) wraps
       inside its column instead of forcing the column that wide. */
    overflow-wrap:anywhere;
  }
  /* Things that are one unit and read wrong broken in half. */
  #invTable .pill, #invTable .epz-badge, #invTable .tag, #invTable .mono,
  #invTable .veh-link, #invTable .sort-btn{white-space:nowrap;}
  /* ...but a location read off GPS is not one of those. A yard code is six
     letters and belongs on one line; "Corporation Drive, Taman Jurong, Jurong
     West, West Region, Singapore, 619774, Singapore" is a whole address, and
     held on one monospace line it ran straight across the columns beside it
     (the cells are overflow:visible, so it did not even clip -- it just sat
     on top of the next one). Set as a small label over a wrapping address, in
     the proportional face, inside the width of its own column. */
  #invTable .olt{
    display:flex;flex-direction:column;align-items:flex-start;gap:1px;
    white-space:normal;font-family:inherit;max-width:100%;overflow-wrap:anywhere;
    text-decoration:none;
  }
  #invTable .olt .olt-kind{
    font-size:9.5px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;
    opacity:.7;line-height:1.2;
  }
  #invTable .olt .olt-place{font-size:12px;font-weight:600;line-height:1.35;}
  #invTable a.olt:hover .olt-place{text-decoration:underline;}
  /* With cells free to wrap, a two-line neighbour would otherwise leave the
     single-line cells floating in the middle of the row. */
  #invTable td{vertical-align:top;}
  th{background:#eef1f6;padding:0;text-align:left;border-bottom:1px solid var(--line);vertical-align:middle;position:relative;}
  th.plain{padding:10px 18px 10px 14px;font-weight:700;color:var(--ink-soft);text-transform:uppercase;font-size:10.5px;letter-spacing:.5px;}
  .sort-btn{
    width:100%;height:auto;background:none;border:none;padding:10px 18px 10px 14px;font-weight:700;color:var(--ink-soft);
    text-transform:uppercase;font-size:10.5px;letter-spacing:.5px;cursor:pointer;display:flex;
    justify-content:space-between;align-items:center;gap:6px;transition:color .15s ease,background .15s ease;
    overflow:hidden;white-space:nowrap;
  }
  .sort-btn:hover{color:var(--ink-strong);background:#e4e9f0;transform:none;box-shadow:none;}
  .sort-btn .arrow{font-size:9px;color:var(--amber-dark);min-width:9px;}
  /* The column the table is actually ordered by. It is worth marking clearly:
     the order now applies to the whole tab rather than the rows on screen, so
     which column is driving it is a fact about the data, not a local tweak. */
  .sort-btn.sorted{color:var(--ink-strong);background:#e4e9f0;}
  a.sort-btn{text-decoration:none;}
  .filter-row th{background:var(--surface);padding:6px 10px;}
  .filter-row input{width:100%;height:28px;padding:0 8px;font-size:12px;border:1px solid var(--line);border-radius:6px;}

  /* Drag handle on the right edge of each header cell to resize its column. */
  .col-resizer{
    position:absolute;top:0;right:0;bottom:0;width:6px;cursor:col-resize;z-index:2;
    transform:translateX(50%);
  }
  .col-resizer:hover, .col-resizer.resizing{background:rgba(242,153,74,.55);}

  /* Drag a header cell sideways to move that column. The Columns panel can
     do the same thing from a list, but on a table this wide the natural
     instruction is "put that one over there", so it works on the table too.
     The insertion point is drawn as a bar on the edge the column will land
     on, rather than by animating the whole table, which at 20+ columns
     reads as the page breaking. */
  #invTable thead tr:first-child th[draggable="true"]{cursor:grab;}
  #invTable thead tr:first-child th.th-dragging{opacity:.45;cursor:grabbing;}
  #invTable thead tr:first-child th.th-drop-before::before,
  #invTable thead tr:first-child th.th-drop-after::after{
    content:"";position:absolute;top:0;bottom:0;width:3px;background:var(--accent);z-index:3;
  }
  #invTable thead tr:first-child th.th-drop-before::before{left:0;}
  #invTable thead tr:first-child th.th-drop-after::after{right:0;}
  tr:hover td{background:#fafbfd;}
  tr.expiry-yellow td{background:#fffbe9;}
  tr.expiry-yellow:hover td{background:#fff3c6;}
  tr.expiry-yellow td:first-child{border-left:3px solid #eab838;}
  tr.expiry-red td{background:#fef1f0;}
  tr.expiry-red:hover td{background:#fbdedb;}
  tr.expiry-red td:first-child{border-left:3px solid #e0554f;}

  .pill{font-size:13px;font-weight:700;}
  .pill-neutral{color:var(--ink);font-weight:400;}
  /* 4.35:1 on white -- just under. */
  .pill-yellow{color:#806000;}
  .pill-red{color:#c0392b;}
  .pill-green{color:#2e7d32;}
  .tag{display:inline-block;padding:2px 10px;border-radius:999px;background:var(--mist);color:var(--ink-soft);font-size:11px;font-weight:600;}

  /* Jobs page "linked" cue -- a tow row and the vendor/service job it's
     still blocking (see _jobs_pending_rows()'s link_group) share this same
     amber accent so they read as one pair at a glance even though they're
     not necessarily adjacent rows (sorted by created_at, not by vehicle).
     The 🔗 badge is the click-to-jump affordance for when they're far
     enough apart in a long/filtered list that the shared border alone
     isn't enough -- see jumpToLinkedJob() below. */
  tr.job-row-linked td:first-child{border-left:3px solid var(--amber);}
  .job-link-badge{
    display:inline-block;margin-left:4px;padding:0 4px;border-radius:5px;font-size:11px;
    cursor:pointer;line-height:1.6;background:rgba(242,153,74,.16);
  }
  .job-link-badge:hover{background:rgba(242,153,74,.32);}
  tr.job-row-flash td{background:rgba(242,153,74,.28) !important;transition:background 1.4s ease;}
  .meter-check-wrap{display:inline-flex;align-items:center;gap:6px;cursor:pointer;}
  .meter-check-wrap input[type=checkbox]{width:16px;height:16px;cursor:pointer;}
  .meter-check-text{font-size:12.5px;color:var(--ink-soft);}
  .mono{font-family:Consolas,"Liberation Mono",Menlo,monospace;font-size:12px;color:var(--ink);}
  .unassigned{color:var(--ink-soft);font-style:italic;font-size:12px;}
  /* A car parked somewhere outside any tracked carpark -- "Other" was chosen
     in the Add Vehicle modal instead of a carpark, so this is the freeform
     location text the user typed in, flagged amber since it's a manual
     exception rather than a normal tracked spot. */
  /* --amber-dark is an accent, not a text colour: it measures 2.8:1 on the
     tinted expiry rows this tag most often sits on. Same hue, taken dark
     enough to read on white and on every row tint. */
  .other-location-tag{color:#94480f;font-weight:700;}
  .vendor-location-tag{color:#7a4fb5;}
  /* Sits under the away-at-vendor tag, quiet enough that a table full of
     them does not turn into a wall of buttons, and only rendered on the
     rows that actually carry the flag. */
  .vendor-back-form{display:block;margin-top:2px;}
  .vendor-back-btn{border:0;background:transparent;padding:0;font:inherit;font-size:10.5px;
    font-weight:700;color:var(--ink-soft);cursor:pointer;text-decoration:underline;
    text-underline-offset:2px;}
  .vendor-back-btn:hover{color:#7a4fb5;}
  .tow-indicator{margin-left:6px;cursor:help;font-size:13px;}
  /* A location the tow said it delivered to and nobody could confirm --
     see verify_helpers.py. Loud on purpose: the row still names a yard, and
     the point of this mark is that the name is not evidence. */
  .loc-disputed{margin-left:6px;cursor:help;font-size:13px;color:#b3261e;font-weight:800;}
  tr.row-towing{background:#eef4ff;}
  tr.row-towing td:first-child{box-shadow:inset 3px 0 0 0 #2f6fed;}
  .veh-link{font-weight:800;color:var(--ink-strong);text-decoration:none;}
  .veh-link:hover{color:var(--amber-dark);text-decoration:underline;}

  /* Row actions render as plain text links, not buttons -- lighter, and no
     table-cell height quirks since nothing here forces the <td> itself into
     a flex box (that was the source of the leftover gap under the buttons). */
  .actions{display:inline-flex;align-items:center;gap:10px;}
  .inline-form{display:inline-flex;}
  .link-action{
    background:none;border:none;padding:0;margin:0;height:auto;line-height:inherit;
    font-size:12.5px;font-weight:600;color:var(--ink-soft);cursor:pointer;text-decoration:none;
    transition:color .15s ease;
  }
  .link-action:hover{color:var(--ink-strong);text-decoration:underline;transform:none;box-shadow:none;}
  .link-action-danger{color:#c0392b;}
  /* A destructive action that is unavailable for this row. Shown rather than
     hidden so the action still reads as one that exists, with a title
     explaining what is holding it -- a button that silently vanishes just
     looks like a rendering bug. */
  .link-action-off{color:var(--ink-soft);opacity:.45;cursor:not-allowed;text-decoration:line-through;}
  .link-action-danger:hover{color:#7c1f18;}
  /* Icon-only action buttons (Vehicle Inventory Actions column) -- same
     link-action base but shown as a glyph in a small rounded hit-target
     instead of a text label, so the sticky Actions column stays narrow.
     Label lives in title/aria-label for hover tooltip + screen readers. */
  .icon-action{
    display:inline-flex;align-items:center;justify-content:center;
    width:24px;height:24px;border-radius:5px;font-size:14px;line-height:1;
  }
  .icon-action:hover{background:rgba(31,74,125,.1);text-decoration:none;}
  .link-action-danger.icon-action:hover{background:rgba(192,57,43,.1);}
  .empty{padding:60px 20px;text-align:center;color:var(--ink-soft);font-size:13px;background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow);}
  .empty .big{font-size:30px;margin-bottom:10px;}
  /* Vehicle comments -- an append-only log (see vehicle_add_comment()), shown
     newest first; each entry is a closed record of who said what and when,
     never an editable field, so there's deliberately no edit/delete control
     anywhere in this markup. */
  .comment-list{display:flex;flex-direction:column;gap:10px;}
  .comment-item{background:var(--mist);border-radius:8px;padding:10px 12px;}
  .comment-meta{display:flex;justify-content:space-between;align-items:baseline;gap:10px;font-size:12px;color:var(--ink-soft);margin-bottom:4px;}
  .comment-meta strong{color:var(--ink);}
  .comment-timestamp{white-space:nowrap;}
  .comment-text{font-size:13px;color:var(--ink);white-space:pre-wrap;word-break:break-word;}

  .pagination{display:flex;justify-content:space-between;align-items:center;margin-top:14px;gap:12px;flex-wrap:wrap;}
  .page-size-form{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink-soft);}
  .page-size-form select{height:32px;padding:0 8px;font-size:12.5px;}
  .page-nav{display:flex;align-items:center;gap:12px;}
  .page-indicator{font-size:12.5px;color:var(--ink-soft);font-weight:600;}
  .btn.disabled{opacity:.4;pointer-events:none;}

  /* The stripe was decoration in the accent colour on every card in the
     app, which is most of why the accent stopped meaning anything. */
  .card{background:var(--surface);border-radius:var(--radius);padding:24px 28px;box-shadow:var(--shadow-md);max-width:820px;border-top:3px solid var(--line);}
  /* A card with nothing to do on it. Still there -- somebody looking for the
     STA section should find it and read why it is empty -- but visibly not
     asking for anything: flatter, quieter text, no coloured top edge. An
     active-looking card offering "Raise the STA batch" on a shipment that
     wants no inspection is an invitation to raise one by mistake. */
  .card-idle{box-shadow:none;border:1px solid var(--line);border-top:3px solid var(--line);
             background:var(--panel);}
  .card-idle .section-title{color:var(--ink-soft);}
  .card-idle .meta{opacity:.85;}
  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
  .field{margin-bottom:4px;}
  .field label{display:block;font-size:10.5px;font-weight:700;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.3px;margin-bottom:5px;}
  .field input, .field select, .field textarea{width:100%;}
  .field textarea{resize:vertical;min-height:70px;font-size:13px;padding:9px 12px;}
  .field .checkbox-row{display:flex;align-items:center;gap:8px;height:34px;}
  .field .checkbox-row input{width:auto;height:auto;}
  .field .checkbox-row label{margin:0;text-transform:none;font-size:13px;font-weight:600;color:var(--ink);letter-spacing:0;}
  .grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px;}

  .zone-badge{display:inline-block;padding:3px 10px;border-radius:999px;font-size:11px;font-weight:700;letter-spacing:.2px;}
  .zone-non_epz{background:#e7edf7;color:#2c4a8a;}
  .zone-epz{background:#fbe9e7;color:#a8391f;}
  .default-badge{display:inline-block;padding:3px 10px;border-radius:999px;font-size:11px;font-weight:700;background:#fff3e6;color:#9a5b12;margin-left:6px;}
  .inactive-badge{display:inline-block;padding:3px 10px;border-radius:999px;font-size:11px;font-weight:700;background:var(--mist);color:var(--ink-soft);margin-left:6px;}
  .carpark-section{margin-bottom:32px;}
  .carpark-section h2{font-size:14px;color:var(--ink-strong);margin:0 0 4px;}
  .carpark-section .section-sub{font-size:12px;color:var(--ink-soft);margin-bottom:14px;}
  tr.inactive-row td{opacity:.55;}
  /* The In Transit page's own toolbar: the column picker and the copy
     button, above the day headings rather than inside any one transit --
     both act on every table on the page. */
  .tr-tools{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0 0 14px;}
  /* Coming in / Going out. Two small pills that wrap onto a second line on a
     narrow screen rather than forcing the table wider -- the numbers are what
     matters and they read fine stacked. */
  .cp-flow{white-space:normal;}
  .cp-flow .pill{margin:1px 3px 1px 0;font-size:11px;}
  /* Column widths are set inline per <th> in the template instead of by
     position here, since the Non-EPZ table has two extra columns (expiry
     stats) that the EPZ table doesn't -- nth-child would misalign between them. */

  .geocode-status{font-size:11.5px;margin-top:6px;color:var(--ink-soft);min-height:14px;}
  /* A job's name on the Jobs page. Monospaced because it is a reference
     somebody reads out over the phone, and a PA-RAJU-202608-004 with a
     transposed digit is invisible in a proportional font. */
  .job-ref-link, .job-ref-plain{font-family:var(--mono, ui-monospace, monospace);font-size:12px;font-weight:700;}
  .job-ref-link{color:var(--ink-strong);text-decoration:none;border-bottom:1px dotted currentColor;}
  .job-ref-link:hover{color:var(--amber);}
  .job-ref-plain{color:var(--ink-soft);}
  /* How a car got here in the first place (History page). Facts in a row so
     the four that matter -- where, who, when, and whether it actually
     arrived -- are read in one glance rather than hunted for in a comment. */
  .pickup-panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);
    padding:16px 18px;margin-bottom:18px;box-shadow:var(--shadow-sm, none);}
  .pickup-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;}
  .pickup-fact{display:flex;flex-direction:column;gap:3px;}
  .pickup-label{font-size:10.5px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--ink-soft);}
  .pickup-value{font-size:13.5px;font-weight:600;color:var(--ink);}
  .pickup-open{color:#8a5a2b;}
  .pickup-shot{display:block;margin-top:14px;max-width:320px;}
  .pickup-shot img{width:100%;border-radius:10px;display:block;border:1px solid var(--line);}
  /* Which routes a tow company handles (Settings -> Users). Each row shows
     who else is on that route, because the question people actually have
     here is never "what did I tick" but "is anything now uncovered". */
  .tow-route-grid{display:grid;gap:6px;margin-top:4px;}
  .tow-route-grid .tow-route-option{
    display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;
    padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);
    font-size:12.5px;font-weight:600;color:var(--ink);text-transform:none;letter-spacing:0;
    margin-bottom:0;cursor:pointer;
  }
  .tow-route-grid .tow-route-option:hover{border-color:var(--navy);}
  .tow-route-grid .tow-route-option input{width:auto;height:auto;margin:0;}
  .tow-route-grid .tow-route-owners{
    font-size:11px;font-weight:600;color:var(--ink-soft);text-align:right;text-transform:none;letter-spacing:0;
  }
  .tow-route-gap{
    margin-top:10px;padding:8px 12px;border-radius:8px;font-size:12px;line-height:1.5;
    color:#8a2c14;background:#fdf1ec;border:1px solid #f0cdc0;
  }
  .geocode-status.error{color:#c0392b;}
  .geocode-status.success{color:#1e7a3c;}

  /* Map on top, full width, then the tables under it. It used to sit in a
     sticky right-hand column; a wide map reads better than a tall narrow one
     and the zone tables want the whole width for their nine columns. */
  .carpark-layout{display:block;}
  .carpark-layout .carpark-col-map{margin-bottom:26px;}
  .carpark-layout .carpark-col-table{min-width:0;}

  /* The zone's own line under its table. Heavier rule above it and a tinted
     ground, so it reads as a summary of the rows rather than one more row --
     and no border under the last cell, because there is nothing after it. */
  .carpark-table tfoot .cp-total td{
    border-top:2px solid var(--ink);border-bottom:0;background:var(--surface-alt,#f3f5f8);
    font-weight:700;padding-top:12px;padding-bottom:12px;
  }
  .carpark-table tfoot .cp-total .cp-total-n{
    font-weight:500;color:var(--ink-soft);font-size:11.5px;margin-left:8px;
    letter-spacing:.04em;text-transform:uppercase;
  }
  .carpark-map{height:320px;border-radius:var(--radius);box-shadow:var(--shadow-md);margin-bottom:16px;}
  .carpark-layout .carpark-map{height:420px;}
  @media (max-width:980px){ .carpark-layout .carpark-map{height:320px;} }
  .carpark-map .leaflet-popup-content{font-family:inherit;font-size:12.5px;}
  /* A circle when it holds a bare count, a pill when it holds "12/40" --
     min-width equal to the height keeps the single-number case perfectly
     round, and the padding lets the wider ones grow rather than clip. */
  .carpark-marker{
    min-width:32px;height:32px;padding:0 8px;box-sizing:border-box;border-radius:16px;
    display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;
    color:#fff;font-weight:800;font-size:12.5px;border:2.5px solid #fff;box-shadow:0 2px 8px rgba(14,28,48,.35);
  }
  /* The marker is positioned by its own centre, so a pill that grew wider
     than 32px has to be pulled back by half the difference or it hangs to
     the right of the place it is marking. */
  .carpark-marker-wrap{width:auto !important;height:auto !important;}
  .carpark-marker-wrap > .carpark-marker{transform:translateX(-50%);margin-left:16px;}
  .carpark-marker.non-epz{background:#2c4a8a;}
  .carpark-marker.epz{background:#a8391f;}
  .carpark-marker.tpt{background:#6a3fa0;}
  .worker-marker{
    width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
    color:#fff;font-weight:800;font-size:11px;border:2.5px solid #fff;box-shadow:0 2px 8px rgba(14,28,48,.35);
    background:#1e8a5f;
  }
  .vendor-marker{
    width:28px;height:28px;border-radius:7px;display:flex;align-items:center;justify-content:center;
    color:#fff;font-weight:800;font-size:14px;border:2.5px solid #fff;box-shadow:0 2px 8px rgba(14,28,48,.35);
    background:#d97b2c;
  }
  .tow-marker{
    width:28px;height:28px;border-radius:7px;display:flex;align-items:center;justify-content:center;
    color:#fff;font-weight:800;font-size:14px;border:2.5px solid #fff;box-shadow:0 2px 8px rgba(14,28,48,.35);
    background:#0f8b8d;
  }
  .sta-marker{
    width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
    color:#fff;font-weight:800;font-size:13px;border:2.5px solid #fff;box-shadow:0 2px 8px rgba(14,28,48,.35);
    background:#7a4fb5;
  }
  /* Labels, not alarms. These were the same colour as the primary button,
     so on a screen like Collect a Car the heading "COLLECT A CAR" competed
     with the button that actually collects one. The accent is reserved now
     for things you press and things that need pressing. */
  .section-title{
    font-size:11px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.6px;
    margin:20px 0 10px;padding-top:14px;border-top:1px solid var(--line);
  }
  .section-title:first-child{margin-top:0;padding-top:0;border-top:none;}
  .form-actions{display:flex;justify-content:space-between;align-items:center;margin-top:20px;padding-top:16px;border-top:1px solid var(--line);}
  .meta{font-size:11.5px;color:var(--ink-soft);margin-top:18px;}
  .flash{background:#fff3e6;border:1px solid var(--amber);color:#7a4a12;padding:10px 14px;border-radius:8px;margin-bottom:14px;font-size:13px;}
  .flash.error{background:#fdecea;border-color:#f3c9c6;color:#8a2c24;}
  /* Saving keeps you on the page you saved, so the message has to read as a
     confirmation rather than as another warning-coloured notice. */
  .flash.saved{background:#e9f4ee;border-color:#8fbfa6;color:#14532d;}
  html[data-theme="premium-dark"] .flash.saved,
  html[data-theme="yardboard"] .flash.saved{background:#10241c;border-color:#255440;color:#5cc397;}

  /* Save and the way out, together, where the hand already is. The default
     .form-actions pushes its children to opposite ends, which is right for a
     Cancel/Confirm pair and wrong for two things somebody chooses between. */
  .form-actions.form-actions-end{justify-content:flex-end;gap:10px;flex-wrap:wrap;}
  a.back{color:var(--ink-soft);font-size:12.5px;text-decoration:none;display:inline-block;margin-bottom:4px;}
  a.back:hover{color:var(--ink);}

  .auth-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;}
  .auth-card{width:100%;max-width:360px;background:var(--surface);border-radius:var(--radius);padding:28px 30px;
    box-shadow:var(--shadow-md);border-top:3px solid var(--amber);}
  .auth-card .mark{height:40px;width:auto;display:block;background:#fff;border-radius:9px;padding:4px 8px;
    object-fit:contain;margin-bottom:14px;border:1px solid var(--line);}
  .auth-card .mark.mark-fallback{width:36px;height:36px;padding:0;border:none;display:flex;align-items:center;justify-content:center;
    background:linear-gradient(135deg,var(--amber),var(--amber-dark));font-weight:800;font-size:13px;color:#fff;}
  .auth-card h1{font-size:16px;margin:0 0 4px;color:var(--ink-strong);}
  .auth-card .sub{font-size:12.5px;color:var(--ink-soft);margin-bottom:18px;}
  .auth-card .field{margin-bottom:14px;}
  .auth-card button[type=submit]{width:100%;margin-top:4px;}
  /* The role is the column the list is grouped by, so it reads as a run of
     like-coloured names rather than a column of lozenges. A pill draws a
     box around every single row; colour alone does the same work and lets
     the eye find where one group stops and the next begins. */
  /* The inventory total strip. The parts are the zone figures, which add
     up to the total; the "of which" half is separated by a rule because
     those cars are already inside the parts. */
  .inv-total{
    display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
    margin:0 0 12px;padding:11px 14px;border:1px solid var(--line);
    border-radius:10px;background:var(--surface);box-shadow:var(--shadow);
  }
  .inv-total .it-n{
    font-size:26px;font-weight:800;line-height:1;color:var(--ink-strong);
    font-variant-numeric:tabular-nums;letter-spacing:-.01em;
  }
  .inv-total .it-l{font-size:12.5px;color:var(--ink-soft);}
  .inv-total .it-parts{display:flex;gap:14px;flex-wrap:wrap;margin-left:8px;
    font-size:12px;color:var(--ink-soft);}
  .inv-total .it-parts b{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;}
  .inv-total .it-of{
    margin-left:auto;padding-left:14px;border-left:1px solid var(--line);
    font-size:12px;color:var(--ink-soft);cursor:help;
  }
  .inv-total .it-of b{color:var(--amber-dark);font-weight:700;font-variant-numeric:tabular-nums;}
  html[data-theme="yardboard"] .inv-total .it-of b{color:var(--amber);}
  @media (max-width:700px){
    .inv-total .it-of{margin-left:0;padding-left:0;border-left:0;}
  }
  .role-badge{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.2px;}
  .role-admin{color:#a33b0e;}
  .role-office_admin{color:#1a6b3c;}
  .role-operations{color:#1f4f9e;}
  .role-vendor{color:#6a3aa8;}
  .role-tow_company{color:#17607d;}
  .role-scrap_requester{color:#4a5a68;}
  .role-scrapper{color:#2f6a4f;}
  /* Without a pale pill behind them these need lifting on a dark page --
     premium-dark included, where the old ink would now be unreadable. */
  html[data-theme="yardboard"] .role-admin,
  html[data-theme="premium-dark"] .role-admin{color:#f0a06a;}
  html[data-theme="yardboard"] .role-office_admin,
  html[data-theme="premium-dark"] .role-office_admin{color:#6ec48b;}
  html[data-theme="yardboard"] .role-operations,
  html[data-theme="premium-dark"] .role-operations{color:#8fb8f0;}
  html[data-theme="yardboard"] .role-vendor,
  html[data-theme="premium-dark"] .role-vendor{color:#c0a5e8;}
  html[data-theme="yardboard"] .role-tow_company,
  html[data-theme="premium-dark"] .role-tow_company{color:#7fc4dd;}
  html[data-theme="yardboard"] .role-scrap_requester,
  html[data-theme="premium-dark"] .role-scrap_requester{color:#a6b6c2;}
  html[data-theme="yardboard"] .role-scrapper,
  html[data-theme="premium-dark"] .role-scrapper{color:#7fc49b;}
  .user-menu{color:#aebdd2;font-size:12px;display:flex;align-items:center;gap:10px;margin-left:auto;padding:0 14px;}
  .user-menu a{color:#aebdd2;text-decoration:none;font-weight:700;}
  .user-menu a:hover{color:#fff;}
  /* Your own name is the button. It carries the role under it, because on a
     shared yard tablet "who am I signed in as" is a real question. */
  /* The runbook link. A plain nav item in the same ink and weight as the
     rest of the bar -- the mark carries the meaning, so it needed no
     container of its own. */
  .nav-help{display:inline-flex;align-items:center;gap:5px;
    color:#aebdd2;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.2px;
    transition:color .15s ease;}
  .nav-help:hover{color:#fff;}
  .nav-help:focus-visible{outline:2px solid var(--amber);outline-offset:3px;border-radius:3px;}
  .nav-help-mark{font-size:13px;font-weight:800;line-height:1;position:relative;top:-.5px;}
  .nav-help:hover .nav-help-mark{color:var(--amber);}
  .nav-user-toggle{display:flex;align-items:center;gap:7px;}
  .nav-user-name{font-weight:700;color:#dbe4f0;}
  .nav-user-role{font-size:11px;font-weight:600;color:#8ea2bc;padding:1px 7px;border-radius:999px;
    background:rgba(255,255,255,.08);}
  .nav-dropdown.open .nav-user-name{color:#fff;}
  .nav-menu-heading{font-size:10px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;
    color:#7f92ad;padding:8px 16px 4px;}
  .nav-menu-sep{height:1px;background:rgba(255,255,255,.12);margin:6px 0;}
  /* Lines up with the theme rows above it: the arrow sits where their
     swatches sit, so the menu reads as one column rather than two. */
  .nav-logout-option{color:#ffb3b3;font-weight:700;justify-content:flex-start;text-align:left;}
  .nav-logout-option:hover{color:#fff;background:rgba(220,90,90,.18);}
  .nav-logout-icon{width:12px;display:inline-block;flex-shrink:0;text-align:center;}

  /* Edit vehicle page: form + captured-details on the left, a media
     viewer + scrollable thumbnail strip on the right, so review/edit of a
     car's paperwork, operations-captured data, and photos/documents can all
     happen from one page instead of hopping between the Review Queue and
     this page. Left/right are flex children either side of a draggable
     .split-handle -- editSplitDrag() below adjusts --split-left and
     remembers it in localStorage per browser. */
  .edit-split{display:flex;align-items:flex-start;}
  .edit-left{flex:0 0 var(--split-left, 60%);min-width:0;}
  .edit-left .card{max-width:none;}
  .split-handle{
    flex:0 0 16px;align-self:stretch;cursor:col-resize;display:flex;
    align-items:center;justify-content:center;touch-action:none;
  }
  .split-handle::before{content:'';width:4px;height:44px;border-radius:3px;background:var(--line);transition:background .15s ease;}
  .split-handle:hover::before, .split-handle.dragging::before{background:var(--amber);}
  .edit-right{flex:1 1 0%;min-width:0;position:sticky;top:20px;}
  .media-viewer{
    background:var(--navy);border-radius:var(--radius);box-shadow:var(--shadow-md);overflow:hidden;
    aspect-ratio:4/3;position:relative;
  }
  .media-viewer-inner{width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .media-viewer-inner img{width:100%;height:100%;object-fit:contain;background:#000;cursor:zoom-in;transform-origin:center center;}
  .media-viewer-inner video{width:100%;height:100%;object-fit:contain;background:#000;}
  .media-viewer-inner iframe{width:100%;height:100%;border:none;background:#fff;}
  .viewer-empty{color:#9fb0c7;font-size:13px;text-align:center;padding:20px;}
  .media-nav-btn{
    position:absolute;top:50%;transform:translateY(-50%);width:36px;height:36px;border-radius:50%;
    background:rgba(14,28,48,.55);color:#fff;border:none;font-size:15px;cursor:pointer;
    display:flex;align-items:center;justify-content:center;z-index:2;transition:background .15s ease;padding:0;
  }
  .media-nav-btn:hover{background:rgba(14,28,48,.85);transform:translateY(-50%);box-shadow:none;}
  .media-nav-prev{left:8px;}
  .media-nav-next{right:8px;}
  .media-zoom-bar{
    position:absolute;bottom:8px;right:8px;background:rgba(14,28,48,.65);border-radius:8px;
    padding:4px 6px;display:flex;align-items:center;gap:6px;z-index:2;
  }
  .media-zoom-bar button{
    width:24px;height:24px;border-radius:6px;border:none;background:rgba(255,255,255,.15);color:#fff;
    font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;height:24px;
  }
  .media-zoom-bar button:hover{background:rgba(255,255,255,.3);transform:none;box-shadow:none;}
  .media-zoom-bar span{color:#fff;font-size:11px;min-width:34px;text-align:center;}
  .media-toolbar{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:8px;min-height:34px;}
  .media-caption{font-size:12px;color:var(--ink-soft);}
  .media-tag-select{height:30px;font-size:12px;padding:0 8px;}
  .media-remove-btn{height:30px;padding:0 10px;font-size:12px;color:#b3261e;border-color:#f3c9c6;}
  .media-remove-btn:hover{background:#fdecea;}
  .media-add-row{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;}
  .media-add-row .btn{font-size:12px;height:30px;padding:0 12px;}
  .media-upload-progress{font-size:11.5px;color:var(--ink-soft);margin-top:6px;min-height:14px;}
  .doc-dropzone-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:4px;}
  .doc-dropzone{
    flex:1 1 180px;border:2px dashed var(--line);border-radius:10px;padding:14px 12px;
    cursor:pointer;text-align:center;background:var(--mist);transition:border-color .12s,background .12s;
  }
  .doc-dropzone:hover{border-color:var(--navy);}
  .doc-dropzone.dragover{border-color:var(--amber);background:#fff3e6;}
  .doc-dropzone-label{display:flex;flex-direction:column;gap:3px;pointer-events:none;}
  .doc-dropzone-label strong{font-size:13px;}
  .doc-dropzone-label .meta{margin:0;font-size:11.5px;}
  .media-strip{display:flex;flex-wrap:wrap;gap:8px;padding:10px 2px 4px;margin-top:2px;}
  .media-thumb{
    flex:0 0 auto;width:104px;height:104px;border-radius:9px;overflow:hidden;cursor:pointer;
    border:2px solid transparent;position:relative;background:#e9edf2;box-shadow:var(--shadow);
    transition:border-color .15s ease,transform .12s ease;
  }
  .media-thumb:hover{transform:translateY(-1px);}
  .media-thumb.active{border-color:var(--amber);}
  .media-thumb img, .media-thumb video{width:100%;height:100%;object-fit:cover;display:block;}
  .media-thumb .doc-icon{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:32px;background:#eef1f6;color:var(--ink-soft);}
  .media-thumb .thumb-label{
    position:absolute;bottom:0;left:0;right:0;background:rgba(14,28,48,.75);color:#fff;
    font-size:10px;font-weight:700;text-align:center;padding:3px 3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  }
  .media-thumb.thumb-uploading{display:flex;align-items:center;justify-content:center;}
  .media-thumb .mini-spinner{
    width:18px;height:18px;border-radius:50%;border:2.5px solid var(--line);border-top-color:var(--amber);
    animation:spin .7s linear infinite;
  }
  @keyframes spin{to{transform:rotate(360deg);}}
  .captured-table{width:100%;background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;}
  .captured-table td{white-space:normal;overflow:visible;text-overflow:clip;padding:6px 10px;font-size:12px;}
  .captured-table td:first-child{font-weight:700;color:var(--ink-soft);font-size:11.5px;width:40%;}
  .captured-table-edit td{padding:5px 10px;vertical-align:middle;}
  .captured-table-edit input[type=text], .captured-table-edit input[type=number], .captured-table-edit select{
    width:100%;height:28px;font-size:12px;padding:0 8px;
  }
  .captured-table-edit input[type=checkbox]{width:auto;height:auto;}
  @media (max-width:980px){
    .edit-split{flex-direction:column;}
    .edit-left{flex-basis:auto !important;width:100%;}
    .split-handle{display:none;}
    .edit-right{position:static;width:100%;}
  }

  /* ------------------------------------------------------------------
     Edit vehicle page: the readiness rail.

     The page used to be one scroll of every field the business knows about a
     car, in the order the columns happen to sit in the table, saying nothing
     about the state of the car on it. That silence had a cost: at the time
     this was written there were 1,042 cars in inventory and not one had a
     single Listing Specification field filled, because nothing anywhere said
     that was the job.

     So the page now leads with what the car still needs. Three lights, one per
     marketplace, because "not listable" as a single badge hid the fact that
     our own site is usually a few fields away while BE FORWARD is a dozen. A
     banner that names fields and counts them rather than saying "incomplete" --
     a vague banner is ignored within a week. Then a rail: one entry per stage,
     each carrying its own gap count, and only the chosen stage's fields on
     screen.

     Everything the rail shows is computed from the record at render time (see
     edit_readiness.py). No stage column, nothing to migrate, and the rail can
     never disagree with the data it describes.
     ------------------------------------------------------------------ */
  .ready-head{
    background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
    box-shadow:var(--shadow-sm);overflow:hidden;margin-bottom:14px;
  }
  .ready-top{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 16px;}
  .ready-plate{font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace);
    font-weight:700;font-size:16px;letter-spacing:.05em;}
  .ready-name{font-weight:600;font-size:15px;}
  .ready-sub{font-size:12.5px;color:var(--muted);}
  .ready-spacer{flex:1 1 auto;}

  /* One cell per marketplace. The dot is the state and the words are the
     shortfall, so the row answers "can this go up, and if not how far off"
     without being read in order. */
  .portal-row{display:flex;border-top:1px solid var(--line);flex-wrap:wrap;}
  .portal-cell{
    flex:1 1 180px;min-width:0;display:flex;align-items:center;gap:9px;
    padding:9px 16px;border-right:1px solid var(--line);
  }
  .portal-cell:last-child{border-right:none;}
  .portal-led{width:9px;height:9px;border-radius:50%;flex:0 0 9px;}
  .portal-led.red{background:#c2410c;}
  .portal-led.amber{background:var(--amber,#b45309);}
  .portal-led.green{background:#15803d;}
  .portal-led.off{background:transparent;border:1.5px solid var(--muted);}
  .portal-name{font-size:12.5px;font-weight:600;}
  .portal-state{font-size:11.5px;color:var(--muted);margin-left:auto;
    font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace);}

  /* The banner names what is missing. The chips are buttons that switch to the
     stage holding that field -- a banner you can act on from where you are
     standing gets cleared; one that only complains does not. */
  .ready-banner{display:flex;gap:11px;align-items:flex-start;padding:11px 16px;
    border-top:1px solid var(--line);font-size:13px;}
  .ready-banner.warn{background:#fdf3e2;color:#6b4200;}
  .ready-banner.locked{background:#fbeae6;color:#7a2a16;}
  .ready-banner.ok{background:#e9f4ee;color:#14532d;}
  .ready-banner b{font-weight:700;}
  .ready-banner .rb-ico{font-weight:700;flex:0 0 auto;}
  .gap-chips{display:flex;gap:5px;flex-wrap:wrap;margin-top:7px;}
  /* Also a <button>, so the same three inherited properties have to go. */
  .gap-chip{
    font-size:11px;background:var(--surface);border:1px solid currentColor;color:inherit;
    padding:3px 9px;border-radius:10px;cursor:pointer;opacity:.85;font-family:inherit;
    height:auto;min-height:0;line-height:1.35;font-weight:500;
  }
  .gap-chip:hover{transform:none;box-shadow:none;}
  .gap-chip:hover,.gap-chip:focus-visible{opacity:1;}

  /* Rail beside the existing split rather than inside it: .edit-left is
     already one half of a draggable pair and cannot afford to give up width. */
  .stage-shell{display:flex;align-items:flex-start;gap:16px;}
  .stage-rail{
    flex:0 0 218px;position:sticky;top:20px;background:var(--surface);
    border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-sm);
    padding:6px 0;overflow:hidden;
  }
  .stage-body{flex:1 1 0%;min-width:0;}
  /* These are <button>s so they are reachable from the keyboard, which means
     they inherit the global button rule above -- height:34px, line-height:1,
     and a lift on hover. All three are wrong here: a rail entry is two lines
     of text that must be allowed to wrap (without this the note ran straight
     over the next step), and a list of stages that jumps under the cursor
     reads as broken rather than responsive. */
  .stage-step{
    display:flex;gap:10px;align-items:flex-start;width:100%;text-align:left;
    padding:9px 13px;background:none;border:none;border-left:3px solid transparent;
    cursor:pointer;font:inherit;color:inherit;
    height:auto;min-height:0;line-height:1.4;border-radius:0;white-space:normal;
  }
  .stage-step:hover{background:var(--surface-2, rgba(127,127,127,.06));}
  .stage-step:hover,.stage-step:focus-visible{transform:none;box-shadow:none;}
  .stage-step[aria-selected="true"]{
    background:var(--surface-2, rgba(127,127,127,.09));border-left-color:var(--ink);
  }
  .stage-step:focus-visible{outline:2px solid var(--amber);outline-offset:-2px;}
  .stage-mk{
    width:18px;height:18px;border-radius:50%;flex:0 0 18px;margin-top:2px;border:1.5px solid;
    display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;
    font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace);
  }
  .stage-mk.done{border-color:#15803d;color:#15803d;background:#e9f4ee;}
  .stage-mk.now{border-color:var(--amber,#b45309);color:var(--amber,#b45309);background:#fdf3e2;}
  .stage-mk.todo{border-color:var(--line);color:var(--muted);}
  /* A stage that is a view rather than a job -- what operations captured,
     the comments. Nothing completes it, so it never wears a tick and never
     glows as the next thing to do. Dashed, so it reads as "not a step"
     rather than as a step somebody has left undone. */
  .stage-mk.info{border-style:dashed;border-color:var(--line);color:var(--ink-soft);background:transparent;}
  .stage-tx{flex:1;min-width:0;}
  .stage-tx b{display:block;font-size:12.5px;font-weight:600;line-height:1.3;}
  .stage-tx span{display:block;font-size:11px;color:var(--muted);line-height:1.35;margin-top:1px;}
  .stage-foot{padding:10px 13px;border-top:1px solid var(--line);margin-top:5px;}

  /* Only the chosen stage's fields are on screen. The form itself is untouched
     -- every pane is still inside it, so one Save still writes everything and
     a hidden pane's inputs still post. */
  .stage-pane[hidden]{display:none !important;}

  /* Where a value came from, and where a missing one is supposed to come from.
     Somebody deciding whether to trust a number wants to know whether a person
     checked it or a spreadsheet import guessed it. */
  .src-tag{
    font-size:9.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);
    font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace);
    margin-left:6px;white-space:nowrap;
  }
  .field.gap > label .src-tag{color:var(--amber,#b45309);}
  .field.gap input:not(:focus){
    border-color:#e8c48a;background:#fdf3e2;
  }
  html[data-theme="premium-dark"] .ready-banner.warn,
  html[data-theme="yardboard"] .ready-banner.warn{background:#2a1f0e;color:#e8a33d;}
  html[data-theme="premium-dark"] .ready-banner.locked,
  html[data-theme="yardboard"] .ready-banner.locked{background:#2a1611;color:#e0836a;}
  html[data-theme="premium-dark"] .ready-banner.ok,
  html[data-theme="yardboard"] .ready-banner.ok{background:#10241c;color:#5cc397;}
  html[data-theme="premium-dark"] .stage-mk.done,
  html[data-theme="yardboard"] .stage-mk.done{background:#10241c;}
  html[data-theme="premium-dark"] .stage-mk.now,
  html[data-theme="yardboard"] .stage-mk.now{background:#2a1f0e;}
  html[data-theme="premium-dark"] .field.gap input:not(:focus),
  html[data-theme="yardboard"] .field.gap input:not(:focus){
    border-color:#5d451c;background:#241b0d;
  }

  @media (max-width:980px){
    .stage-shell{flex-direction:column;}
    .stage-rail{position:static;width:100%;flex:0 0 auto;display:flex;overflow-x:auto;padding:0;}
    .stage-step{width:auto;flex:0 0 auto;border-left:none;border-bottom:3px solid transparent;
      white-space:nowrap;}
    .stage-step[aria-selected="true"]{border-left-color:transparent;border-bottom-color:var(--ink);}
    .stage-tx span{display:none;}
    .stage-foot{display:none;}
    .stage-body{width:100%;}
  }

  /* Drag handle for reordering .media-thumb / .thumb elements (Pointer
     Events, not native HTML5 drag-and-drop -- that doesn't fire reliably on
     touch screens) -- shared by the Edit Vehicle page's filmstrip and the
     Car Intake capture page's Review grid. */
  .media-thumb.dragging, .review-thumb.dragging{
    opacity:.92;transform:scale(1.07);box-shadow:0 10px 22px rgba(14,28,48,.35);z-index:10;
  }
  /* Full-width grab bar across the top of the thumb -- much easier to hit
     (especially on touch) than a small corner icon, and reads clearly as
     "drag me" rather than looking like part of the photo. */
  .drag-handle{
    position:absolute;top:0;left:0;right:0;height:20px;
    background:rgba(14,28,48,.55);color:#fff;display:flex;align-items:center;justify-content:center;
    font-size:12px;letter-spacing:2px;cursor:grab;z-index:3;touch-action:none;
  }
  .drag-handle:active{cursor:grabbing;background:rgba(14,28,48,.8);}

  /* Live in-page camera -- a persistent viewfinder with a shutter/record
     button that stays open across shots, so capturing N photos/videos in a
     row doesn't round-trip through the OS camera app N times. Shared by the
     Car Intake capture page and the Edit Vehicle page's media panel. */
  .camera-overlay{position:fixed;inset:0;background:#000;z-index:70;display:none;flex-direction:column;}
  .camera-overlay video{flex:1;width:100%;height:100%;object-fit:cover;background:#000;}
  .camera-flash{position:absolute;inset:0;background:#fff;opacity:0;pointer-events:none;transition:opacity .15s ease-out;}
  .camera-flash.flashing{opacity:.85;transition:none;}
  .camera-topbar{
    position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;
    padding:16px;z-index:2;
  }
  .camera-count-badge{background:rgba(0,0,0,.5);color:#fff;padding:5px 14px;border-radius:999px;font-size:13px;font-weight:700;}
  .camera-icon-btn{
    width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,.15);border:none;color:#fff;
    font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;
  }
  .camera-topbar-right{display:flex;align-items:center;gap:10px;}

  /* ------------------------------------------------------------------
     Where the shutter lives.

     It used to be nailed to the bottom centre, which is right for a phone
     held upright and wrong for one held sideways -- in landscape the
     bottom centre is the far edge of the screen, reachable only by
     letting go of the phone with the hand that is steadying it. Every
     camera app in the world moves the button to the side in landscape,
     and which side depends on which hand you hold a phone in, which is
     not something this app can know.

     So it is draggable, and it remembers -- separately for upright and
     sideways, because the right answer genuinely differs between them.
     Five places it can land, all reachable by a thumb: the three along
     the bottom and the middle of each side. Snapping to those rather
     than leaving it wherever the finger stopped keeps it out of the
     viewfinder's way and keeps the hit area predictable.
     ------------------------------------------------------------------ */
  .camera-shutter-dock{
    position:absolute;z-index:3;display:flex;align-items:center;justify-content:center;
    touch-action:none;
  }
  .camera-overlay[data-dock="bottom"] .camera-shutter-dock{
    left:0;right:0;bottom:0;padding:20px 0 max(38px,env(safe-area-inset-bottom));
  }
  .camera-overlay[data-dock="bottom-left"] .camera-shutter-dock{
    left:max(18px,env(safe-area-inset-left));bottom:0;padding:20px 0 max(38px,env(safe-area-inset-bottom));
  }
  .camera-overlay[data-dock="bottom-right"] .camera-shutter-dock{
    right:max(18px,env(safe-area-inset-right));bottom:0;padding:20px 0 max(38px,env(safe-area-inset-bottom));
  }
  .camera-overlay[data-dock="left"] .camera-shutter-dock{
    left:0;top:0;bottom:0;flex-direction:column;padding:0 max(20px,env(safe-area-inset-left));
  }
  .camera-overlay[data-dock="right"] .camera-shutter-dock{
    right:0;top:0;bottom:0;flex-direction:column;padding:0 max(20px,env(safe-area-inset-right));
  }
  /* While dragging, the dock follows the finger instead of an anchor.
     Selector deliberately carries [data-dock] so it outweighs the anchor
     rules above -- without it they win on specificity and the button
     stays pinned to its corner while the finger moves away from it. */
  .camera-overlay[data-dock] .camera-shutter-dock.dragging{
    left:0;right:auto;top:0;bottom:auto;padding:0;transition:none;
    transform:translate(var(--dx,0px), var(--dy,0px)) translate(-50%,-50%);
  }
  .shutter-btn{
    width:70px;height:70px;border-radius:50%;background:#fff;border:4px solid rgba(255,255,255,.4);
    cursor:pointer;padding:0;touch-action:none;
  }
  .shutter-btn:active{background:#d9d9d9;}
  .record-btn{
    width:70px;height:70px;border-radius:50%;background:#e0342c;border:4px solid rgba(255,255,255,.4);
    cursor:pointer;padding:0;touch-action:none;
    transition:border-radius .15s ease,width .15s ease,height .15s ease;
  }
  .record-btn.recording{width:32px;height:32px;border-radius:10px;}

  .camera-dock-targets{position:absolute;inset:0;z-index:2;pointer-events:none;display:none;}
  .camera-dock-targets.showing{display:block;}
  .camera-dock-targets span{
    position:absolute;width:62px;height:62px;margin:-31px 0 0 -31px;border-radius:50%;
    border:2px dashed rgba(255,255,255,.45);
  }
  .camera-dock-targets span.near{border-color:#fff;border-style:solid;background:rgba(255,255,255,.16);}
  .camera-dock-targets span[data-dock="bottom"]{left:50%;top:calc(100% - 72px);}
  .camera-dock-targets span[data-dock="bottom-left"]{left:56px;top:calc(100% - 72px);}
  .camera-dock-targets span[data-dock="bottom-right"]{left:calc(100% - 56px);top:calc(100% - 72px);}
  .camera-dock-targets span[data-dock="left"]{left:56px;top:50%;}
  .camera-dock-targets span[data-dock="right"]{left:calc(100% - 56px);top:50%;}

  .camera-move-hint{
    position:absolute;left:50%;transform:translateX(-50%);top:64px;z-index:3;margin:0;
    background:rgba(0,0,0,.55);color:#fff;font-size:12.5px;font-weight:600;
    padding:7px 14px;border-radius:999px;opacity:0;transition:opacity .3s ease;pointer-events:none;
  }
  .camera-move-hint.showing{opacity:1;}

  .camera-done-btn{
    color:#fff;background:rgba(255,255,255,.15);border:none;
    border-radius:10px;padding:9px 18px;font-size:13px;font-weight:700;cursor:pointer;
  }
  .camera-error{
    position:absolute;inset:0;display:none;flex-direction:column;align-items:center;justify-content:center;
    color:#fff;text-align:center;padding:0 28px;font-size:13.5px;line-height:1.6;
  }
  /* Job-completed toast notifications -- see NAV_HTML's polling script and
     jobs_recent_completions(). Shown on any page that has the shared nav,
     regardless of whether that page has anything to do with Jobs. */
  .job-toast-container{position:fixed;bottom:20px;right:20px;z-index:1000;display:flex;flex-direction:column;gap:8px;align-items:flex-end;}
  .job-toast{
    background:var(--navy);color:#fff;padding:11px 16px;border-radius:10px;font-size:13px;font-weight:600;
    box-shadow:var(--shadow-md);opacity:0;transform:translateY(8px);transition:opacity .35s ease,transform .35s ease;
    max-width:320px;
  }
  .job-toast.job-toast-visible{opacity:1;transform:translateY(0);}
  .job-toast-check{color:#7fd996;margin-right:6px;}

/* --- The "tell me when a car is ready" switch (PUSH_OPTIN_HTML) ---------
   Deliberately a quiet row rather than a banner. It is offered once, on the
   screen the notification leads to, and after somebody has turned it on it
   should stop asking for attention -- so the "on" state loses the button
   colour and the accent stripe rather than gaining a tick and a fanfare. */
.push-optin{
  display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--amber);
  border-radius:var(--radius);padding:12px 16px;margin-bottom:16px;
}
.push-optin-text b{display:block;font-size:13.5px;font-weight:700;color:var(--ink);}
.push-optin-text span{font-size:12px;color:var(--ink-soft);}
/* The clock on a collection that went to a vendor first. Days left, not days
   elapsed: a long paint job on a car with three weeks in hand costs nothing,
   and the same job on a car with four days left is a forced tow at short
   notice. Only the second is worth anyone's attention, so only it turns red. */
.pk-clock{font-weight:700;}
.pk-clock-short{color:#b3261e;}
.pk-note{color:var(--ink-soft);font-size:11.5px;}

.push-optin .btn{margin:0;white-space:nowrap;}
.push-optin-on{border-left-color:var(--line);}
.push-optin-on .push-optin-text b{color:var(--ink-soft);font-weight:600;}


/* --- Arriving a car asks first ------------------------------------------
   Four identical orange buttons in a scrolling list, and the one that ends
   the collection is among them. So the tap opens a question instead of
   doing the thing -- and the question names the car and the yard, because
   "are you sure" is what people learn to answer without reading. */
.pk-arrive-confirm{
  background:var(--mist);border-radius:10px;padding:12px 14px;margin-top:4px;
}
.pk-arrive-q{margin:0 0 4px;font-size:13.5px;color:var(--ink);line-height:1.45;}
.pk-arrive-far{
  margin:0 0 10px;font-size:12.5px;font-weight:600;color:#a5401f;line-height:1.45;
}
.pk-arrive-actions{display:flex;gap:8px;margin:8px 0 0;flex-wrap:wrap;align-items:flex-end;}
.pk-arrive-actions .btn{flex:1;margin:0;}
/* Level and lot, asked at the moment the car is parked. Each takes half the
   width above the two buttons, so the question and the answer to it are one
   block rather than a field floating over a pair of actions. */
.pk-arrive-level{flex:1 1 calc(50% - 4px);display:flex;flex-direction:column;gap:3px;
  font-size:11px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;
  color:var(--ink-soft);}
.pk-arrive-level select,.pk-arrive-level input{width:100%;font-size:15px;padding:8px 9px;
  text-transform:none;letter-spacing:0;font-weight:400;}
.pk-arrive-no{background:var(--surface);}

/* --- Recording a vendor's job for them (job_as_vendor) ------------------
   A separate panel below the vendor's own card, never buttons inside it.
   The card is what the vendor sees and does; this is the office writing it
   down on their behalf, and keeping the two visibly apart is what stops
   somebody reading the history later and thinking the vendor pressed it. */
.ojv-onbehalf{
  background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--navy);
  border-radius:var(--radius);box-shadow:var(--shadow);padding:18px 20px;margin-top:18px;max-width:620px;
}
.ojv-onbehalf-title{margin:0 0 3px;font-size:14px;font-weight:700;color:var(--ink);}
.ojv-onbehalf-note{margin:0 0 14px;font-size:12.5px;color:var(--ink-soft);line-height:1.5;}
.ojv-onbehalf .field{margin-bottom:12px;}
.ojv-onbehalf-none{font-size:12.5px;color:var(--ink-soft);background:var(--mist);
  border-radius:8px;padding:11px 13px;}
.ojv-onbehalf-cancel{margin-top:18px;padding-top:16px;border-top:1px dashed var(--line);}

/* --- Car Intake, in sections (intake details) ---------------------------
   The form was one 4,742px scroll with a hundred inputs, no landmarks and
   nothing saved until the end. It is the longest thing anybody in the
   business does on a phone, and it was one held breath from start to
   finish. Same fields, same order -- now with a beginning, a middle and a
   visible end. */
.ib{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);margin-bottom:14px;overflow:hidden;
}
.ib-head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:15px 18px;cursor:pointer;list-style:none;user-select:none;
  font-size:15px;font-weight:700;color:var(--ink);
}
.ib-head::-webkit-details-marker{display:none;}
/* A caret that turns, drawn rather than typed so it looks the same on every
   phone -- the app has enough platform-drawn glyphs already. */
.ib-head::after{
  content:"";width:9px;height:9px;flex:0 0 auto;margin-left:2px;
  border-right:2px solid var(--ink-soft);border-bottom:2px solid var(--ink-soft);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .18s ease;
}
.ib[open] > .ib-head::after{transform:rotate(-135deg) translate(-3px,-3px);}
.ib-head:hover{background:var(--mist);}
.ib-name{flex:1;}
.ib-count{
  font-size:12px;font-weight:600;color:var(--ink-soft);background:var(--mist);
  border-radius:999px;padding:3px 11px;white-space:nowrap;
}
.ib-count.done{background:#e6f5ec;color:#1f7a45;}
.ib-body{padding:0 18px 18px;}

/* Always reachable, and it says how far through you are. */
.ib-save{
  position:sticky;bottom:0;z-index:20;margin:18px -16px 0;
  background:var(--surface);border-top:1px solid var(--line);
  box-shadow:0 -2px 14px rgba(14,28,48,.10);
  padding:12px 16px max(12px,env(safe-area-inset-bottom));
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
}
.ib-save .big-btn{margin:0;flex:1;min-width:180px;}
.ib-save-progress{font-size:12.5px;font-weight:600;color:var(--ink-soft);}

@media (max-width:700px){
  /* Ten short fields -- odometer, doors, the three dimensions, model and
     engine codes -- each had a full-width 50px box for a four-digit number,
     which is most of why this form was six screens long. All of them fit
     two across at 390px with room to spare. */
  .lrows-compact{grid-template-columns:repeat(2,minmax(0,1fr));gap:0 14px;}
  .ib-body{padding:0 14px 14px;}
  .ib-head{padding:14px;}
}

/* --- When a map cannot draw itself ---------------------------------------
   Leaflet is vendored now, so the library is always there; the tiles still
   come from openstreetmap and this app runs on a LAN that may have no way
   out. A grey grid with nothing said about it reads as a broken page, so
   the page says which half failed and which half did not. */
.map-offline{
  background:var(--mist);border:1px solid var(--line);border-radius:var(--radius);
  padding:11px 14px;margin:0 0 12px;font-size:12.5px;color:var(--ink-soft);
}

/* --- Going back to a car's work list (Collect a Car) ---------------------
   The whole row is the target, not a small link at the end of it: this is
   read on a phone in a yard, and a five-character "Edit" is the kind of
   thing that gets tapped three times. */
.pk-row-link{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  text-decoration:none;color:inherit;
}
.pk-row-link:hover{background:var(--mist);}
.pk-row-go{
  font-size:12.5px;font-weight:700;color:var(--ink-strong);white-space:nowrap;flex:0 0 auto;
}
.pk-job-locked{opacity:.72;}

/* --- Search results on Find a car to shoot -------------------------------
   A car found by searching is one somebody is standing next to. Outstanding
   work is worth saying, so it is said in amber; it is not worth refusing
   over, so the button beside it stays live. */
.pk-hit{
  display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  padding:10px 0;border-top:1px solid var(--line);
}
.pk-hit:first-of-type{border-top:0;}
.pk-hit-plate{font-family:var(--mono, ui-monospace, monospace);font-size:16px;font-weight:800;color:var(--ink);}
.pk-hit-meta{font-size:12px;color:var(--ink-soft);line-height:1.5;}
.pk-hit-pending-note{color:var(--amber-dark, #a86b1f);font-weight:600;}

/* Batch-page column picker: the group each column belongs to, and the box
   that narrows a list of forty-odd down to the one being looked for. */
.cols-group{margin-left:6px;font-size:10px;opacity:.75;}
.cols-find{width:100%;box-sizing:border-box;margin:0 0 8px;padding:7px 9px;font-size:13px;
  color:var(--ink);background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);}

/* Retyping the label of a car that is not in one of the yards.
   A real dialog, not fields spliced into the location line. The first
   version put the form inside .current-location -- which is a flex row --
   so every input became a flex item on that line and stretched to the
   height of the tallest thing on it. A form with its own shape needs its
   own box, and a box that dims the page behind it is also the honest
   signal that this is a change being made rather than a line being read. */
.ol-dialog{
  border:1px solid var(--line);border-radius:var(--radius);padding:0;
  background:var(--surface);color:var(--ink);
  width:min(560px, calc(100vw - 32px));box-shadow:var(--shadow-lg, 0 18px 50px rgba(0,0,0,.28));}
.ol-dialog::backdrop{background:rgba(8,12,20,.55);}
.ol-dialog-form{display:flex;flex-direction:column;gap:12px;padding:18px 20px 16px;}
.ol-dialog h3{margin:0;font-size:15px;font-weight:800;color:var(--ink-strong);}
.ol-dialog .meta{margin:0;}
/* Postal code and its button on one line; level and unit beside them where
   there is room and beneath where there is not, which is the order they are
   filled in anyway. */
.ol-pin-row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px;}
.ol-pin-field{display:flex;flex-direction:column;gap:4px;min-width:0;
  font-size:10.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:var(--ink-soft);}
.ol-pin-field input{width:118px;padding:8px 10px;font-size:14px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--ink);background:var(--panel, var(--surface));
  border:1px solid var(--line);border-radius:var(--radius);}
.ol-pin-field.ol-pin-small input{width:78px;}
.ol-pin-go{white-space:nowrap;}
.ol-pin-say:empty{display:none;}
.ol-pin-say{color:var(--ink-soft);font-size:11.5px;}
.ol-full{display:flex;flex-direction:column;gap:4px;
  font-size:10.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:var(--ink-soft);}
.ol-full input{width:100%;box-sizing:border-box;padding:9px 11px;font-size:14px;font-weight:500;
  letter-spacing:0;text-transform:none;color:var(--ink);background:var(--panel, var(--surface));
  border:1px solid var(--line);border-radius:var(--radius);}
.ol-dialog-actions{display:flex;gap:8px;justify-content:flex-end;
  border-top:1px solid var(--line);padding-top:12px;margin-top:2px;}
.ol-edit-open{margin-left:6px;}

/* What is still unanswered on a job that has been ticked. Sits inside the
   category's own detail block, under the questions it is about. */
.job-req-note{margin:8px 0 0;font-size:11.5px;font-weight:700;color:var(--tint-red-ink, #b3261e);}
.job-req-note:empty{display:none;}
/* Pressing another job while one is unfinished sends you back here. The
   second attempt has to look like something happened, so the line flashes
   again even when its words have not changed. */
@keyframes jobReqShout{
  0%{background:var(--tint-red, #fdeceb);}
  100%{background:transparent;}
}
.job-req-shout{animation:jobReqShout 1.1s ease-out 1;border-radius:var(--radius);
  padding:2px 6px;margin-left:-6px;}
@media (prefers-reduced-motion:reduce){ .job-req-shout{animation:none;} }
/* Not disabled: a disabled checkbox swallows the click, so pressing it
   teaches nothing. This one still takes the press, refuses it, and sends
   the person back to what they left open. */
.job-req-waiting{opacity:.5;}
label.job-req-waiting{cursor:not-allowed;}
label.job-req-waiting input{cursor:not-allowed;}

/* The comment box on a vehicle record. Full width of whatever card it is
   in -- box-sizing named, because the padding would otherwise push it past
   the edge -- and it grows with what is typed rather than scrolling inside
   three lines. A comment here cannot be edited once saved, so being able to
   read the whole of it before pressing the button is the point. */
.comment-box{width:100%;box-sizing:border-box;min-height:64px;resize:vertical;
  font:inherit;line-height:1.45;padding:9px 11px;
  color:var(--ink);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.comment-box:focus{outline:none;border-color:var(--amber);}

/* Car info, with the document that fills it open beside the fields.
   Closed, .ci-fields is the whole width and nothing has moved; open, the
   two share the row and the document stays put while the fields scroll,
   because the point is reading one against the other. */
.ci-split{display:flex;gap:18px;align-items:flex-start;}
.ci-fields{flex:1 1 100%;min-width:0;}
.ci-split-open .ci-fields{flex:1 1 55%;}
.ci-doc{flex:1 1 45%;min-width:0;position:sticky;top:16px;
  display:flex;flex-direction:column;gap:8px;}
.ci-doc-bar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;}
.ci-doc-pick{padding:5px 11px;font-size:12px;}
.ci-doc-pick.on{background:var(--navy);color:#fff;border-color:var(--navy);}
.ci-doc-out{margin-left:auto;font-size:11.5px;}
.ci-doc-frame{width:100%;height:min(72vh, 780px);border:1px solid var(--line);
  border-radius:var(--radius);background:var(--panel, var(--surface));}
.ci-doc-open{display:flex;align-items:center;gap:10px;margin:0 0 12px;}
/* Only while a document is open beside the fields. A column of form fields
   reads badly much wider than this, which is why the cap exists. */
.card.card-wide{max-width:none;}
/* On a narrow screen they stack, and the document goes first: a person who
   opened it wants to see it, and scrolling past every field to reach it is
   the opposite of a side-by-side check. */
@media (max-width: 900px){
  .ci-split{flex-direction:column;}
  .ci-split-open .ci-fields, .ci-doc{flex:1 1 auto;width:100%;}
  .ci-doc{position:static;order:-1;}
  .ci-doc-frame{height:60vh;}
}

/* What is already on file, at the top of Document upload. */
.doc-list{list-style:none;margin:0 0 14px;padding:0;
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;}
.doc-list-item{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;
  padding:8px 12px;border-bottom:1px solid var(--line);background:var(--panel, var(--surface));}
.doc-list-item:last-child{border-bottom:0;}
.doc-list-name{font-weight:700;color:var(--ink-strong);text-decoration:none;}
.doc-list-name:hover{color:var(--amber);text-decoration:underline;}
.doc-list-item .meta{margin-left:auto;}
/* A drop zone whose document is already there says so, rather than looking
   like an empty one over a car that has its certificate. */
.doc-have{font-size:11.5px;font-weight:700;color:var(--tint-green-ink, #15803d);}
