/* M&M Marketing Pipeline — interface
   Categorical palette validated for colour-blind separation, light and dark. */
:root{
  color-scheme: light;
  /* Direction B — Showroom. Warm paper rather than grey, near-black ink rather
     than a soft charcoal, and brass as the single accent. The whole point of
     the direction is that nothing shouts: the money is the loudest thing on
     any screen, and it is loud through size and a serif rather than colour. */
  --bg:#f7f5f0; --panel:#ffffff; --panel-2:#fcfbf8; --rail:#faf8f3;
  --line:#e4dfd3; --line-2:#eeeae0; --line-3:#f4f1e9;
  --ink:#1c1b18; --ink-2:#5a574d; --ink-3:#928d7c;
  --brass:#b4802a; --brass-soft:#f6eddc;
  --s1:#2a78d6; --s2:#eb6834; --s3:#1baf7a; --s4:#eda100;
  --s5:#e87ba4; --s6:#008300; --s7:#4a3aa7; --s8:#e34948;
  --ord-1:#86b6ef; --ord-2:#5598e7; --ord-3:#2a78d6; --ord-4:#1c5cab; --ord-5:#104281;
  --good:#0ca30c; --warn:#fab219; --bad:#d03b3b;
  /* Readable ink versions of the alert hues. --warn at 4.5:1 on white is
     unreadable as text, so anything that has to be *read* uses these.
     --good was the same trap and had no readable twin: #0ca30c is 3.35:1 on
     white, which is fine as a stripe down the edge of a card and not fine as
     the word inside it. */
  --warn-2:#8a6100; --bad-2:#b32d2d; --good-2:#0a7d0a;
  --warn-bg:#fdf6e6; --warn-line:#eddfb5; --accent-soft:rgba(180,128,42,.18);
  --pos:#1f7a45; --neg:#b3392b; --mute:#cdc7b8; --accent:#b4802a;
  --deemph:#c2bdae;
  --s-neutral:#8a887f;
  --grid:#efece2; --axis:#ded9cc;
  --lift:0 1px 1.5px rgba(15,15,14,.055), 0 2px 5px -1px rgba(15,15,14,.05),
    0 6px 14px -6px rgba(15,15,14,.05);
  --lift-2:0 2px 4px rgba(15,15,14,.07), 0 10px 22px -6px rgba(15,15,14,.10),
    0 28px 60px -20px rgba(15,15,14,.14);
  --inset:inset 0 1px 0 rgba(255,255,255,.7);
  --r:14px; --r-sm:10px; --r-xs:8px;
  --rail-w:248px;

  /* The rail's own ink. Every skin is free to make the rail a different
     material from the page -- Steel and Ledger both put a near-black column
     beside a light app, which is the single most effective thing either of
     them does -- and the moment one does, `--ink` is the page's ink and no
     longer the rail's. It was patched once, for Steel, with four hard-coded
     greys; Ledger arrived afterwards with the same dark rail, no patch, and
     the signed-in name at the bottom of the column was #4b463c on #16150f.
     Two copies of one rule is how one goes stale, so there is one rule: the
     rail reads these, they default to the page's ink, and a skin with its own
     rail sets them once. */
  --rail-ink:var(--ink); --rail-ink-2:var(--ink-2); --rail-ink-3:var(--ink-3);
  --rail-line:var(--line); --rail-line-2:var(--line-2); --rail-line-3:var(--line-3);
  --rail-panel:var(--panel); --rail-panel-2:var(--panel-2);
}
@media (prefers-color-scheme: dark){
  :root:where(:not([data-theme="light"])){
    color-scheme: dark;
    --bg:#0c0b09; --panel:#161513; --panel-2:#1a1917; --rail:#110f0d;
    --line:#2a2724; --line-2:#221f1d; --line-3:#1c1a18;
    --ink:#faf8f4; --ink-2:#b2ada0; --ink-3:#7e796c;
    --brass:#d9a441; --brass-soft:#2a2113;
    --s1:#3987e5; --s2:#d95926; --s3:#199e70; --s4:#c98500;
    --s5:#d55181; --s6:#008300; --s7:#9085e9; --s8:#e66767;
    --ord-1:#cde2fb; --ord-2:#9ec5f4; --ord-3:#5598e7; --ord-4:#256abf; --ord-5:#184f95;
    --pos:#43c977; --neg:#ef6f62; --mute:#3f3b35; --accent:#d9a441;
    --warn-2:#e0ad3f; --bad-2:#ef6f62;
    --warn-bg:#231c0c; --warn-line:#3b3016; --accent-soft:rgba(217,164,65,.22);
    --deemph:#4d4d49;
    --s-neutral:#8d8c83;
    --grid:#232120; --axis:#302d2a;
    --lift:0 1px 1px rgba(0,0,0,.4), 0 3px 8px rgba(0,0,0,.3);
    --lift-2:0 2px 4px rgba(0,0,0,.5), 0 14px 34px rgba(0,0,0,.45);
    --inset:inset 0 1px 0 rgba(255,255,255,.045);
  }
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --bg:#0c0b09; --panel:#161513; --panel-2:#1a1917; --rail:#110f0d;
  --line:#2a2724; --line-2:#221f1d; --line-3:#1c1a18;
  --ink:#faf8f4; --ink-2:#b2ada0; --ink-3:#7e796c;
  --brass:#d9a441; --brass-soft:#2a2113;
  --s1:#3987e5; --s2:#d95926; --s3:#199e70; --s4:#c98500;
  --s5:#d55181; --s6:#008300; --s7:#9085e9; --s8:#e66767;
  --ord-1:#cde2fb; --ord-2:#9ec5f4; --ord-3:#5598e7; --ord-4:#256abf; --ord-5:#184f95;
  --pos:#43c977; --neg:#ef6f62; --mute:#3f3b35; --accent:#d9a441;
  --warn-2:#e0ad3f; --bad-2:#ef6f62;
  /* The light-mode green is 3.4:1 against this paper, which is the same fault
     the other way round. Dark needs a lighter one, not the same one. */
  --good-2:#43c977;
  --warn-bg:#231c0c; --warn-line:#3b3016; --accent-soft:rgba(217,164,65,.22);
  --deemph:#4d4d49;
  --s-neutral:#8d8c83;
  --grid:#232120; --axis:#302d2a;
  --lift:0 1px 1px rgba(0,0,0,.4), 0 3px 8px rgba(0,0,0,.3);
  --lift-2:0 2px 4px rgba(0,0,0,.5), 0 14px 34px rgba(0,0,0,.45);
  --inset:inset 0 1px 0 rgba(255,255,255,.045);
}

*{box-sizing:border-box}
/* An author `display` rule outbids the browser's own [hidden] rule, so a class
   like .statstrip{display:flex} silently defeats `element.hidden = true` and the
   thing stays on screen. This makes hidden mean hidden, everywhere. */
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%}
body{margin:0; background:var(--bg); color:var(--ink);
  font-size:calc(14px * var(--scale)); line-height:1.55;
  font-family:var(--face);
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  letter-spacing:-0.006em}
::selection{background:color-mix(in srgb, var(--accent) 22%, transparent)}

/* Explanatory text, everywhere. There was no base rule for this: .cap was
   styled in eleven specific places and nowhere in general, so every other one
   in the app came out at full body size and full page width. That is why the
   app read as if it were talking to you — a caption set at fourteen point
   across thirty inches is not a caption, it is a paragraph. */
.cap{font-size:calc(12.5px * var(--scale, 1)); line-height:1.5;
  color:var(--ink-2); max-width:70ch}

/* =========================================================== shell */
.shell{display:grid; grid-template-columns:var(--rail-w) minmax(0,1fr); min-height:100vh;
  /* the rail colour continues the full page height even though the rail is sticky */
  background:linear-gradient(to right, var(--rail) 0 var(--rail-w), var(--bg) var(--rail-w))}

.rail{background:transparent; box-shadow:1px 0 0 var(--rail-line);
  padding:22px 16px 28px; position:sticky; top:0; height:100vh; overflow-y:auto;
  display:flex; flex-direction:column; gap:26px}
.rail::-webkit-scrollbar{width:6px}
.rail::-webkit-scrollbar-thumb{background:var(--rail-line); border-radius:3px}

/* The identity block. The line under the name is the town on every page
   without exception — it used to be the town on two tabs and the tab's own
   name on the rest, which made the corner of the screen look like it belonged
   to a different app depending where you were standing. */
.mark{display:flex; align-items:center; gap:11px; padding:0 6px 18px;
  border-bottom:1px solid var(--rail-line-2); margin-bottom:-8px}
.mark .glyph{width:34px; height:34px; border-radius:10px; flex:none; display:grid;
  place-items:center; font-size:12.5px; font-weight:750; letter-spacing:-0.04em;
  color:#fff; background:linear-gradient(140deg, var(--s1), var(--s7));
  box-shadow:0 2px 6px color-mix(in srgb, var(--s1) 35%, transparent)}
.mark .txt{min-width:0}
.mark .txt b{display:block; font-size:15px; font-weight:680; letter-spacing:-0.025em;
  line-height:1.15; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.mark .txt span{display:block; font-size:11px; color:var(--rail-ink-3); margin-top:2px;
  letter-spacing:.005em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}

/* A button that looks like a search field. It is not an input on purpose: an
   input here would want its own results, its own keyboard handling and its own
   idea of what a match is, and the app would have two searches that behave the
   same way right up until one of them did not. This one opens the panel. */
.railfind{display:flex; align-items:center; gap:9px; width:100%;
  margin:0 0 13px; padding:8px 10px; border:1px solid var(--rail-line-2);
  border-radius:9px; background:var(--rail-panel-2, transparent); cursor:pointer;
  font:inherit; font-size:12.5px; color:var(--rail-ink-3); text-align:left;
  box-shadow:none; transition:border-color .12s, color .12s}
.railfind:hover{border-color:var(--rail-line); color:var(--rail-ink-2)}
.railfind .fico{font-size:13px; opacity:.75}
.railfind .ftx{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap}
.railfind .fkey{flex:0 0 auto; font-size:10px; font-weight:640; opacity:.6;
  border:1px solid var(--rail-line-2); border-radius:5px; padding:1px 4px}
/* No keyboard to press ⌘K on. */
@media (max-width: 760px){ .railfind .fkey{display:none} }

.railnav{display:flex; flex-direction:column; gap:1px}
.railnav a{position:relative; display:flex; align-items:center; gap:11px;
  padding:9px 11px; border-radius:9px;
  font-size:13.5px; color:var(--rail-ink-2); text-decoration:none; font-weight:500;
  letter-spacing:-.005em;
  transition:background .13s, color .13s}
.railnav a:hover{background:var(--rail-line-3); color:var(--rail-ink)}
.railnav a.on{background:var(--rail-panel); color:var(--rail-ink); font-weight:620; box-shadow:var(--lift)}
/* No accent bar on the live item. It was tried; at left:0 it sat on the pill's
   own rounded corner and read as a rendering fault rather than as an
   indicator, and the pill is already the highest-contrast thing in the
   column. */
.railnav .ico{width:16px; text-align:center; opacity:.7; font-size:13px}
.railnav a.on .ico{opacity:1}
/* Three groups: the daily work, the two that are their own jobs, then Admin.
   Space rather than a rule, because a rule strands itself the moment somebody
   cannot see the item under it. */
.railnav a[href="/auction"], .railnav a.adminlink{margin-top:11px}

.railsec{display:flex; flex-direction:column; gap:7px}
.railsec > h4{font-size:10px; font-weight:650; letter-spacing:.1em; text-transform:uppercase;
  color:var(--rail-ink-3); margin:0; padding:0 4px}
.chanlist{display:flex; flex-direction:column; gap:1px;
  background:var(--rail-panel); border:1px solid var(--rail-line-2); border-radius:11px;
  padding:5px; box-shadow:var(--lift)}
.chanitem{display:grid; grid-template-columns:8px 1fr auto; gap:9px; align-items:center;
  padding:6px 8px; border-radius:7px; font-size:12.5px; transition:background .13s}
.chanitem.nopip{grid-template-columns:1fr auto}
/* An empty card is worse than no card — it reads as something that failed
   to load rather than as a section with nothing in it. */
.chanlist:empty{display:none}
.chanitem:hover{background:var(--rail-line-3)}
.chanitem .pip{width:8px; height:8px; border-radius:50%}
.chanitem .cn{color:var(--rail-ink-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.chanitem .cv{color:var(--rail-ink); font-weight:650; font-variant-numeric:tabular-nums; font-size:12px}
/* One thing in this column claims the leftover height, not two. Both the
   signed-in chip and the footer used to say margin-top:auto, so whichever had
   more above it won — which is why the chip floated at a different height on
   every tab. The chip claims the space; the footer sits under it. */
.rail .foot{margin-top:-14px; padding:13px 6px 0; border-top:1px solid var(--rail-line-2);
  font-size:10.5px; color:var(--rail-ink-3); line-height:1.5; letter-spacing:.005em}

/* =========================================================== main */
.main{min-width:0; display:flex; flex-direction:column}
.topbar{position:sticky; top:0; z-index:40; display:flex; align-items:center; gap:14px;
  padding:0 34px; height:64px; background:color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter:saturate(180%) blur(14px); border-bottom:1px solid var(--line-2)}
/* Nothing in a top bar may wrap. A control that drops onto a second line takes
   the bar's height with it and shoves the whole page down — which is most of
   what "cluttered" looks like on a laptop screen. Instead the parts that can
   give way do: the sub-line truncates, then disappears. */
.topbar > *{flex:0 0 auto}
.topbar h1{font-size:17px; font-weight:650; letter-spacing:-0.024em; margin:0;
  white-space:nowrap}
.topbar .sub{font-size:12.5px; color:var(--ink-3); margin-left:-4px; min-width:0;
  flex:0 1 auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.topbar .sp{flex:1 1 auto; min-width:8px}
.topbar button,.topbar a.btn{white-space:nowrap}
.topbar .seg button{white-space:nowrap}
@media (max-width:1320px){ .topbar .sub{display:none} }

/* Icon-only button — a square, so a row of them keeps an even rhythm. */
button.ico-btn{width:32px; height:32px; padding:0; display:inline-flex;
  align-items:center; justify-content:center; font-size:15px; line-height:1;
  border-radius:var(--r-xs)}

/* --------------------------------------------------------------- popover menu */
.popmenu{position:fixed; z-index:120; min-width:214px; padding:6px;
  background:var(--panel); border:1px solid var(--line); border-radius:12px;
  box-shadow:var(--lift-2); display:flex; flex-direction:column; gap:1px;
  animation:popin .12s cubic-bezier(.2,.7,.3,1)}
@keyframes popin{from{opacity:0; transform:translateY(-4px)} to{opacity:1; transform:none}}
.popmenu button{display:grid; grid-template-columns:18px 1fr; gap:2px 9px;
  align-items:center; text-align:left; background:transparent; border:none;
  box-shadow:none; padding:8px 10px; border-radius:8px; font-size:13px;
  font-weight:500; color:var(--ink); width:100%}
.popmenu button:hover{background:var(--line-3)}
.popmenu .mi{color:var(--ink-3); font-size:12.5px; text-align:center; line-height:1}
.popmenu .ml{min-width:0}
.popmenu .mh{grid-column:2; font-size:11px; color:var(--ink-3); line-height:1.4}
.popmenu .sep{height:1px; background:var(--line-2); margin:5px 4px}
.content{padding:26px 34px 90px; max-width:1420px; width:100%; min-width:0}
/* --------------------------------------------------------------- narrow
   The page used to be 543px wide on a 390px phone, and every screen scrolled
   sideways. The cause was the nav: five destinations in a row have a min-content
   width of 507px, a grid column of `1fr` cannot go under its content, and that
   one number then set the width of the rail, the top bar, the content and every
   card inside it. Anything that is a row of things now scrolls on its own. */
@media (max-width:1080px){
  .shell{grid-template-columns:minmax(0,1fr); background:var(--bg)}
  /* The sidebar becomes a header. It used to lay the name, all seven
     destinations and the signed-in chip out on ONE row: on a 390px phone the
     name and the chip took all of it and the nav was left about thirty pixels
     wide, so Inventory was the only page anybody could reach from the lot.
     The nav gets a row to itself now and scrolls sideways within it. */
  .rail{position:static; height:auto; min-width:0; flex-direction:row;
    align-items:center; overflow:visible; gap:10px 12px; padding:9px 12px 7px;
    background:var(--rail); box-shadow:0 1px 0 var(--line); flex-wrap:wrap}
  .railsec, .rail .foot{display:none}
  .rail .mark{flex:1 1 auto; min-width:0; padding:0; margin:0; border:none}
  .rail .mark .glyph{width:30px; height:30px; border-radius:9px; font-size:11px}
  .rail .mark .txt b{font-size:14px}
  .whoami{order:2; flex:0 0 auto; margin:0; padding:0; max-width:46%}
  .whobtn{padding:6px 8px}
  .whobtn .rl{display:none}
  .railnav{order:3; flex:1 0 100%; flex-direction:row; min-width:0; gap:3px;
    overflow-x:auto; scrollbar-width:none; -ms-overflow-style:none;
    /* room for the scroll to run to the edge rather than stopping short */
    margin:0 -12px; padding:2px 12px}
  .railnav::-webkit-scrollbar{display:none}
  .railnav a{flex:0 0 auto; white-space:nowrap; padding:7px 11px; margin-top:0}
  .railnav a[href="/auction"], .railnav a.adminlink{margin-top:0; margin-left:7px}
  .topbar{padding:10px 14px; height:auto; min-height:58px; flex-wrap:wrap; row-gap:8px}
  .topbar h1{font-size:16px}
  .topbar .sub{display:none}
  .topbar .seg{max-width:100%; overflow-x:auto; scrollbar-width:none}
  .topbar .seg::-webkit-scrollbar{display:none}
  .content{padding:16px 14px 80px}
}

/* =========================================================== controls */
button,select,input,textarea{font-family:inherit; color:var(--ink)}
button,select{font-size:13px; background:var(--panel); border:1px solid var(--line);
  border-radius:var(--r-sm); padding:8px 13px; cursor:pointer; font-weight:500;
  transition:.14s; box-shadow:var(--lift)}
button:hover,select:hover{border-color:var(--axis)}
button:active{transform:translateY(.5px)}
button:disabled{opacity:.4; cursor:default}
button.primary{background:var(--ink); border-color:var(--ink); color:var(--panel); font-weight:600}
button.primary:hover{opacity:.9}
button.ghost{background:transparent; border-color:transparent; box-shadow:none; color:var(--ink-2)}
button.ghost:hover{background:var(--line-3); color:var(--ink)}
button.sm{padding:6px 10px; font-size:12.5px; border-radius:var(--r-xs)}
.btnrow{display:flex; gap:9px; align-items:center; flex-wrap:wrap}

/* an <a> that has to look and behave exactly like a button */
a.btn{display:inline-flex; align-items:center; gap:6px; font-size:13px;
  background:var(--panel); border:1px solid var(--line); border-radius:var(--r-sm);
  padding:8px 13px; cursor:pointer; font-weight:500; transition:.14s;
  box-shadow:var(--lift); color:var(--ink); text-decoration:none; white-space:nowrap}
a.btn:hover{border-color:var(--axis)}
a.btn.sm{padding:6px 10px; font-size:12.5px; border-radius:var(--r-xs)}
a.btn.primary{background:var(--ink); border-color:var(--ink); color:var(--panel); font-weight:600}
a.btn.primary:hover{opacity:.9}
a.btn.ghost{background:transparent; border-color:transparent; box-shadow:none; color:var(--ink-2)}
a.btn.ghost:hover{background:var(--line-3); color:var(--ink)}

.seg{display:inline-flex; background:var(--line-3); padding:3px; border-radius:11px; gap:2px;
  border:1px solid var(--line-2)}
.seg button{background:transparent; border:none; box-shadow:none; padding:5px 12px;
  font-size:12.5px; border-radius:8px; color:var(--ink-3); font-weight:500}
.seg button:hover{color:var(--ink)}
.seg button[aria-pressed="true"]{background:var(--panel); color:var(--ink);
  box-shadow:var(--lift); font-weight:600}

input[type=text],input[type=number],input[type=date],textarea,select.fld{
  width:100%; font-size:13.5px; padding:9px 12px; border:1px solid var(--line);
  border-radius:var(--r-sm); background:var(--panel); box-shadow:var(--lift)}
input:focus,textarea:focus,select:focus{outline:2px solid var(--accent); outline-offset:1px}
label.lb{display:block; font-size:11px; font-weight:600; letter-spacing:.05em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:7px}
.num{text-align:right; font-variant-numeric:tabular-nums}

/* =========================================================== bento */
.bento{display:grid; grid-template-columns:repeat(12,1fr); gap:16px; margin-bottom:16px}
.c3{grid-column:span 3} .c4{grid-column:span 4} .c5{grid-column:span 5}
.c6{grid-column:span 6} .c7{grid-column:span 7} .c8{grid-column:span 8}
.c9{grid-column:span 9} .c12{grid-column:span 12}
@media (max-width:1180px){
  .c3{grid-column:span 6} .c4{grid-column:span 6} .c5{grid-column:span 12}
  .c7{grid-column:span 12} .c8{grid-column:span 12} .c9{grid-column:span 12}
}
@media (max-width:680px){ .c3,.c4,.c6{grid-column:span 12} }

.tile{background:var(--panel); border:1px solid var(--line); border-radius:var(--r);
  box-shadow:var(--lift), var(--inset); padding:18px 20px 20px; position:relative;
  transition:box-shadow .18s, transform .18s; min-width:0; overflow:hidden}
.tile.hover:hover{box-shadow:var(--lift-2), var(--inset); transform:translateY(-1px)}
.tile.pad{padding:22px 24px 24px}
.tile.tight{padding:16px 18px}
.tl{font-size:10.5px; font-weight:600; letter-spacing:.085em; text-transform:uppercase;
  color:var(--ink-3); display:flex; align-items:center; gap:7px}
.tv{font-size:34px; font-weight:640; line-height:1.02; letter-spacing:-0.038em;
  margin-top:12px; font-variant-numeric:lining-nums}
.tv.xl{font-size:52px; letter-spacing:-0.045em}
.tv.sm{font-size:24px; letter-spacing:-0.03em}
.minirows{margin-top:18px; border-top:1px solid var(--line-3)}
.minirows .r{display:flex; justify-content:space-between; gap:10px; padding:8px 0;
  border-bottom:1px solid var(--line-3); font-size:12.5px}
.minirows .r:last-child{border-bottom:none}
.minirows .r .k{color:var(--ink-3)}
.minirows .r .v{font-weight:650; font-variant-numeric:tabular-nums}
.tm{font-size:12px; color:var(--ink-3); margin-top:9px; line-height:1.45}
.tm b{color:var(--ink-2); font-weight:600}
.trend{font-size:11.5px; font-weight:600; display:inline-flex; align-items:center; gap:3px;
  padding:2px 7px; border-radius:20px; letter-spacing:0}
.trend.up{color:var(--pos); background:color-mix(in srgb, var(--pos) 11%, transparent)}
.trend.down{color:var(--neg); background:color-mix(in srgb, var(--neg) 11%, transparent)}
.trend.flat{color:var(--ink-3); background:var(--line-3)}

.tilehead{display:flex; align-items:flex-start; gap:14px; margin-bottom:18px; flex-wrap:wrap}
.tilehead > div:first-child{flex:0 0 auto}
.tilehead h3{font-size:14px; font-weight:640; margin:0; letter-spacing:-0.015em; white-space:nowrap}
.tilehead .sp{flex:1}
.tilehead .cap{font-size:12px; color:var(--ink-3); margin:2px 0 0}

/* =========================================================== rank list */
.rank{display:flex; flex-direction:column; gap:2px}
.rankrow{display:grid; grid-template-columns:20px 1fr 84px; gap:12px; align-items:center;
  padding:10px 10px; border-radius:var(--r-xs); transition:background .13s; cursor:default}
.rankrow:hover{background:var(--line-3)}
.rankrow .pos{font-size:11.5px; color:var(--ink-3); font-variant-numeric:tabular-nums;
  text-align:center}
.rankrow .who{min-width:0}
.rankrow .who .n{display:flex; align-items:center; gap:8px; font-size:13.5px; font-weight:600;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.rankrow .track{height:5px; border-radius:3px; background:var(--line-3); margin-top:7px;
  overflow:hidden}
.rankrow .track i{display:block; height:100%; border-radius:3px; transition:width .35s cubic-bezier(.2,.7,.3,1)}
.rankrow .amt{text-align:right; font-size:13.5px; font-weight:650;
  font-variant-numeric:tabular-nums; letter-spacing:-0.015em}
.rankrow .amt small{display:block; font-size:10.5px; color:var(--ink-3); font-weight:500;
  letter-spacing:.03em; margin-top:2px}

/* =========================================================== chips / badges */
.pill{font-size:11px; font-weight:600; padding:3px 9px; border-radius:20px; letter-spacing:0;
  display:inline-flex; align-items:center; gap:5px; border:1px solid var(--line);
  background:var(--panel-2); color:var(--ink-2); white-space:nowrap}
.pill.ok{color:var(--pos); border-color:color-mix(in srgb,var(--good) 40%,transparent)}
.pill.no{color:var(--neg); border-color:color-mix(in srgb,var(--bad) 40%,transparent)}
.pill .ic{font-weight:700}
.dot{width:9px; height:9px; border-radius:50%; flex:none; display:inline-block}

/* =========================================================== channel cards */
.grid-cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(252px,1fr)); gap:16px}
.ccard{background:var(--panel); border:1px solid var(--line); border-radius:var(--r);
  box-shadow:var(--lift), var(--inset); overflow:hidden;
  transition:box-shadow .18s, transform .18s}
.ccard:hover{box-shadow:var(--lift-2), var(--inset); transform:translateY(-1px)}
.ccard .hd{padding:15px 17px 0}
.ccard .nm{display:flex; align-items:center; gap:8px; font-size:13.5px; font-weight:650;
  letter-spacing:-0.012em}
.ccard .big{font-size:26px; font-weight:640; letter-spacing:-0.032em; margin-top:11px; line-height:1}
.ccard .cap{font-size:11.5px; color:var(--ink-3); margin-top:5px}
.ccard .spark{margin:12px 0 0; padding:0 4px}
.ccard .rows{padding:4px 17px 14px}
.ccard .r{display:flex; justify-content:space-between; gap:10px; padding:7px 0;
  border-top:1px solid var(--line-3); font-size:12.5px}
.ccard .r .k{color:var(--ink-3)}
.ccard .r .v{font-weight:600; font-variant-numeric:tabular-nums}

/* =========================================================== disclosure */
details.dd{border:1px solid var(--line); border-radius:var(--r); background:var(--panel);
  box-shadow:var(--lift), var(--inset); margin-bottom:12px; overflow:hidden}
details.dd > summary{list-style:none; cursor:pointer; display:flex; align-items:center; gap:11px;
  font-size:13.5px; font-weight:600; padding:15px 20px; letter-spacing:-0.012em;
  transition:background .13s}
details.dd > summary::-webkit-details-marker{display:none}
details.dd > summary:hover{background:var(--line-3)}
details.dd > summary::before{content:"›"; font-size:16px; color:var(--ink-3);
  transition:transform .18s; display:inline-block; line-height:1}
details.dd[open] > summary::before{transform:rotate(90deg)}
details.dd > summary .sp{flex:1}
details.dd > summary .hint{font-size:11.5px; color:var(--ink-3); font-weight:500}
details.dd .dbody{padding:4px 20px 22px; border-top:1px solid var(--line-2);
  min-width:0; overflow:hidden}
details.dd .dbody > .cap{font-size:12.5px; color:var(--ink-3); margin:16px 0 18px; max-width:74ch}

/* =========================================================== tables */
.scroll{overflow-x:auto; -webkit-overflow-scrolling:touch; max-width:100%; min-width:0}
table.dt{border-collapse:collapse; width:100%; font-size:13px; min-width:520px}
table.dt th,table.dt td{text-align:right; padding:10px 12px; font-variant-numeric:tabular-nums;
  border-bottom:1px solid var(--line-3); white-space:nowrap}
table.dt th:first-child,table.dt td:first-child{text-align:left; font-variant-numeric:normal}
table.dt th{color:var(--ink-3); font-weight:600; font-size:10.5px; letter-spacing:.06em;
  text-transform:uppercase; border-bottom:1px solid var(--line); padding-bottom:9px}
table.dt tfoot td{font-weight:650; border-bottom:none; border-top:1px solid var(--line)}
table.dt tbody tr{transition:background .1s}
table.dt tbody tr:hover{background:var(--line-3)}
table.dt td.strong{font-weight:650}
.who2{display:flex; align-items:center; gap:9px; font-weight:600}
.lnk{background:none; border:none; box-shadow:none; padding:0; color:var(--ink-2);
  font-size:12.5px; text-decoration:underline; text-underline-offset:3px; cursor:pointer;
  font-weight:500}
.lnk:hover{color:var(--ink)}

/* =========================================================== charts */
svg{display:block; width:100%; overflow:visible}
.gridline{stroke:var(--grid); stroke-width:1}
.axisline{stroke:var(--axis); stroke-width:1}
.targetline{stroke:var(--ink-3); stroke-width:1}
.tick{fill:var(--ink-3); font-size:11px; font-variant-numeric:tabular-nums}
.catlab{fill:var(--ink-2); font-size:11.5px}
.dlabel{fill:var(--ink); font-size:11.5px; font-weight:650}
.hit{fill:transparent; pointer-events:all; cursor:pointer}
.legend{display:flex; gap:14px; flex-wrap:wrap; margin:2px 0 0; padding:0; list-style:none;
  justify-content:flex-end}
.legend li{display:flex; align-items:center; gap:7px; font-size:11.5px; color:var(--ink-2)}
.swatch{width:9px; height:9px; border-radius:3px; flex:none}
#tip{position:fixed; pointer-events:none; opacity:0; transition:opacity .12s;
  background:var(--panel); border:1px solid var(--line); border-radius:var(--r-sm);
  box-shadow:var(--lift-2); padding:12px 14px; font-size:12px; z-index:90; min-width:164px}
#tip .tt-h{color:var(--ink-3); font-size:10.5px; margin-bottom:9px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase}
#tip .tt-r{display:flex; align-items:center; gap:9px; margin-top:5px}
#tip .tt-k{width:11px; height:2px; border-radius:2px; flex:none}
#tip .tt-v{font-weight:650; font-variant-numeric:tabular-nums; margin-left:auto}
#tip .tt-n{color:var(--ink-2)}

/* =========================================================== states */
.empty{color:var(--ink-3); font-size:13.5px; padding:52px 28px; text-align:center;
  border:1px dashed var(--line); border-radius:var(--r); background:var(--panel)}
.callout{display:flex; gap:12px; align-items:flex-start; background:var(--panel);
  border:1px solid var(--line); border-left:2.5px solid var(--warn); border-radius:var(--r);
  padding:14px 18px; font-size:12.5px; color:var(--ink-2); box-shadow:var(--lift)}
.callout .ic{color:var(--warn); font-weight:700; flex:none}
.callout strong{color:var(--ink); font-weight:650}
.msg{font-size:12.5px; font-weight:600; color:var(--pos)}
.msg.err{color:var(--neg)}
.secttl{font-size:11px; font-weight:600; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); margin:32px 0 14px}
.secthead{display:flex; align-items:center; gap:14px; margin:32px 0 6px}
.secthead .secttl{margin:0}
.secthead .sp{flex:1}

/* =========================================================== admin */
.drop{border:1.5px dashed var(--axis); border-radius:var(--r); background:var(--panel);
  padding:42px 24px; text-align:center; cursor:pointer; transition:.18s; box-shadow:var(--inset)}
.drop:hover,.drop.over{border-color:var(--accent);
  background:color-mix(in srgb, var(--accent) 4%, var(--panel))}
.drop .big{font-size:15.5px; font-weight:650; margin-bottom:6px; letter-spacing:-0.018em}
.drop .small{font-size:12.5px; color:var(--ink-3)}
.drop kbd{font-size:11.5px; background:var(--line-3); border:1px solid var(--line);
  border-radius:6px; padding:2px 6px; font-family:inherit}
.bar{height:3px; background:var(--line-3); border-radius:2px; overflow:hidden; margin-top:12px}
.bar i{display:block; height:100%; background:var(--accent); width:0; transition:width .25s}
.ocrstat{font-size:12.5px; color:var(--ink-2); margin-top:13px; min-height:19px}
.ocrstat b{color:var(--ink); font-weight:650}

.revgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px}
.rev{background:var(--panel); border:1px solid var(--line); border-radius:var(--r);
  overflow:hidden; box-shadow:var(--lift), var(--inset)}
.rev .top{display:flex; gap:12px; padding:14px 16px; border-bottom:1px solid var(--line-2);
  align-items:center; background:var(--panel-2)}
.rev .top.unsure{box-shadow:inset 2.5px 0 0 var(--bad)}
.rev .top.unsure select{border-color:var(--bad)}
.rev img{width:46px; height:46px; object-fit:cover; border-radius:9px; flex:none;
  border:1px solid var(--line)}
.rev .rt{min-width:0; flex:1}
.rev .fn{font-size:11px; color:var(--ink-3); overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; margin-top:3px}
.rev .body{padding:14px 16px}
.rev .grouphd{font-size:10px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink-3); margin:0 0 9px; padding-bottom:6px; border-bottom:1px solid var(--line-3)}
.rev .grouphd.spaced{margin-top:18px}
.fldrow{display:grid; grid-template-columns:1fr 92px; gap:10px; align-items:center; margin-bottom:7px}
.fldrow label{font-size:12.5px; color:var(--ink-2)}
.fldrow input{padding:6px 9px; text-align:right; font-variant-numeric:tabular-nums;
  font-size:13px; box-shadow:none}
.fldrow.guessed input{border-color:var(--accent);
  background:color-mix(in srgb, var(--accent) 6%, var(--panel))}
.rev .foot{display:flex; gap:8px; align-items:center; padding:0 16px 15px; flex-wrap:wrap}
details.raw{margin-top:11px}
details.raw summary{font-size:12px; color:var(--ink-3); cursor:pointer}
details.raw pre{background:var(--panel-2); border:1px solid var(--line); border-radius:9px;
  padding:11px; font-size:11px; max-height:150px; overflow:auto; white-space:pre-wrap;
  color:var(--ink-2); margin:9px 0 0}

.chrow{display:grid; grid-template-columns:98px 1fr 122px 66px 34px; gap:12px;
  align-items:center; padding:11px 0; border-bottom:1px solid var(--line-3)}
.chrow:last-of-type{border-bottom:none}
.chrow .hd{font-size:10.5px; color:var(--ink-3); font-weight:600; letter-spacing:.06em;
  text-transform:uppercase}
select.slotsel{font-size:11px; padding:6px 7px; border-radius:8px; font-weight:700;
  border:1px solid transparent; width:100%; -webkit-appearance:none; appearance:none;
  text-align:center; text-shadow:0 1px 1px rgba(0,0,0,.25); box-shadow:none;
  letter-spacing:.02em}
@media (max-width:760px){
  .chrow{grid-template-columns:86px 1fr 96px 34px}
  .chrow .hide-s{display:none}
}
.dealpaste textarea{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12.5px;
  line-height:1.7; resize:vertical}
#dealTable table.dt td{padding:7px 10px}
#dealTable input{padding:6px 9px; font-size:13px; width:100%; min-width:80px}
#dealTable input.num{text-align:right}
pre.out{background:var(--panel-2); border:1px solid var(--line); border-radius:10px;
  padding:13px; font-size:11px; overflow:auto; max-height:190px; margin:13px 0 0;
  white-space:pre-wrap; word-break:break-all; color:var(--ink-2)}

/* ==========================================================================
   INVENTORY MODULE
   Shares the shell, palette, and elevation system with Marketing. Anything
   here that is not inventory-specific belongs further up the file instead.
   ========================================================================== */

/* Six numbers about capital. This used to print four of the six in alarm red,
   which is how a dashboard teaches people to stop reading it — if two thirds of
   the lot is flagged, the flag means nothing. The number is always ink; the only
   thing that changes when something wants attention is a small amber dot beside
   the caption and a warmer border. Quiet, and it still catches the eye. */
.statstrip{display:grid; gap:10px; margin-bottom:20px;
  grid-template-columns:repeat(auto-fit,minmax(154px,1fr))}
.stat{background:var(--panel); border:1px solid var(--line); border-radius:12px;
  padding:13px 15px 14px; box-shadow:var(--lift), var(--inset); min-width:0;
  transition:border-color .13s, transform .12s, box-shadow .16s}
.stat .k{font-size:9.5px; font-weight:660; letter-spacing:.085em;
  text-transform:uppercase; color:var(--ink-3); white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis}
.stat b{display:block; font-size:23px; font-weight:640; letter-spacing:-.03em;
  font-variant-numeric:tabular-nums; line-height:1.1; margin-top:8px}
.stat .m{display:flex; align-items:center; gap:6px; font-size:11.5px;
  color:var(--ink-3); margin-top:6px; min-width:0}
.stat .m em{font-style:normal; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; min-width:0}
.stat .m .d{width:6px; height:6px; border-radius:50%; background:var(--mute); flex:none}
.stat.warn .m{color:var(--ink-2)}
.stat.warn .m .d{background:var(--warn)}
.stat.warn{border-color:color-mix(in srgb,var(--warn) 32%,var(--line))}

/* --------------------------------------------------------------------------
   SALES — the salesman's view. Deliberately the least dense surface in the app:
   one card per truck, four things on it, sized to be read at arm's length while
   somebody is talking. Everything an owner needs and a salesman does not lives
   on the other three tabs.
   -------------------------------------------------------------------------- */
/* One search field, one set of chips, one sort. The tab used to stack five
   full-width bars — search, chips, a labelled sort row, a yellow banner and a
   count — before a single truck appeared. Same controls, two rows. */
.sfind{display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-bottom:12px}
.sfind input[type=search]{flex:1 1 320px; min-width:220px; padding:13px 16px 13px 43px;
  font-size:15px; border-radius:12px; border:1px solid var(--line);
  background:var(--panel); color:var(--ink); box-shadow:var(--lift);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238e8d85' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.8-3.8'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:15px 50%; background-size:16px}
.sfind input[type=search]:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 15%,transparent)}
.seg.sm button{padding:7px 12px; font-size:12px}

/* Chips and the sort control share a line: the sort is a property of this list,
   not a heading of its own. */
.filterline{display:flex; align-items:flex-start; gap:14px; margin-bottom:12px}
.filterline #specChips{flex:1 1 auto; min-width:0; margin-bottom:0}
.filterline .sortsel{flex:0 0 auto; margin-left:auto; padding:5px 9px;
  border-radius:9px; font-size:12px; font-weight:550; border:1px solid var(--line);
  background:var(--panel); color:var(--ink-2); box-shadow:none; max-width:210px}
.filterline .sortsel:hover{color:var(--ink); border-color:var(--axis)}

.chips{display:flex; flex-wrap:wrap; gap:6px; align-items:center}
/* Row two sits visibly under row one rather than reading as more of the same.
   flex-basis 100% makes the rule span the row instead of the chip. */
.chips.sub{margin-top:7px; padding-top:9px; border-top:1px solid var(--line-2);
  flex:1 0 100%}
.chip.ghostchip{background:transparent; border-style:dashed; color:var(--ink-3)}
.chip.clearchip{background:transparent; border-color:transparent; color:var(--accent);
  text-decoration:underline; text-underline-offset:2px; padding-left:6px}
.chip.clearchip:hover{color:var(--ink)}
.chiplab{font-size:10px; font-weight:650; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-3); margin:0 2px 0 6px}
.chiplab:first-child{margin-left:0}
.chip{background:var(--panel); border:1px solid var(--line); border-radius:20px;
  padding:5px 11px; font-size:12.5px; font-weight:550; color:var(--ink-2);
  cursor:pointer; display:inline-flex; align-items:center; gap:6px; box-shadow:none}
.chip:hover{color:var(--ink); border-color:var(--axis)}
.chip i{font-style:normal; font-size:10.5px; color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.chip.on{background:var(--accent); border-color:var(--accent); color:#fff}
.chip.on i{color:rgba(255,255,255,.75)}

.scount{font-size:12px; color:var(--ink-3); margin:0 2px 12px;
  font-variant-numeric:tabular-nums}

/* Uncategorised notice. It is a gap to close, not a fault, so it gets one warm
   edge and nothing else — a filled yellow panel at full width out-shouted the
   trucks underneath it, which are the reason the page exists. */
.uncat{display:flex; align-items:baseline; gap:7px; flex-wrap:wrap;
  background:var(--panel); border:1px solid var(--line);
  border-left:3px solid color-mix(in srgb,var(--warn) 70%,transparent);
  border-radius:10px; padding:10px 14px; margin:0 0 12px; font-size:12.5px;
  color:var(--ink-3); box-shadow:var(--lift)}
.uncat b{font-size:13px; font-weight:680; font-variant-numeric:tabular-nums;
  color:var(--ink)}
.uncat .lnk{background:none; border:none; box-shadow:none; padding:0; margin-left:2px;
  font-size:12.5px; font-weight:600; color:var(--accent); cursor:pointer;
  text-decoration:underline; text-underline-offset:2px}
.uncat .lnk:hover{color:var(--ink)}

.cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(352px,1fr)); gap:14px}

/* A flex column with the status row pinned to the bottom, so cards in a row line
   up along two edges instead of one. Ragged bottoms are most of what makes a
   card grid look unfinished. */
.scard{position:relative; background:var(--panel); border:1px solid var(--line);
  border-radius:14px; padding:15px 17px 15px 18px; cursor:pointer;
  box-shadow:var(--lift), var(--inset); display:flex; flex-direction:column;
  min-height:158px; overflow:hidden;
  transition:border-color .14s, transform .1s, box-shadow .18s}
.scard:hover{border-color:var(--axis); transform:translateY(-1px);
  box-shadow:var(--lift-2), var(--inset)}
.scard:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
/* The ready stripe as a pseudo-element, not a border: a 3px border on a 14px
   radius flattens the two corners it touches. */
.scard::before{content:""; position:absolute; left:0; top:12px; bottom:12px; width:3px;
  border-radius:0 3px 3px 0; background:transparent; transition:background .14s}
.scard.ready::before{background:var(--good)}
/* Thumbnail beside the name; price on its own line underneath. Squeezing the
   price into a third column left the name about 160px to say
   "2021 FREIGHTLINER M2 106 75' OCE REAR MOUNT BUCKET TRUCK" in — so every card
   on the page ended in an ellipsis and none of them said what the truck was. */
.shead{display:grid; grid-template-columns:74px minmax(0,1fr); gap:13px;
  align-items:start}
.sname{min-width:0}
/* Two lines, then ellipsis. DealerCenter descriptions run to 70+ characters
   ("2010 ALTEC ENVIRONMENTAL PRODUCTS 13" DC1317 WOOD CHIPPER KUBOTA DIESEL")
   and left unclamped they make every card a different height, which is what
   turns a grid into a mess. The full text is one click away in the drawer. */
/* Both lines hold their height whether or not they fill it, so the price and the
   status row land on the same baseline on every card across a row. Two cards
   side by side whose prices sit 14px apart is the difference between a grid and
   a pile, and it is worth the occasional short card carrying some white space. */
.sname b{display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; font-size:14px; font-weight:620; line-height:1.35;
  letter-spacing:-.01em; height:37.8px}
.sname span{display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; font-size:11.5px; color:var(--ink-3); margin-top:4px;
  line-height:1.5; height:34.5px}
/* Price, age and where it is standing: one line, read left to right in the order
   a salesman says them. */
.sfoot{display:flex; align-items:baseline; gap:10px; margin-top:13px; min-width:0}
.sfoot .p{font-size:18px; font-weight:670; letter-spacing:-.028em; line-height:1.1;
  font-variant-numeric:tabular-nums; white-space:nowrap}
/* "Not priced" is true of 230 units. In red it read as 230 faults; it is one job
   on a list, so it looks like the blank field it is. */
.sfoot .p.none{font-size:10.5px; font-weight:650; color:var(--ink-3);
  letter-spacing:.05em; text-transform:uppercase; border:1px dashed var(--line);
  border-radius:6px; padding:3px 8px; position:relative; top:-2px}
.sfoot .d{font-size:11px; color:var(--ink-3); white-space:nowrap;
  font-variant-numeric:tabular-nums}
.sfoot .loc{margin-left:auto; display:flex; align-items:center; gap:6px; min-width:0;
  font-size:11.5px; color:var(--ink-2); position:relative; top:-1px}
.sfoot .loc .pin{font-size:7px; color:var(--ink-3); flex:none}
.sfoot .loc em{font-style:normal; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; min-width:0}
.sfoot.away .loc{color:var(--warn-2)}
.sfoot.away .loc .pin{color:var(--warn-2)}
.stags{display:flex; flex-wrap:wrap; gap:5px; margin-top:12px; padding-top:11px;
  border-top:1px solid var(--line-3)}
.scard > .stags:last-child{margin-top:auto}
.snote{font-size:11.5px; color:var(--ink-3); line-height:1.5; margin:10px 0 0;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
.showmore{grid-column:1/-1; justify-self:center; margin-top:6px}

/* Card thumbnail. Fixed box so a portrait phone shot and a landscape lot photo
   both sit on the same grid line instead of shoving the text around — and it is
   drawn even when there is no photo, because a slot that appears on some cards
   and not others makes every row of the grid a different shape. */
.sthumb{position:relative; flex:0 0 auto; width:74px; height:58px; border-radius:9px;
  overflow:hidden; background:var(--line-3); border:1px solid var(--line)}
.sthumb img{width:100%; height:100%; object-fit:cover; display:block}
.sthumb i{position:absolute; right:3px; bottom:3px; font-style:normal; font-size:9.5px;
  font-weight:700; color:#fff; background:rgba(0,0,0,.62); border-radius:20px;
  padding:1px 6px; line-height:1.5}
/* Not .empty — that is the page-level empty state, whose 52px padding wins the
   box-sizing floor and makes the tile twice its height. */
.sthumb.blank{display:flex; align-items:center; justify-content:center;
  border-style:dashed; background:transparent}
.sthumb.blank b{font-size:15px; font-weight:400; color:var(--deemph); line-height:1}

/* A photo file the browser refuses to draw. Shown as a plain grey tile that says
   so, instead of the broken-image icon that reads as "the whole app is broken". */
.nopic{display:flex; align-items:center; justify-content:center; text-align:center}
.nopicmsg{font-size:9.5px; line-height:1.25; color:var(--ink-3); padding:0 5px}
.pcell.nopic .nopicmsg{font-size:11px}

/* --------------------------------------------------------------- drawer, rebuilt */
.pwrap{margin:0 0 18px}
.pgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(92px,1fr)); gap:7px}
.pcell{position:relative; padding:0; border:2px solid transparent; border-radius:10px;
  overflow:hidden; background:var(--line-3); cursor:pointer; aspect-ratio:4/3;
  box-shadow:none; display:block; width:100%}
.pcell img{width:100%; height:100%; object-fit:cover; display:block}
.pcell .tick{position:absolute; top:5px; right:5px; width:19px; height:19px;
  border-radius:50%; background:rgba(255,255,255,.9); border:1px solid var(--line);
  color:transparent; font-size:11px; font-style:normal; line-height:19px;
  text-align:center; font-weight:800}
.pcell.on{border-color:var(--accent)}
.pcell.on .tick{background:var(--accent); border-color:var(--accent); color:#fff}
.pcell.static{cursor:default; border-color:transparent}
/* Which photo the rest of the app is using. Bottom-left, out of the way of the
   selection tick, and small — it is a label on the one that is already the
   answer, not a button to press. */
.pcell .mainpip{position:absolute; left:5px; bottom:5px; font-style:normal;
  font-size:8.5px; font-weight:700; letter-spacing:.09em; line-height:1;
  padding:4px 5px; border-radius:5px; color:#fff;
  background:color-mix(in srgb,#000 62%,transparent);
  backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px)}
.pcell.main{border-color:color-mix(in srgb,var(--accent) 55%,transparent)}
.pcell.main.on{border-color:var(--accent)}
.pactions{display:flex; align-items:center; gap:9px; margin-top:9px; flex-wrap:wrap}
.pcount{flex:1; font-size:11.5px; color:var(--ink-3); font-variant-numeric:tabular-nums}
.pnone{display:flex; align-items:center; gap:7px; font-size:12.5px; color:var(--ink-3);
  background:var(--line-3); border-radius:10px; padding:14px 16px}
.lnk{background:none; border:none; box-shadow:none; padding:0; font-size:12.5px;
  font-weight:600; color:var(--accent); cursor:pointer; text-decoration:underline;
  text-underline-offset:2px}
.lnk:hover{color:var(--ink)}

.dhero{display:grid; grid-template-columns:repeat(4,1fr); gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:12px; overflow:hidden; margin-bottom:14px}
.dhero .hp{background:var(--panel); padding:11px 12px}
.dhero k{display:block; font-size:9.5px; font-weight:650; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3)}
.dhero v{display:block; font-size:16px; font-weight:680; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums; margin-top:3px}
.dhero v.none{font-size:13px; color:var(--neg)}
.dhero v.bad{color:var(--neg)}

.dline{display:flex; align-items:center; gap:7px; flex-wrap:wrap; font-size:12.5px;
  color:var(--ink-2); margin-bottom:6px}
.dline .pin{font-size:8px; color:var(--ink-3)}
.dline b{font-weight:600}
.dline b.away{color:var(--neg)}
.dline em{font-style:normal; font-size:11px; font-weight:650; color:var(--ink-3);
  border:1px solid var(--line); border-radius:20px; padding:2px 8px; margin-left:2px}

.catrow{display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin-bottom:16px}
.catsel{padding:8px 11px; border-radius:9px; font-size:13px; font-weight:600;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.catnote{font-size:11px; color:var(--ink-3)}
.subtag{font-size:10.5px; font-weight:650; color:var(--ink-2); border:1px solid var(--line);
  border-radius:20px; padding:3px 9px}
/* A chosen value, not a problem — so blue, unlike the red hold-up tags. */
.tagbtn.pick{background:color-mix(in srgb,var(--accent) 12%,transparent);
  border-color:color-mix(in srgb,var(--accent) 45%,transparent); color:var(--accent)}
.override{display:block; font-style:normal; font-size:11px; color:var(--accent);
  margin-top:3px}

.dfold{border-top:1px solid var(--line-2); margin-bottom:2px}
.dfold summary{cursor:pointer; list-style:none; padding:13px 2px; font-size:10.5px;
  font-weight:650; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3);
  display:flex; align-items:center; gap:7px}
.dfold summary::-webkit-details-marker{display:none}
.dfold summary:before{content:"›"; display:inline-block; font-size:14px;
  transition:transform .14s; color:var(--ink-3)}
.dfold[open] summary:before{transform:rotate(90deg)}
.dfold summary:hover{color:var(--ink)}
.dfold .dgrid{margin-bottom:16px}

.pbar{height:4px; border-radius:3px; background:var(--line-3); overflow:hidden;
  margin:12px 0 4px}
.pbar i{display:block; height:100%; width:0; background:var(--accent);
  transition:width .18s}
.fhint.ok{color:var(--pos)}
.fhint.bad{color:var(--neg)}

/* Tag pickers — in the detail drawer and on the add form. */
.tagpick{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:14px}
.tagbtn{background:var(--panel); border:1px solid var(--line); border-radius:9px;
  padding:7px 11px; font-size:12.5px; font-weight:550; color:var(--ink-3);
  cursor:pointer; display:inline-flex; align-items:center; gap:6px; box-shadow:none}
.tagbtn:hover{color:var(--ink); border-color:var(--axis)}
.tagbtn i{font-style:normal; font-size:10.5px; opacity:.8;
  font-variant-numeric:tabular-nums}
.tagbtn.on{background:color-mix(in srgb,var(--bad) 12%,transparent);
  border-color:color-mix(in srgb,var(--bad) 45%,transparent); color:var(--neg)}
.flab{display:block; font-size:11px; font-weight:600; color:var(--ink-3);
  margin:0 0 6px 2px; letter-spacing:.02em}
.fhint{font-size:11px; color:var(--ink-3); margin:-6px 0 14px 2px; min-height:14px}

/* Sits under a money box and says what the number was read as. Collapses to
   nothing when empty so an untouched form has no blank gaps in it. */
.fieldhint{font-size:11px; color:var(--ink-3); margin:-8px 0 12px 105px;
  text-align:right; padding-right:2px; line-height:1.3}
.fieldhint:empty{display:none}
.fieldhint.ok{color:var(--pos)}
.fieldhint.soft{color:var(--ink-3); opacity:.85}

/* The filter bar is one bar. Four dropdowns of four different widths, each
   sized to its own longest option, is what makes a toolbar look accidental —
   so they share a width and sit in a single bordered group. */
.fbar{display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:14px}
.fbar .grow{flex:1 1 240px; min-width:170px}
.fbar input[type=search]{width:100%; padding:9px 12px 9px 36px; font-size:13px;
  border-radius:10px; border:1px solid var(--line); background:var(--panel);
  color:var(--ink); box-shadow:var(--lift);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238e8d85' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.8-3.8'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:12px 50%; background-size:14px}
.fbar input[type=search]:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 15%,transparent)}
.fbar select{padding:8px 10px; border-radius:10px; font-size:12.5px;
  flex:0 1 150px; min-width:118px; color:var(--ink-2)}
.fbar select:hover{color:var(--ink)}
.fbar .cnt{font-size:12px; color:var(--ink-3); white-space:nowrap; margin-left:auto;
  font-variant-numeric:tabular-nums}

/* Fixed layout. Left to itself the browser hands the widest column — a 70
   character DealerCenter description — as much room as it wants and squeezes
   the money columns until "−$6,238" breaks across two lines. Every column
   that holds a known shape of thing gets a width; the description takes the
   rest. */
table.ut{border-collapse:collapse; width:100%; font-size:13px; min-width:962px;
  table-layout:fixed}
table.ut th:nth-child(1){width:58px}
table.ut th:nth-child(3){width:80px}
table.ut th:nth-child(4){width:78px}
table.ut th:nth-child(5){width:100px}
table.ut th:nth-child(6),table.ut th:nth-child(7){width:84px}
table.ut th:nth-child(8){width:88px}
table.ut th:nth-child(9){width:200px}
table.ut th,table.ut td{padding-left:10px; padding-right:10px}
table.ut td.n{white-space:nowrap}
table.ut th,table.ut td{padding:10px 12px; border-bottom:1px solid var(--line-2);
  vertical-align:middle}
table.ut th{position:sticky; top:0; z-index:2; background:var(--panel);
  color:var(--ink-3); font-weight:660; font-size:9.5px; letter-spacing:.085em;
  text-transform:uppercase; text-align:left; white-space:nowrap; cursor:pointer;
  user-select:none; box-shadow:0 1px 0 var(--line); padding-top:12px; padding-bottom:12px}
table.ut th:hover{color:var(--ink)}
table.ut th.n,table.ut td.n{text-align:right; font-variant-numeric:tabular-nums}
table.ut th[aria-sort]{color:var(--ink)}
table.ut th[aria-sort]:after{content:"↓"; margin-left:5px; opacity:.55}
table.ut th[aria-sort="ascending"]:after{content:"↑"}
table.ut tbody tr{cursor:pointer; transition:background .1s}
table.ut tbody tr:hover{background:var(--line-3)}
table.ut tbody tr:last-child td{border-bottom:none}
table.ut .veh{max-width:420px}
table.ut .veh b{display:block; font-weight:600; font-size:13px; line-height:1.35;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; letter-spacing:-.008em}
table.ut .veh span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
table.ut td.mono{font-variant-numeric:tabular-nums; font-weight:650;
  color:var(--ink-2); letter-spacing:.01em}
/* Status reads as a word, not a shout: sentence case, muted, never all-caps. */
table.ut td.st{font-size:12px; color:var(--ink-2); white-space:nowrap}
table.ut td.neg{color:var(--neg)}
/* Flags stay on one line. Wrapping them added 30px to the height of every row in
   the table, which cost four rows of the window to say the same thing. */
table.ut td .tags{flex-wrap:nowrap; overflow:hidden}


/* --------------------------------------------------------------------------
   THREE LEVELS, AND ONLY THREE.

   high — costs money right now: priced under cost, title 90 days overdue.
   med  — somebody has to do something: no price, needs repairs, awaiting arrival.
   low  — a fact about the record, not a fault: no photos, no VIN, sat a while.

   The 'low' level is the important one. 480 of 482 units have no photos and 361
   sit in recon; printing those in red put an alarm on every card and every row,
   which is exactly how an alarm stops being read. Low is grey, deliberately.
   -------------------------------------------------------------------------- */
.tags{display:flex; flex-wrap:wrap; gap:4px}
.tag{font-size:10.5px; font-weight:600; padding:2.5px 8px; border-radius:7px;
  border:1px solid var(--line); background:var(--panel-2); color:var(--ink-3);
  white-space:nowrap; letter-spacing:.005em; display:inline-flex; align-items:center;
  gap:5px; line-height:1.45}
.tag.low{}
.tag.high{color:var(--bad-2); border-color:color-mix(in srgb,var(--bad) 42%,transparent);
  background:color-mix(in srgb,var(--bad) 8%,transparent)}
.tag.med{color:var(--warn-2); border-color:color-mix(in srgb,var(--warn) 46%,transparent);
  background:color-mix(in srgb,var(--warn) 11%,transparent)}
.tag.ok{color:var(--pos); border-color:color-mix(in srgb,var(--good) 36%,transparent);
  background:color-mix(in srgb,var(--good) 8%,transparent)}
.tag.more{color:var(--ink-3); background:transparent; border-style:dashed}

/* Under the number, not beside it. Inline, it drew a hairline from the day count
   across to the next column and read as a leader rule joining two unrelated
   cells. */
.agebar{display:block; width:40px; height:3px; border-radius:2px;
  background:var(--line-3); overflow:hidden; margin:5px 0 0 auto}
.agebar i{display:block; height:100%; border-radius:2px; background:var(--deemph)}
/* No colour. Length already says how long, and on a list sorted by age every
   single bar was full and amber, which is a stripe, not information. */
.agebar.hot i{background:var(--axis)}

/* --- worklist cards --- */
.work{display:grid; grid-template-columns:repeat(auto-fill,minmax(330px,1fr)); gap:16px}
.wcard{background:var(--panel); border:1px solid var(--line); border-radius:var(--r);
  box-shadow:var(--lift); overflow:hidden; display:flex; flex-direction:column}
.wcard > header{padding:16px 18px 13px; border-bottom:1px solid var(--line-2)}
.wcard h3{margin:0; font-size:13.5px; font-weight:650; letter-spacing:-.01em;
  display:flex; align-items:center; gap:9px}
/* The count is coloured by what the pile costs, not by how tall it is. "343 with
   no photos" is a long afternoon; "11 with an overdue title" is money that
   cannot close. Only the second one gets to be red. */
.wcard h3 em{font-style:normal; font-size:11px; font-weight:650; padding:2px 8px;
  border-radius:7px; background:var(--line-3); color:var(--ink-2)}
.wcard h3 em.high{background:color-mix(in srgb,var(--bad) 11%,transparent); color:var(--bad-2)}
.wcard h3 em.med{background:color-mix(in srgb,var(--warn) 15%,transparent); color:var(--warn-2)}
.wcard .cap{margin:5px 0 0; font-size:11.5px; color:var(--ink-3); line-height:1.5}
.wcard ul{list-style:none; margin:0; padding:5px 8px 8px; max-height:280px; overflow:auto}
.wcard li{display:flex; gap:10px; align-items:baseline; padding:7px 10px; border-radius:9px;
  cursor:pointer; font-size:12.5px}
.wcard li:hover{background:var(--line-3)}
.wcard li b{font-variant-numeric:tabular-nums; font-weight:650; flex:0 0 auto; font-size:12px}
.wcard li span{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; color:var(--ink-2)}
.wcard li i{font-style:normal; flex:0 0 auto; font-size:11.5px; color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.wcard .more{padding:9px 18px 14px; font-size:11.5px; color:var(--ink-3)}

/* --- the check-up: one row per truck, not one card per problem -------------
   Greg: "everything in inventory needs to be auto checked and put in there
   everything needs to be connected ... accesible and easy to use/make
   changes". It sits at the top of Attention and it is deliberately wider and
   plainer than the cards under it -- a worklist, not a statistic. */
.checkup{border-left:3px solid var(--accent)}
.checkbar{display:flex; flex-wrap:wrap; gap:6px; padding:0 18px 14px}
.ckbtn{display:inline-flex; align-items:center; gap:6px; padding:5px 11px;
  border-radius:999px; font-size:12px; font-weight:550; line-height:1.5;
  border:1px solid var(--line); background:var(--panel); color:var(--ink-2);
  cursor:pointer}
.ckbtn:hover{border-color:var(--axis); color:var(--ink)}
.ckbtn.pick{background:var(--ink); border-color:var(--ink); color:var(--panel)}
.ckbtn i{font-style:normal; font-size:11px; font-variant-numeric:tabular-nums;
  opacity:.72}
.cklist{display:flex; flex-direction:column; border-top:1px solid var(--line-2)}
.ckrow{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.05fr) 74px;
  gap:12px; align-items:center; width:100%; text-align:left; cursor:pointer;
  padding:9px 18px; background:transparent; border:0;
  border-bottom:1px solid var(--line-3); color:inherit; font:inherit}
.ckrow:last-child{border-bottom:0}
.ckrow:hover{background:var(--line-3)}
.ck-l{display:flex; align-items:baseline; gap:9px; min-width:0}
.ck-l b{flex:0 0 auto; font-size:12.5px; font-weight:650;
  font-variant-numeric:tabular-nums}
.ck-l span{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; font-size:12px; color:var(--ink-3)}
.ck-g{display:flex; flex-wrap:wrap; gap:5px; min-width:0}
.ckchip{font-style:normal; font-size:11px; font-weight:550; line-height:1.6;
  padding:1px 7px; border-radius:4px; white-space:nowrap;
  background:var(--line-3); color:var(--ink-2); border:1px solid transparent}
.ckchip.high{background:var(--neg-soft,var(--line-3)); color:var(--neg);
  border-color:var(--neg)}
.ckchip.med{background:var(--line-3); color:var(--ink-2); border-color:var(--line)}
.ckrow u{text-decoration:none; text-align:right; font-size:11.5px;
  color:var(--ink-3); font-variant-numeric:tabular-nums}
.ckfoot{margin:0; padding:12px 18px 16px; font-size:11.5px; line-height:1.6;
  color:var(--ink-3); border-top:1px solid var(--line-2)}
@media (max-width:820px){
  .ckrow{grid-template-columns:minmax(0,1fr) 66px; row-gap:6px}
  .ck-g{grid-column:1 / -1}
}


/* --- detail drawer --- */
.scrim{position:fixed; inset:0; background:rgba(10,12,16,.42); backdrop-filter:blur(2px);
  opacity:0; pointer-events:none; transition:opacity .18s; z-index:80}
.scrim.on{opacity:1; pointer-events:auto}
.drawer{position:fixed; top:0; right:0; bottom:0; width:min(460px,100%);
  background:var(--panel);
  border-left:1px solid var(--line); z-index:90; display:flex; flex-direction:column;
  transform:translateX(102%); transition:transform .22s cubic-bezier(.2,.7,.3,1)}
/* The shadow belongs to an open drawer. A parked one sits nine pixels past the
   right edge, but a 44px blur does not — it bled back in as a grey band down
   the side of every page, which read as the window being cut off. */
.drawer.on{transform:none; box-shadow:-16px 0 44px rgba(10,12,16,.22)}
.drawer > header{padding:20px 22px 15px; border-bottom:1px solid var(--line-2);
  display:flex; align-items:flex-start; gap:12px}
.drawer > header h2{margin:0; font-size:15px; font-weight:660; letter-spacing:-.02em;
  line-height:1.35}
.drawer > header .sub{font-size:11.5px; color:var(--ink-3); margin-top:4px}
.drawer > header button{margin-left:auto; flex:0 0 auto; padding:5px 10px; font-size:16px;
  line-height:1; border-radius:9px}
/* Two drawers (Publish, and one deal) were written with .dhead instead of a
   bare <header> and no rule ever existed for it — so their title, their sub-line
   and their close button stacked down the panel in three full-width blocks. Same
   shape as the header above, one selector list. */
.drawer > .dhead{padding:20px 22px 15px; border-bottom:1px solid var(--line-2);
  display:flex; align-items:flex-start; gap:12px}
.drawer > .dhead > div{min-width:0; flex:1}
.drawer > .dhead h2{margin:0; font-size:15px; font-weight:660; letter-spacing:-.02em;
  line-height:1.35}
.drawer > .dhead .dsub{font-size:11.5px; color:var(--ink-3); margin:4px 0 0;
  line-height:1.5}
.drawer > .dhead .x{flex:0 0 auto; padding:5px 10px; font-size:16px; line-height:1;
  border-radius:9px; background:transparent; border-color:transparent;
  box-shadow:none; color:var(--ink-2)}
.drawer > .dhead .x:hover{background:var(--line-3); color:var(--ink)}
.drawer .dbody{flex:1; overflow:auto; padding:18px 22px 26px}
.dgrid{display:grid; grid-template-columns:1fr 1fr; gap:14px 18px; margin-bottom:20px}
.dgrid > div{min-width:0}
.dgrid k{display:block; font-size:10px; font-weight:600; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:3px}
.dgrid v{display:block; font-size:13.5px; font-weight:550; word-break:break-word;
  font-variant-numeric:tabular-nums}
.dsec{font-size:10.5px; font-weight:650; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-3); margin:22px 0 11px; padding-top:16px; border-top:1px solid var(--line-2)}
.drawer textarea{width:100%; min-height:78px; padding:10px 12px; border-radius:10px;
  border:1px solid var(--line); background:var(--panel-2,var(--panel)); font-size:13px;
  resize:vertical; line-height:1.5}
.drawer .erow{display:flex; gap:9px; align-items:center; margin-bottom:12px}
.drawer .erow label{font-size:12px; color:var(--ink-3); flex:0 0 96px}
.drawer .erow input{flex:1; padding:8px 11px; border-radius:9px; text-align:right}
.drawer .save{display:flex; gap:9px; align-items:center; margin-top:14px}
.drawer .save .msg{font-size:12px; color:var(--ink-3)}

/* --- the truck's own screen ----------------------------------------------
   Greg, on opening a truck: "that little side bar is not inutitive at all and
   just overall crowded ... rework that to be centered and more spacious".

   He is right, and the cause is arithmetic rather than taste. A 460px rail has
   about 415px of usable width; the drawer was asked to hold photographs, four
   money tiles, a category picker, two rows of buttons, a price box, a note box,
   a freight history and two folds. Everything queued into one narrow column,
   so nothing could sit beside anything, and the eye had eleven screens of
   scrolling to do before it reached the Save button.

   So the truck gets a stage instead of a rail: a centred sheet with room for
   two columns — what the truck IS on the left, what there is to DO with it on
   the right. The Save button lands above the fold. Every other drawer in the
   app is untouched; this class is added to one panel by name. */
.drawer.stage{
  top:0; right:0; bottom:0; left:0; margin:auto;
  width:min(1080px, calc(100vw - 56px));
  height:min(884px, calc(100vh - 56px));
  border:1px solid var(--line); border-radius:20px; overflow:hidden;
  /* Off-stage is smaller and faded rather than shoved sideways — a sheet that
     slides in from the right edge and lands in the middle reads as a mistake. */
  transform:translateY(10px) scale(.988); opacity:0;
  visibility:hidden; pointer-events:none;
  transition:transform .2s cubic-bezier(.2,.7,.3,1), opacity .16s ease,
             visibility 0s linear .2s}
.drawer.stage.on{
  transform:none; opacity:1; visibility:visible; pointer-events:auto;
  /* A drawer parked against the right edge only ever cast its shadow to the
     left, so one offset did the job. A centred sheet has page on all four
     sides and needs a halo rather than a direction -- with a downward-only
     shadow the pixels beside it were indistinguishable from the scrim, which
     is the whole point of the shadow gone. */
  box-shadow:0 26px 64px rgba(10,12,16,.30),
             0 0 38px rgba(10,12,16,.24),
             0 2px 8px rgba(10,12,16,.14);
  transition:transform .2s cubic-bezier(.2,.7,.3,1), opacity .16s ease,
             visibility 0s}

.drawer.stage > header{padding:22px 30px 18px; gap:16px}
.drawer.stage > header h2{font-size:20px; line-height:1.25}
.drawer.stage > header .sub{font-size:12.5px; margin-top:6px}
.drawer.stage > header button{padding:7px 13px; font-size:18px}

/* Two columns: the truck, then the work. `align-items:start` so a short right
   column does not stretch itself to match a tall left one. */
.drawer.stage .dbody{padding:24px 30px 34px}
.stagegrid{display:grid; grid-template-columns:minmax(0,1.02fr) minmax(0,1fr);
  gap:0 34px; align-items:start}
.stagegrid > div{min-width:0}
/* The right column is the one with the controls, so it gets a hairline to sit
   against rather than floating next to the photographs. */
.stagegrid > .stgb{padding-left:34px; margin-left:-34px;
  border-left:1px solid var(--line-2)}

/* Room to breathe. The tight numbers below were chosen for a 415px column and
   read as cramped at 480. */
.drawer.stage .dsec{margin:26px 0 13px; padding-top:18px}
.drawer.stage .stagegrid > div > .dsec:first-child{margin-top:0; padding-top:0;
  border-top:0}
.drawer.stage .dgrid{gap:16px 20px; margin-bottom:22px}
.drawer.stage .dhero{margin-bottom:16px}
.drawer.stage .dhero .hp{padding:14px 14px}
.drawer.stage .dhero v{font-size:18px}
.drawer.stage .erow label{flex:0 0 104px; font-size:12.5px}
.drawer.stage .erow input{padding:10px 13px; font-size:14px}
.drawer.stage textarea{min-height:104px}
.drawer.stage .tagpick{gap:8px; margin-bottom:18px}
.drawer.stage .save{margin-top:18px}

/* One column again once two will not fit. The sheet stays centred — it is the
   shape Greg asked for — it just stops pretending it has a second column. */
@media (max-width: 1000px){
  .stagegrid{grid-template-columns:1fr; gap:0}
  .stagegrid > .stgb{padding-left:0; margin-left:0; border-left:0;
    border-top:1px solid var(--line-2); margin-top:26px; padding-top:4px}
  .drawer.stage > header{padding:18px 20px 15px}
  .drawer.stage > header h2{font-size:17px}
  .drawer.stage .dbody{padding:20px 20px 30px}
}
/* On a phone a centred sheet with a margin is a smaller phone. Full bleed. */
@media (max-width: 700px){
  .drawer.stage{width:100vw; height:100dvh; border-radius:0; border:0}
}


/* --- import panel --- */
.drop{border:1.5px dashed var(--axis); border-radius:14px; padding:34px 24px;
  text-align:center; background:var(--line-3); transition:.15s; cursor:pointer}
.drop:hover,.drop.over{border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 7%,transparent)}
.drop b{display:block; font-size:14px; font-weight:640; margin-bottom:5px}
.drop span{font-size:12.5px; color:var(--ink-3); line-height:1.6}
.impmsg{margin-top:14px; font-size:13px; line-height:1.6}
.impmsg.bad{color:var(--neg)}
/* A spec that is filled in with the wrong sort of thing. Not an error — nothing
   failed — but the listing quietly drops the row, so the screen has to say so. */
.lbad{margin:10px 0 2px; display:grid; gap:6px}
.lbadrow{display:flex; align-items:center; gap:10px; padding:8px 10px;
  border-radius:8px; font-size:12.5px; line-height:1.5;
  background:color-mix(in srgb, var(--warn) 13%, var(--panel));
  border:1px solid color-mix(in srgb, var(--warn) 32%, transparent)}
.lbadrow span{flex:1; color:var(--ink)}
.lbadrow button{flex:none}

/* What an import deliberately did NOT change. Quieter than a warning —
   nothing went wrong — but it has to be on screen. */
.impkept{margin-top:7px; padding:7px 10px; border-radius:7px;
  background:color-mix(in srgb, var(--accent) 8%, var(--panel));
  border:1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  color:var(--ink-2); font-size:12.5px; line-height:1.55}

@media (max-width:900px){
  .dgrid{grid-template-columns:1fr}
  .stat{min-width:104px; flex:1 1 104px; padding:10px 13px}
  .stat b{font-size:17px}
}

/* ==========================================================================
   LEADS & CRM
   The call list is a list of people, not a table of records — one row per
   person, the next step on the right, and the log buttons always visible so
   recording a call costs one click. Ten of twelve leads went uncalled because
   logging one was work; this is the fix.
   ========================================================================== */
.calllist{display:flex; flex-direction:column; gap:9px}
.crow{display:grid; grid-template-columns:1fr auto; gap:20px; align-items:start;
  background:var(--panel); border:1px solid var(--line); border-left:3px solid var(--axis);
  border-radius:13px; padding:14px 16px; cursor:pointer; box-shadow:var(--lift);
  transition:border-color .12s, transform .08s}
.crow:hover{border-color:var(--axis); transform:translateY(-1px)}
.crow:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.crow.closed{opacity:.6; border-left-color:var(--line)}
.cwho{min-width:0}
.cname{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
/* A pasted name with no spaces in it used to run straight across the card and
   under the next-step column. Break it wherever it has to break. */
.cname b{font-size:14.5px; font-weight:640; letter-spacing:-.01em;
  min-width:0; overflow-wrap:anywhere}
.cmeta,.cwants{overflow-wrap:anywhere}
.cco{font-size:11.5px; color:var(--ink-3)}
.cmeta{display:block; font-size:11.5px; color:var(--ink-3); margin-top:4px}
.cwants{display:block; font-size:12.5px; color:var(--ink-2); margin-top:7px;
  line-height:1.5}
.cunit{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:8px;
  padding-top:8px; border-top:1px solid var(--line-2); font-size:11.5px}
.cunit b{font-weight:650; font-variant-numeric:tabular-nums}
.cunit span{color:var(--ink-3)}
.cunit i{font-style:normal; font-size:10px; font-weight:700; padding:2px 7px;
  border-radius:20px; border:1px solid var(--line)}
.cunit i.good{color:var(--pos); border-color:color-mix(in srgb,var(--good) 40%,transparent)}
.cunit i.bad{color:var(--neg); border-color:color-mix(in srgb,var(--bad) 45%,transparent)}

.cnext{text-align:right; min-width:168px; display:flex; flex-direction:column;
  align-items:flex-end; gap:2px}
.cnext b{display:block; font-size:12.5px; font-weight:620}
.cnext span{display:block; font-size:11px; color:var(--ink-3); margin-top:3px}
/* Overdue — you said you would call and did not — stays red. "Never contacted"
   is the shape of a 344-lead import, not a fault, so it is amber. */
.cnext .over{color:var(--neg)}
.cnext .cold{color:var(--warn-2)}
.cnext .now{color:var(--warn-2)}
.cnext .muted{color:var(--ink-3); font-style:italic}
.cnext .stage{display:inline-block; margin-top:7px; font-size:10px; font-weight:650;
  letter-spacing:.04em; text-transform:uppercase; color:var(--ink-3);
  border:1px solid var(--line); border-radius:20px; padding:2px 8px}

/* One tight row, right under the next step. Three small buttons the whole point
   of which is that logging a call is never more than one click away. */
.cacts{display:flex; gap:5px; margin-top:9px}
.cacts.wide{margin:0 0 18px}
.cacts button{white-space:nowrap; padding:5px 9px; font-size:11.5px}

/* Warmth reads at a glance or it is not doing its job. */
.heat{width:9px; height:9px; border-radius:50%; display:inline-block; flex:0 0 auto;
  background:var(--mute)}
.heat.hot{background:var(--bad)}
.heat.warm{background:var(--warn)}
.heat.cold{background:var(--s1)}
.chip.heat-hot.on{background:var(--bad); border-color:var(--bad)}
.chip.heat-warm.on{background:var(--warn); border-color:var(--warn); color:#3a2c00}
.chip.heat-cold.on{background:var(--s1); border-color:var(--s1)}
.tagbtn.heat-hot.pick{background:color-mix(in srgb,var(--bad) 14%,transparent);
  border-color:color-mix(in srgb,var(--bad) 50%,transparent); color:var(--neg)}
.tagbtn.heat-warm.pick{background:color-mix(in srgb,var(--warn) 20%,transparent);
  border-color:color-mix(in srgb,var(--warn) 55%,transparent); color:var(--ink)}
.tagbtn.heat-cold.pick{background:color-mix(in srgb,var(--s1) 12%,transparent);
  border-color:color-mix(in srgb,var(--s1) 45%,transparent); color:var(--s1)}

.hist{list-style:none; margin:0; padding:0}
.hist li{padding:9px 0; border-bottom:1px solid var(--line-2)}
.hist li:last-child{border-bottom:none}
.hist b{display:block; font-size:12.5px; font-weight:550}
.hist span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px}
.lnk.danger{color:var(--neg)}
/* Only the tiles that actually go somewhere get the hand cursor. */
/* A number that is not known reads as absent, not as zero. */
.b-gross b.unk{color:var(--ink-3); font-weight:600}
.stat.click{cursor:pointer}
.stat.click:hover{border-color:var(--axis); transform:translateY(-1px);
  box-shadow:var(--lift-2), var(--inset)}

@media (max-width:900px){
  .crow{grid-template-columns:1fr; gap:10px}
  .cnext{text-align:left}
  .cacts{flex-direction:row}
}

/* Import preview. Nothing is written until this has been looked at — the point is
   to make a bad PDF read obvious before it becomes 200 junk contacts. */
.pvhead{display:flex; align-items:baseline; gap:10px; margin:14px 0 8px}
.pvhead b{font-size:17px; font-weight:680}
.pvhead span{font-size:11.5px; color:var(--ink-3)}
.pvwarn{font-size:11.5px; line-height:1.55; color:var(--ink-2); margin:0 0 10px;
  background:color-mix(in srgb,var(--warn) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 38%,transparent);
  border-radius:9px; padding:9px 11px}
.pvtable{width:100%; border-collapse:collapse; font-size:11.5px;
  border:1px solid var(--line); border-radius:10px; overflow:hidden}
.pvtable th{text-align:left; font-size:9.5px; letter-spacing:.07em; text-transform:uppercase;
  color:var(--ink-3); font-weight:650; padding:7px 9px; background:var(--panel-2);
  border-bottom:1px solid var(--line)}
.pvtable td{padding:6px 9px; border-bottom:1px solid var(--line-2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:150px}
.pvtable tr:last-child td{border-bottom:none}

/* --------------------------------------------------------------------------
   Toasts. One place the app speaks to a person about something that went wrong
   or something it just did. Bottom-left so it never covers the drawer, and an
   error with an Undo stays put until it is dealt with.
   -------------------------------------------------------------------------- */
#mmtoast{position:fixed; left:22px; bottom:22px; z-index:400; display:flex;
  flex-direction:column; gap:8px; max-width:min(460px,calc(100vw - 44px));
  pointer-events:none}
.toast{pointer-events:auto; display:flex; align-items:flex-start; gap:10px;
  background:var(--panel); border:1px solid var(--line); border-left:3px solid var(--axis);
  border-radius:12px; padding:12px 12px 12px 14px; box-shadow:var(--lift-2);
  font-size:13px; line-height:1.5; animation:toastin .18s ease-out}
.toast.out{opacity:0; transform:translateY(6px); transition:opacity .2s, transform .2s}
.toast .tmsg{flex:1; min-width:0; color:var(--ink-2)}
.toast.bad{border-left-color:var(--bad)}
.toast.good{border-left-color:var(--good)}
.toast.warn{border-left-color:var(--warn)}
.toast .tact{flex:0 0 auto; padding:4px 10px; font-size:12px; font-weight:650;
  border-radius:8px; background:var(--ink); border-color:var(--ink);
  color:var(--panel); box-shadow:none}
.toast .tact:hover{opacity:.88}
.toast .tx{flex:0 0 auto; background:none; border:none; box-shadow:none; padding:0 2px;
  font-size:16px; line-height:1; color:var(--ink-3); cursor:pointer}
.toast .tx:hover{color:var(--ink)}
@keyframes toastin{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@media (max-width:640px){ #mmtoast{left:12px; right:12px; bottom:12px; max-width:none} }

/* Shown in place of the page when the very first load fails. Deliberately plain
   and centred — this is the one screen somebody sees when nothing else works. */
.bootfail-wrap{position:fixed; inset:0; z-index:500; background:var(--bg);
  overflow:auto; padding:0 18px}
.bootfail{max-width:460px; margin:14vh auto; text-align:center; padding:26px 24px;
  border:1px solid var(--line); border-radius:16px; background:var(--panel)}
.bootfail h2{margin:0 0 10px; font-size:18px}
.bootfail p{margin:0 0 16px; font-size:13px; color:var(--ink-2); line-height:1.5}
.bootfail p.quiet{margin:14px 0 0; font-size:11px; color:var(--ink-3)}

/* ---------------------------------------------- listings, sold, and take-down */
/* The count on the Sold tab. A truck that has sold and is still advertised costs
   money every day it stays up, so the number is on the tab itself rather than
   waiting to be found. */
.seg button .pip{display:inline-block; margin-left:6px; min-width:17px; padding:0 5px;
  font-style:normal; font-size:10px; font-weight:800; line-height:17px; text-align:center;
  border-radius:20px; background:var(--neg); color:#fff; vertical-align:1px}
.seg.tight{margin:0 0 14px}

.notice{padding:11px 14px; border-radius:11px; margin:0 0 14px; font-size:13px;
  border:1px solid var(--line); background:var(--panel)}
.notice b{font-weight:700}
.notice span{color:var(--ink-2)}
.notice.bad{border-color:color-mix(in srgb, var(--neg) 40%, var(--line));
  background:color-mix(in srgb, var(--neg) 7%, var(--panel))}
.notice.bad b{color:var(--neg)}
.notice.ok{border-color:color-mix(in srgb, var(--pos) 35%, var(--line));
  background:color-mix(in srgb, var(--pos) 6%, var(--panel))}
.notice.ok b{color:var(--pos)}

.soldtbl tr.up td{background:color-mix(in srgb, var(--neg) 4%, transparent)}
.soldtbl td{vertical-align:top}
/* The sub-line under the vehicle name is styled by `table.ut .veh span`, which
   only matches the Units table — here it ran on as one sentence. */
.soldtbl .veh b{display:block}
.soldtbl .veh span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px}
.soldtbl .veh{max-width:520px}
.pullbtn{cursor:pointer; border:1px solid currentColor; background:transparent;
  font:inherit; font-size:11px; font-weight:700; padding:2px 8px; border-radius:20px;
  margin:0 5px 4px 0}
.pullbtn:hover{background:color-mix(in srgb, var(--neg) 12%, transparent)}
.pullbtn:disabled{opacity:.45; cursor:default}

/* The caption box. Monospace so the emoji columns line up the way they will in
   the post, and tall enough to show a whole caption without scrolling. */
.capbox{width:100%; min-height:210px; font-family:ui-monospace,SFMono-Regular,
  Menlo,Consolas,monospace; font-size:12px; line-height:1.55; white-space:pre-wrap}
#lEbayTitle{width:100%; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12px; padding:8px 11px; border-radius:9px; margin-bottom:4px}

.gaprow{display:flex; gap:8px; align-items:center; margin-bottom:9px}
.gaprow label{flex:0 0 104px; font-size:12px; color:var(--ink-3)}
.gaprow input, .gaprow select{flex:1; padding:7px 10px; border-radius:9px; font-size:13px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.gaprow button{flex:0 0 auto; white-space:nowrap}
.impmsg.warn{color:var(--ink); background:color-mix(in srgb, var(--warn) 12%, var(--panel));
  border:1px solid color-mix(in srgb, var(--warn) 45%, var(--line))}
.impmsg.warn b{display:block; margin-bottom:3px}
.impmsg.warn span{color:var(--ink-2)}
.impacts{display:flex; gap:8px; margin-top:11px; flex-wrap:wrap}

/* ---------------------------------------------- batch photo upload */
.orline{display:flex; align-items:center; gap:11px; margin:16px 0 12px;
  font-size:11px; color:var(--ink-3); text-transform:uppercase; letter-spacing:.07em}
.orline::before,.orline::after{content:""; flex:1; height:1px; background:var(--line)}
.drop.alt{border-style:dashed}

/* What the batch matched, shown before a single byte is uploaded. Six hundred
   photos into the wrong truck is not a recoverable mistake. */
.planlist{max-height:250px; overflow-y:auto; border:1px solid var(--line);
  border-radius:11px; margin:10px 0 4px; background:var(--panel-2)}
.planrow{display:grid; grid-template-columns:62px 1fr auto; gap:10px; align-items:baseline;
  padding:7px 12px; border-bottom:1px solid var(--line-3); font-size:12px}
.planrow:last-child{border-bottom:0}
.planrow b{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px}
.planrow span{color:var(--ink-2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.planrow i{font-style:normal; color:var(--ink-3); font-size:11px; white-space:nowrap}
.planrow.bad b, .planrow.bad span{color:var(--neg)}
.drop.alt code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11px;
  background:var(--line-3); padding:1px 5px; border-radius:5px; color:var(--ink-2)}
.drop.alt span{display:block}
.drop.alt button{display:block; margin:13px auto 0}

/* ============================================================ shipping */
.maptop{display:flex; align-items:center; justify-content:space-between; gap:16px;
  flex-wrap:wrap; margin:0 0 14px}
.maplegend{display:flex; gap:15px; flex-wrap:wrap}
.lgd{display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--ink-2)}
.lgd i{width:9px; height:9px; border-radius:50%; display:inline-block}

.zoomer{display:flex; align-items:center; gap:2px; padding:3px;
  border:1px solid var(--line); border-radius:10px; background:var(--panel-2)}
.zoomer button{padding:3px 10px; font-size:14px; line-height:1.3; border:0;
  background:transparent; box-shadow:none; border-radius:7px; color:var(--ink-2)}
.zoomer button:hover{background:var(--line-3); color:var(--ink)}
.zoomer button.zreset{font-size:11px; padding:5px 9px}
.zoomer span{font-size:11px; color:var(--ink-3); min-width:38px; text-align:center;
  font-variant-numeric:tabular-nums}

.mapsplit{display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:14px;
  align-items:start}
@media (max-width:1100px){ .mapsplit{grid-template-columns:1fr} }
.mapcard{margin:0}
.mapwrap{width:100%; overflow:hidden; border-radius:14px;
  background:linear-gradient(180deg, var(--panel-2), var(--bg));
  border:1px solid var(--line-3)}

.placepanel{padding:0; overflow:hidden; display:flex; flex-direction:column;
  max-height:620px}
.pp-head{padding:15px 16px 12px; border-bottom:1px solid var(--line)}
.pp-head h3{margin:0 0 10px; font-size:13px; font-weight:650}
.pp-head input{width:100%; padding:8px 12px; font-size:12.5px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel-2); color:var(--ink)}
.pp-list{overflow-y:auto; padding:5px}
.pp-none{padding:16px; margin:0}
.pp-row{display:grid; grid-template-columns:minmax(0,1fr) 46px 34px; gap:10px;
  align-items:center; width:100%; text-align:left; padding:9px 11px; border:0;
  border-radius:9px; background:transparent; box-shadow:none; cursor:pointer}
.pp-row:hover, .pp-row.hot{background:var(--line-3)}
.pp-row.on{background:color-mix(in srgb, var(--accent) 11%, transparent)}
.pp-row.home b{font-weight:700}
.pp-name{min-width:0}
.pp-name b{display:block; font-size:12.5px; font-weight:600; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.pp-name span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:1px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pp-bar{display:flex; height:6px; border-radius:4px; overflow:hidden;
  background:var(--line-3)}
.pp-bar i{display:block}
.pp-row em{font-style:normal; font-size:12.5px; font-weight:650; text-align:right;
  font-variant-numeric:tabular-nums}
.usmap{width:100%; height:auto; display:block; cursor:grab; touch-action:none}
.usmap.dragging{cursor:grabbing}
.usmap .state{fill:var(--panel); stroke:var(--axis); stroke-width:0.75;
  vector-effect:non-scaling-stroke}
.usmap .stlabel{font:600 8px system-ui; fill:var(--ink-3); text-anchor:middle;
  pointer-events:none; opacity:.38}
.usmap .spot.hot .dot{stroke:var(--ink); stroke-width:2.2;
  vector-effect:non-scaling-stroke}
.usmap .spot{cursor:pointer}
.usmap .spot .dot{stroke:var(--panel); stroke-width:1.4; vector-effect:non-scaling-stroke;
  transition:opacity .12s}
.usmap .spot:hover .dot{filter:brightness(1.08)}
.usmap .spot.on .dot{stroke:var(--ink); stroke-width:2; vector-effect:non-scaling-stroke}
.usmap .spot:focus{outline:none}
.usmap .spot:focus .dot{stroke:var(--ink); stroke-width:2; vector-effect:non-scaling-stroke}
.usmap .dotn{font-weight:700; fill:#fff; text-anchor:middle; pointer-events:none;
  font-size:calc(9px / var(--k, 1)); font-family:system-ui}
.usmap .homering{fill:none; stroke:var(--ink); stroke-width:1.3; opacity:.3;
  vector-effect:non-scaling-stroke}
.usmap .homedot{fill:var(--ink); stroke:var(--panel); stroke-width:1.8;
  vector-effect:non-scaling-stroke}
.usmap .spot.home:hover .homering{opacity:.65}
.mlabels{pointer-events:none}
.usmap .spotlab{font-family:system-ui; font-weight:700; fill:var(--ink);
  paint-order:stroke; stroke:var(--panel); stroke-width:4px;
  stroke-linejoin:round; pointer-events:none}
.usmap .spotlab.homelab{font-weight:700; fill:var(--ink)}
.maphint{margin:12px 2px 0; font-size:11.5px; color:var(--ink-3)}

/* --- locations underneath the map */
.spotgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:12px; margin-top:20px}
.spotcard{margin-top:0}
.spotcard.open{margin-top:20px}
.spothead{display:flex; align-items:flex-start; justify-content:space-between; gap:14px}
.spothead h3{margin:0 0 4px; font-size:14.5px; letter-spacing:-0.015em}
.spothead .cap{margin:0; line-height:1.5}
.trucklist{margin-top:6px; max-height:440px; overflow-y:auto}
.truckrow{display:grid; grid-template-columns:10px 62px 1fr auto; gap:12px;
  align-items:center; width:100%; text-align:left; padding:11px 6px; font-size:12.5px;
  background:transparent; border:0; border-bottom:1px solid var(--line-3);
  box-shadow:none; border-radius:0; cursor:pointer}
.truckrow:last-child{border-bottom:0}
.truckrow:hover{background:var(--line-3)}
.truckrow .kdot{width:8px; height:8px; border-radius:50%}
.truckrow b{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px}
.truckrow span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  color:var(--ink-2); min-width:0}
.truckrow span small{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px}
.truckrow em{font-style:normal; color:var(--ink-3); font-size:11.5px; white-space:nowrap}
.yardhead{padding:14px 6px 6px; font-size:10.5px; font-weight:700; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3); border-bottom:1px solid var(--line-3);
  position:sticky; top:0; background:var(--panel); z-index:1}
.listsearch{width:100%; margin:16px 0 2px; padding:10px 13px; font-size:13px;
  border-radius:10px; border:1px solid var(--line); background:var(--panel-2);
  color:var(--ink)}
.listcount{margin:6px 0 0; font-size:11px; color:var(--ink-3)}

/* --- the quote */
.qrow{display:grid; grid-template-columns:1fr 1fr; gap:20px}
@media (max-width:760px){ .qrow{grid-template-columns:1fr} }
.qfield{position:relative}
.qfield label{display:block; font-size:11px; font-weight:600; color:var(--ink-3);
  letter-spacing:.03em; margin-bottom:7px; text-transform:uppercase}
.qfield input{width:100%; padding:11px 14px; font-size:14px; border-radius:11px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.acbox{position:absolute; z-index:50; left:0; right:0; margin-top:4px;
  background:var(--panel); border:1px solid var(--line); border-radius:11px;
  box-shadow:var(--lift-2); overflow:hidden; max-height:250px; overflow-y:auto}
.acitem{display:block; width:100%; text-align:left; padding:9px 13px; font-size:13px;
  background:transparent; border:0; border-radius:0; box-shadow:none; cursor:pointer}
.acitem:hover{background:var(--line-3)}
.quote{margin-top:18px}
.qhead h3{margin:0 0 4px; font-size:16.5px; letter-spacing:-0.02em}
.qhead .cap{display:block}
.qbig{display:flex; align-items:baseline; gap:16px; margin:20px 0 4px; flex-wrap:wrap}
.qbig b{font-size:38px; font-weight:680; letter-spacing:-0.035em; line-height:1}
.qbig span{font-size:12.5px; color:var(--ink-3)}
.qwhy{margin:14px 0 0; font-size:11.5px; color:var(--ink-3); line-height:1.6}
.quote .impacts{margin-top:20px}

/* --- loads, as cards grouped by month */
.monthhead{display:flex; align-items:baseline; justify-content:space-between;
  gap:14px; margin:26px 2px 12px; padding-bottom:9px;
  border-bottom:1px solid var(--line)}
.monthhead:first-child{margin-top:6px}
.monthhead h3{margin:0; font-size:13px; font-weight:650; letter-spacing:-0.01em}
.monthhead span{font-size:11.5px; color:var(--ink-3)}
.loadgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(330px,1fr));
  gap:12px}
.loadcard{border:1px solid var(--line); border-radius:14px; background:var(--panel);
  padding:15px 17px; box-shadow:var(--lift)}
.lc-top{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.lc-route{font-size:14px; font-weight:680; letter-spacing:.01em}
.lc-date{font-size:11px; color:var(--ink-3); font-variant-numeric:tabular-nums}
.lc-what{margin:11px 0 0; border-top:1px solid var(--line-3); padding-top:10px}
.lc-stop{display:flex; gap:9px; align-items:baseline; font-size:12px; padding:2px 0}
.lc-stop b{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11.5px;
  min-width:44px}
.lc-stop span{color:var(--ink-2)}
.lc-nums{display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin:13px 0 0;
  padding:11px 0 0; border-top:1px solid var(--line-3)}
.lc-num b{display:block; font-size:16px; font-weight:660; letter-spacing:-0.02em;
  font-variant-numeric:tabular-nums}
.lc-num span{display:block; font-size:10px; color:var(--ink-3); margin-top:2px;
  line-height:1.35}
.lc-foot{display:flex; align-items:center; gap:10px; margin-top:13px; padding-top:11px;
  border-top:1px solid var(--line-3); font-size:11.5px; color:var(--ink-2)}
.lc-who{display:flex; align-items:center; gap:7px; flex:1; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.lc-size{font-size:10.5px; color:var(--ink-3); white-space:nowrap}
.lc-foot .lnk{font-size:11px}
.lc-note{margin:10px 0 0; font-size:11px; color:var(--ink-3); line-height:1.5}
.kindpip{width:7px; height:7px; border-radius:50%; flex:none; background:var(--s-neutral)}
.kindpip.ours{background:var(--s3)}
.kindpip.independent{background:var(--s4)}
.kindpip.broker{background:var(--s1)}

/* --- haulers */
.whogrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
  gap:11px; margin-top:16px}
.whocard{display:block; text-align:left; width:100%; padding:14px 16px;
  border:1px solid var(--line); border-radius:13px; background:var(--panel-2);
  box-shadow:none; cursor:pointer}
.whocard:hover{border-color:var(--axis); background:var(--panel); box-shadow:var(--lift)}
.whocard.loose{border-style:dashed}
.whotop b{display:block; font-size:13.5px; font-weight:650}
.whotop span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px}
.whostat{display:flex; align-items:baseline; gap:10px; margin-top:11px; flex-wrap:wrap}
.whostat em{font-style:normal; font-size:18px; font-weight:680; letter-spacing:-0.025em}
.whostat i{font-style:normal; font-size:11px; color:var(--ink-3)}
.wholane{margin:8px 0 0; font-size:10.5px; color:var(--ink-3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.picklab{display:block; width:100%; font-size:10px; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--ink-3); margin:12px 0 3px}
.picklab:first-child{margin-top:0}

/* --- log a load */
.stoprow{display:grid; grid-template-columns:100px 1fr auto; gap:8px; margin-bottom:8px;
  align-items:center; position:relative}
.stoprow input{padding:9px 12px; border-radius:10px; font-size:13px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.notice{padding:12px 15px; border-radius:12px; margin:0 0 14px; font-size:13px;
  border:1px solid var(--line); background:var(--panel); line-height:1.55}
.notice b{font-weight:700}
.notice span{color:var(--ink-2)}
.notice.bad{border-color:color-mix(in srgb, var(--neg) 38%, var(--line));
  background:color-mix(in srgb, var(--neg) 6%, var(--panel))}
.notice.bad b{color:var(--neg)}
.notice.ok{border-color:color-mix(in srgb, var(--pos) 32%, var(--line));
  background:color-mix(in srgb, var(--pos) 5%, var(--panel))}
.notice.ok b{color:var(--pos)}

/* where one truck has been */
.movelist{margin-top:12px; border-top:1px solid var(--line-3)}
.moverow{display:flex; align-items:baseline; justify-content:space-between; gap:14px;
  padding:11px 2px; border-bottom:1px solid var(--line-3)}
.moverow:last-child{border-bottom:0}
.mv-l{min-width:0}
.mv-l b{display:block; font-size:12.5px; font-weight:620}
.mv-l span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px}
.mv-r{text-align:right; white-space:nowrap}
.mv-r b.dim{color:var(--ink-3); font-weight:600}
.mv-r b{display:block; font-size:14px; font-weight:660; font-variant-numeric:tabular-nums}
.mv-r span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px}
.movenote{margin:0 0 16px; padding:9px 12px; border-radius:10px; font-size:11.5px;
  line-height:1.5; background:color-mix(in srgb, var(--pos) 7%, var(--panel-2));
  border:1px solid color-mix(in srgb, var(--pos) 26%, var(--line))}
.movenote b{color:var(--pos)}
.movenote span{color:var(--ink-2)}

/* ============================================================ deals */
.boardbar{display:flex; align-items:center; gap:14px; margin:0 0 14px}
.boardbar input{flex:1; max-width:420px; padding:10px 13px; font-size:13px;
  border-radius:10px; border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.board{border:1px solid var(--line); border-radius:14px; overflow:hidden;
  background:var(--panel); box-shadow:var(--lift)}
/* The steps column used to be `auto`, so six chips at their natural width took
   whatever they wanted and squeezed the buyer column to nothing — a company name
   then wrapped to three lines and ran straight under the chips. Both columns are
   told what they get: the chips a fixed lane wide enough for all six on one
   line, the buyer a real minimum. */
.brow{display:grid; grid-template-columns:126px minmax(96px,1fr) 452px 104px 140px;
  gap:12px; align-items:center; padding:12px 16px; border-bottom:1px solid var(--line-3)}
/* Both of these matter. min-width:0 lets a grid item be narrower than its
   content; overflow:hidden is what actually stops the content spilling into the
   next column when it is. Without the second one the six step chips laid
   themselves out 94px left of their own track and printed over the buyer. */
.brow > *{min-width:0; overflow:hidden}
.brow:last-child{border-bottom:0}
.brow:hover{background:var(--panel-2)}
.brow.bhead{background:var(--panel-2); font-size:10.5px; font-weight:700;
  letter-spacing:.07em; text-transform:uppercase; color:var(--ink-3); padding:10px 16px}
.brow.bhead:hover{background:var(--panel-2)}
.brow.won{background:color-mix(in srgb, var(--pos) 4%, transparent)}
.brow.lost{opacity:.5}
.b-unit .unitlink{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:13px; font-weight:700; padding:0; text-decoration:none}
.b-unit span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.b-who{min-width:0}
.b-who b{display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; font-size:13px; font-weight:620; line-height:1.3}
.b-who span{display:block; font-size:11px; color:var(--ink-3); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* One line, always. Six chips on two rows made every row of the board a
   different height and turned a list into a wall. */
.b-steps{display:flex; gap:4px; flex-wrap:nowrap; justify-content:flex-end}
.stepbox{display:flex; align-items:center; gap:4px; padding:4px 8px; font-size:10px;
  font-weight:600; border-radius:7px; border:1px solid var(--line);
  background:var(--panel-2); box-shadow:none; color:var(--ink-3); cursor:pointer;
  white-space:nowrap; flex:0 0 auto}
.stepbox i{font-style:normal; width:8px; display:inline-block; text-align:center;
  font-size:9px}
.stepbox:hover{border-color:var(--axis)}
.stepbox.on{background:color-mix(in srgb, var(--pos) 13%, var(--panel));
  border-color:color-mix(in srgb, var(--pos) 42%, var(--line)); color:var(--pos)}
.stepbox:disabled{opacity:.5}
/* Off at full width — the column header already says it, and printing "BALANCE"
   over "BUYER OWES" is the kind of doubling that makes a screen feel cluttered.
   Switched on in the card block below, where the header is gone and the label is
   the only thing saying what the number is. */
.b-money i,.b-gross i{display:none; font-style:normal; font-size:9px; font-weight:660;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:3px}
.b-money{text-align:right}
.b-money b{display:block; font-size:13.5px; font-weight:660; font-variant-numeric:tabular-nums}
.b-money b.owed{color:var(--warn-2)}
.b-money b.clear{color:var(--pos)}
.b-money span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:1px}
.b-gross{text-align:right}
.b-gross b{display:block; font-size:15px; font-weight:680; letter-spacing:-0.02em;
  font-variant-numeric:tabular-nums}
.b-gross b.neg{color:var(--neg)}
.b-gross span{display:block; font-size:10px; color:var(--ink-3); margin-top:2px;
  line-height:1.3}
/* The header row prints its own labels, so it must not print them twice. */
.brow.bhead .b-money i,.brow.bhead .b-gross i{display:none}
/* Below this the row stops being a table row and becomes a card. The header
   row's labels then belong to nothing — they were stacking into "STOCK BUYER /
   WHERE IT STANDS / BALANCE GROSS" over an empty band, which is where the board
   stopped being readable. Each card now carries its own labels instead. */
@media (max-width:1420px){
  .brow.bhead{display:none}
  .b-money i,.b-gross i{display:block}
  /* Wide enough that the truck description under the stock number is still a
     description rather than four characters and an ellipsis. */
  .brow{grid-template-columns:minmax(150px,210px) minmax(0,1fr) auto auto;
    row-gap:10px; align-items:start; padding:14px 16px}
  .b-unit{grid-row:1}
  .b-who{grid-row:1; grid-column:2/-1}
  .b-steps{grid-column:1/-1; grid-row:2; justify-content:flex-start;
    overflow-x:auto; scrollbar-width:none; flex-wrap:wrap; gap:5px}
  .b-steps::-webkit-scrollbar{display:none}
  .b-money{grid-row:3; grid-column:1/3; text-align:left}
  .b-gross{grid-row:3; grid-column:3/-1; text-align:right}
  .board .brow + .brow{border-top:1px solid var(--line-2)}
}
@media (max-width:720px){
  .brow{grid-template-columns:1fr auto}
  .b-unit{grid-column:1} .b-who{grid-column:1/-1; grid-row:2}
  .b-steps{grid-row:3} .b-money{grid-row:4; grid-column:1}
  .b-gross{grid-row:4; grid-column:2}
}
.drawer.wide{width:min(560px,100%)}
.dealmoney{margin:18px 0 6px; border:1px solid var(--line); border-radius:12px;
  overflow:hidden; background:var(--panel-2)}
.dm-row{display:flex; align-items:baseline; justify-content:space-between;
  padding:9px 14px; font-size:12.5px; border-bottom:1px solid var(--line-3)}
.dm-row:last-of-type{border-bottom:0}
.dm-row span{color:var(--ink-2)}
.dm-row b{font-variant-numeric:tabular-nums; font-weight:640}
.dm-row.total{background:var(--panel); border-top:1px solid var(--line)}
.dm-row.total b{font-size:17px; font-weight:700}
.dm-note{margin:0; padding:10px 14px; font-size:10.5px; color:var(--ink-3);
  line-height:1.5; border-top:1px solid var(--line-3)}
.mgrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:14px; margin-top:16px}
.mcell b{display:block; font-size:21px; font-weight:680; letter-spacing:-0.03em;
  font-variant-numeric:tabular-nums}
.mcell i{display:block; font-style:normal; font-size:11.5px; font-weight:600;
  margin-top:3px}
.mcell span{display:block; font-size:10.5px; color:var(--ink-3); margin-top:2px;
  line-height:1.4}

/* A row the badge was counting when you opened the tab. Fades out of meaning on
   the next visit, which is exactly how long it should matter. */
.soldtbl tr.fresh td{background:color-mix(in srgb,var(--warn) 7%,transparent)}
.soldtbl .newdot{display:inline-block; margin-left:7px; font-style:normal;
  font-size:9px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--warn-2); border:1px solid color-mix(in srgb,var(--warn) 45%,transparent);
  border-radius:5px; padding:1px 5px; vertical-align:1px}

/* --- Done: nine hundred finished deals, as a history rather than a board --- */
/* Every column, explicitly. This table borrows table.ut for its chrome, and
   table.ut sets nth-child widths for the Units grid — so Truck inherited the
   80px meant for a Days column and came out as "2017 INTERNA…". Any column left
   unnamed here picks up somebody else's width. */
table.dealtbl{min-width:900px}
table.dealtbl th:nth-child(1){width:92px}
table.dealtbl th:nth-child(2){width:64px}
table.dealtbl th:nth-child(3){width:auto}
table.dealtbl th:nth-child(4){width:190px}
table.dealtbl th:nth-child(5){width:140px}
table.dealtbl th:nth-child(6){width:106px}
table.dealtbl th:nth-child(7){width:106px}
table.dealtbl td.when{font-variant-numeric:tabular-nums; color:var(--ink-2);
  font-size:12.5px; white-space:nowrap}
table.dealtbl td.veh,table.dealtbl td.who{overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; max-width:0}
table.dealtbl td.veh{color:var(--ink-2)}
table.dealtbl td.who{font-weight:600}
table.dealtbl td.dim{color:var(--ink-3); font-weight:500}
table.dealtbl td.neg{color:var(--neg)}
/* A dead deal is history too, and it belongs in the list — but it is not a sale
   and must not read like one. */
table.dealtbl tr.dead td{opacity:.5}

/* ============================================================ intake
   Trucks on their way in, and the look-over they get the day they land.
   ------------------------------------------------------------------------- */
.insec{display:flex; align-items:baseline; gap:12px; flex-wrap:wrap;
  margin:22px 2px 10px}
.insec:first-child{margin-top:2px}
.insec b{font-size:13px; font-weight:660; letter-spacing:-.01em}
.insec span{font-size:11.5px; color:var(--ink-3)}
.insec.hot b{color:var(--warn-2)}

.inrow{grid-template-columns:56px minmax(0,1fr) 150px auto}
/* A truck standing unlooked-at for three days gets an edge. Not red — nothing is
   broken, somebody just has not got to it. */
.inrow.late{border-left:3px solid color-mix(in srgb,var(--warn) 72%,transparent)}
.inrow .who .note{color:var(--ink-3); font-style:italic; margin-top:2px}

.progwrap{display:flex; align-items:center; gap:9px; min-width:0}
.progwrap span{font-size:11px; color:var(--ink-3); white-space:nowrap;
  font-variant-numeric:tabular-nums}
.prog{flex:1; height:5px; border-radius:3px; background:var(--line-3); overflow:hidden;
  min-width:52px}
.prog i{display:block; height:100%; border-radius:3px; background:var(--good);
  transition:width .25s cubic-bezier(.2,.7,.3,1)}

/* --- the check list --- */
.chk{display:grid; grid-template-columns:24px minmax(0,1fr) auto; gap:12px;
  align-items:start; padding:11px 12px; border:1px solid var(--line);
  border-radius:11px; margin-bottom:7px; cursor:pointer; background:var(--panel);
  transition:border-color .13s, background .13s}
.chk:hover{border-color:var(--axis)}
.chk .tickbox{width:20px; height:20px; border-radius:6px; border:1.5px solid var(--line);
  display:flex; align-items:center; justify-content:center; font-size:12px;
  font-weight:800; color:transparent; margin-top:1px}
.chk.on .tickbox{background:var(--good); border-color:var(--good); color:#fff}
.chk.on{background:color-mix(in srgb,var(--good) 5%,var(--panel));
  border-color:color-mix(in srgb,var(--good) 30%,var(--line))}
.chk .lab b{display:block; font-size:13px; font-weight:620}
.chk .lab span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px;
  line-height:1.45}
/* Not clickable on purpose: these four are read off the truck's own record, and
   a box you can tick while the record disagrees is a box nobody believes. */
.chk.auto{cursor:default; background:var(--panel-2)}
.chk.auto:hover{border-color:var(--line)}
.chk .auto-tag{font-size:9.5px; font-weight:650; letter-spacing:.05em;
  text-transform:uppercase; color:var(--ink-3); border:1px solid var(--line);
  border-radius:5px; padding:2px 6px; white-space:nowrap; margin-top:2px}

/* Same red as Sold when there is work standing on the lot; plain grey when the
   only thing in the tab is a truck still on the road. */
.seg button .pip.soon{background:var(--mute); color:var(--ink-2)}

@media (max-width:760px){
  .inrow{grid-template-columns:52px minmax(0,1fr)}
  .inrow .progwrap,.inrow .acts,.inrow .tags{grid-column:2}
}

/* ============================================================ publish
   Getting one truck out of here and into DealerCenter, which has no bulk import
   on this plan — so the job is not automation, it is removing the retyping and
   the looking back and forth. See publish.py.
   ------------------------------------------------------------------------- */
.pubbar{display:flex; align-items:center; gap:12px; margin-bottom:8px; flex-wrap:wrap}
.pubbar .sp{flex:1}
#pubWhy{margin:0 2px 14px; font-size:12px; color:var(--ink-3); max-width:78ch}

.pubrow{display:grid; grid-template-columns:56px minmax(0,1fr) auto auto; gap:14px;
  align-items:center; background:var(--panel); border:1px solid var(--line);
  border-radius:12px; padding:12px 15px; margin-bottom:8px; box-shadow:var(--lift);
  transition:border-color .13s, opacity .2s}
.pubrow:hover{border-color:var(--axis)}
.pubrow .st{font-variant-numeric:tabular-nums; font-weight:650; font-size:13px;
  color:var(--ink-2)}
.pubrow .who{min-width:0}
.pubrow .who b{display:block; font-size:13.5px; font-weight:620; letter-spacing:-.008em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pubrow .who span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:3px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pubrow .tags{flex-wrap:nowrap}
.pubrow .acts{display:flex; gap:7px; align-items:center}
/* Ticked rows stay put rather than vanishing — pulling a row out from under the
   pointer hands the next click to whatever row jumps up into the gap. */
.pubrow.ticked{opacity:.45}

/* --- the copy-down list: DealerCenter's form, one row per box --- */
.cpgroup{font-size:10px; font-weight:660; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); margin:20px 0 8px; padding-bottom:7px;
  border-bottom:1px solid var(--line-2)}
.cpgroup:first-child{margin-top:4px}
/* Explicit placement for all four children. The note is optional and sits after
   the value in the DOM but before the button, so leaving it to auto-flow put it
   in a row of its own and doubled the height of every field on the list. */
.cprow{display:grid; grid-template-columns:minmax(92px,124px) minmax(0,1fr) 68px;
  gap:2px 12px; align-items:baseline; padding:7px 10px; border-radius:9px;
  transition:background .12s, opacity .16s}
.cprow:hover{background:var(--line-3)}
.cprow .k{grid-column:1; grid-row:1; font-size:11.5px; color:var(--ink-3);
  display:flex; align-items:center; gap:6px; flex-wrap:wrap}
.cprow .v{grid-column:2; grid-row:1; font-size:13px; font-weight:600; min-width:0;
  overflow-wrap:anywhere; font-variant-numeric:tabular-nums; line-height:1.4;
  display:flex; align-items:baseline; gap:8px; flex-wrap:wrap}
.cprow .n{grid-column:2; grid-row:2; margin:1px 0 0; font-size:10.5px;
  color:var(--ink-3); line-height:1.4}
/* Always on. This is a list whose entire purpose is being clicked — hiding the
   button until hover made the panel read as a table you cannot do anything with. */
.cprow .cpbtn{grid-column:3; grid-row:1; justify-self:end; opacity:.62;
  transition:opacity .12s; padding:4px 9px; font-size:11.5px}
.cprow:hover .cpbtn,.cprow .cpbtn:focus-visible{opacity:1}
/* Copied rows go quiet. That trail is how you keep your place going down their
   form without counting boxes on two screens. */
.cprow.done{opacity:.4}
.cprow.gone .v{color:var(--ink-3); font-style:italic; font-weight:500}
.cprow.gone .cpbtn{opacity:.35}
.cprow .guess{font-style:normal; font-size:9.5px; font-weight:650; letter-spacing:.04em;
  text-transform:uppercase; color:var(--warn-2); border:1px solid
  color-mix(in srgb,var(--warn) 40%,transparent); border-radius:5px; padding:1px 5px}

@media (max-width:760px){
  .pubrow{grid-template-columns:52px minmax(0,1fr); row-gap:9px}
  .pubrow .tags,.pubrow .acts{grid-column:2}
  .cprow{grid-template-columns:minmax(0,1fr) auto}
  .cprow .k{grid-column:1/-1}
  .cprow .n{grid-column:1/-1}
  .cprow .cpbtn{opacity:1}
}

/* --------------------------------------------------------------------------
   WHO IS SIGNED IN

   The nav rules below run off a `data-can` attribute the server writes into the
   document head before the stylesheet is even applied. Deciding this in
   JavaScript at the bottom of the body meant a salesman watched the Marketing
   link appear and then vanish on every page load, which looks like a bug and
   invites a second click. It is also why the theme is set there — the same
   frame, the same reason.

   Each selector names the link's href, so these rules and the rail's markup
   have to agree. They stopped agreeing once: Marketing's link was `href="/"`
   for historical reasons, and when that was corrected to `/marketing` this rule
   went on matching a link that no longer existed — so a salesman with no
   Marketing permission was shown the tab. The server still refused him, but
   offering somebody a door that will not open is the exact thing writing these
   before the first paint was meant to prevent. navtest.py now checks that every
   link in the rail has a rule here that matches it.
   -------------------------------------------------------------------------- */
html:not([data-can~="inventory"]) .railnav > a[href="/inventory"],
html:not([data-can~="crm"])       .railnav > a[href="/crm"],
html:not([data-can~="deals"])     .railnav > a[href="/deals"],
html:not([data-can~="shipping"])  .railnav > a[href="/shipping"],
html:not([data-can~="auction"])   .railnav > a[href="/auction"],
html:not([data-can~="marketing"]) .railnav > a[href="/marketing"],
html:not([data-can~="admin"])     .railnav > a.adminlink{display:none}

.whoami{margin:auto 0 0; padding:0 2px}
.whobtn{display:flex; align-items:center; gap:9px; width:100%; padding:9px 10px;
  border:1px solid transparent; border-radius:10px; background:none; cursor:pointer;
  text-align:left; font:inherit; color:var(--ink-2); min-width:0}
.whobtn:hover{background:var(--line-3); border-color:var(--line-2); color:var(--ink)}
.whobtn .dot{flex:0 0 auto; width:26px; height:26px; border-radius:8px;
  background:var(--accent); color:#fff; font-size:11px; font-weight:700;
  display:flex; align-items:center; justify-content:center; letter-spacing:-.02em}
.whobtn .nm{font-size:12.5px; font-weight:600; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; min-width:0}
.whobtn .rl{margin-left:auto; flex:0 0 auto; font-size:10px; color:var(--ink-3);
  text-transform:uppercase; letter-spacing:.05em}
html[data-mode="view"] .whobtn .rl{color:var(--warn-2)}

/* A read-only sign-in should say so once, at the top, rather than letting
   somebody fill in a form and find out when they press Save. */
.readonlybar{display:flex; align-items:center; gap:9px; padding:8px 14px;
  background:var(--warn-bg); color:var(--warn-2); border-bottom:1px solid var(--warn-line);
  font-size:12.5px}
.readonlybar b{font-weight:650}

/* --------------------------------------------------------------------------
   THE AUCTION SCREEN

   Two columns on a desk, one on a phone — and the phone case is the one that
   matters, because that is where it gets used. The answer is the largest thing
   on the page on purpose: standing in a yard, the only question is whether the
   number on the block is above or below it.
   -------------------------------------------------------------------------- */
.auclayout{display:grid; grid-template-columns:400px minmax(0,1fr); gap:20px;
  align-items:start}
.aucform{position:sticky; top:14px}
.aucrow, .aurow{display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px}
.aucform .flab{margin-top:15px}
.aucform .chipset + .aurow{margin-top:14px}
.aucform input[type=text]{width:100%; padding:9px 11px; font-size:13.5px;
  border-radius:9px; border:1px solid var(--line); background:var(--panel);
  color:var(--ink)}
.aucform input:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.aucform input.bad{border-color:var(--bad)}

.bignum{padding:22px 24px 20px; border-radius:14px; background:var(--panel);
  border:1px solid var(--line); box-shadow:var(--lift); margin-bottom:14px}
.bignum .lab{display:block; font-size:11px; text-transform:uppercase;
  letter-spacing:.07em; color:var(--ink-3); font-weight:650}
.bignum b{display:block; font-size:46px; line-height:1.05; letter-spacing:-.035em;
  margin:8px 0 6px; font-variant-numeric:tabular-nums}
.bignum b.bad{color:var(--bad-2)}
/* The book cannot price this one. Said at a size that does not pretend to be
   an answer. */
.bignum.thin b{font-size:26px; color:var(--warn-2);
  font-family:inherit; font-weight:650; letter-spacing:-.02em}
.bignum.thin{border-top:3px solid var(--warn)}
.bignum .sub{display:block; font-size:13px; color:var(--ink-2)}

.aucboxes{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px;
  margin:16px 0 0}
.aucbox{position:relative; padding:12px 13px; border:1px solid var(--line-2);
  border-radius:11px; background:var(--panel)}
.aucbox .flab{margin:0 0 7px; font-size:11px; line-height:1.35; min-height:30px}
.aucbox .ring{position:relative}
.aucbox input{width:100%; padding:7px 10px 7px 22px; font-size:15px;
  font-variant-numeric:tabular-nums; border-radius:8px; border:1px solid var(--line);
  background:var(--panel); color:var(--ink); font-weight:640}
.aucbox input:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.aucbox .unit{position:absolute; left:10px; top:50%; transform:translateY(-50%);
  font-size:12px; color:var(--ink-3); pointer-events:none}
.aucbox .cap{margin:7px 0 0; font-size:10.5px; line-height:1.4}
.workline{margin:12px 0 0; font-size:11.5px; font-style:italic}

.buyrow{display:flex; align-items:center; gap:10px; margin-top:2px}
.buyrow .dol{font-size:22px; color:var(--ink-3)}
.buyin{width:190px; padding:10px 13px; font-size:23px; font-weight:660;
  letter-spacing:-.02em; font-variant-numeric:tabular-nums; border-radius:10px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.buyin:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.buyrow .over{font-size:12.5px; color:var(--ink-3)}
.buyrow .over.bad{color:var(--bad-2); font-weight:600}

.aucres{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px;
  margin-top:14px}
.aucstat{padding:13px 14px; border:1px solid var(--line-2); border-radius:11px;
  background:var(--panel)}
.aucstat .k{display:block; font-size:10.5px; text-transform:uppercase;
  letter-spacing:.06em; color:var(--ink-3); font-weight:650; margin-bottom:6px}
.aucstat b{font-size:21px; letter-spacing:-.02em; font-variant-numeric:tabular-nums}
.aucstat b.good{color:var(--pos)}
.aucstat b.bad{color:var(--neg)}
.notein{flex:1; min-width:180px; padding:9px 11px; font-size:13px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}

.comptbl{table-layout:fixed; width:100%}
.comptbl th:nth-child(1),.comptbl td:nth-child(1){width:56px}
.comptbl th:nth-child(2),.comptbl td:nth-child(2){width:auto; min-width:150px}
.comptbl th:nth-child(3),.comptbl td:nth-child(3){width:62px}
.comptbl th:nth-child(4),.comptbl td:nth-child(4){width:82px}
.comptbl th:nth-child(5),.comptbl td:nth-child(5){width:82px}
.comptbl th:nth-child(6),.comptbl td:nth-child(6){width:78px}
.comptbl th:nth-child(7),.comptbl td:nth-child(7){width:86px}
.comptbl th:nth-child(8),.comptbl td:nth-child(8){width:56px}
.comptbl th:nth-child(9),.comptbl td:nth-child(9){width:58px}
.comptbl td{overflow:hidden}
/* Nine columns is more than a narrow window holds. Letting the tile scroll
   sideways beats hiding the margin, which is the column being read. */
.tile:has(> .comptbl), .tile:has(> .logtbl){overflow-x:auto}
.comptbl{min-width:760px}
.logtbl{min-width:700px}
.comptbl td i{display:block; font-style:normal; font-size:11px; color:var(--ink-3);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.comptbl .b{font-weight:650}

.logtbl{table-layout:fixed; width:100%}
.logtbl th:nth-child(1),.logtbl td:nth-child(1){width:96px}
.logtbl th:nth-child(2),.logtbl td:nth-child(2){width:auto; min-width:200px}
.logtbl th:nth-child(3),.logtbl td:nth-child(3){width:104px}
.logtbl th:nth-child(4),.logtbl td:nth-child(4){width:104px}
.logtbl th:nth-child(5),.logtbl td:nth-child(5){width:190px}
.logtbl th:nth-child(6),.logtbl td:nth-child(6){width:120px}
.logtbl td{overflow:hidden}
.logtbl td i{display:block; font-style:normal; font-size:11px; color:var(--ink-3);
  margin-top:2px}
.logtbl td i.good{color:var(--pos)}
.logtbl td i.bad{color:var(--neg)}

/* --------------------------------------------------------------------------
   PRICE ACTION

   One y axis, always. Two series, so a key is always present and both are named
   in words as well as coloured. Grid and axes recede; the marks and the ceiling
   line are the only things with weight. Colours are the app's own slots 1 and 2,
   which validate as a pair in both modes.
   -------------------------------------------------------------------------- */
.chartwrap{margin-top:18px; border:1px solid var(--line-2); border-radius:12px;
  padding:14px 15px 12px; background:var(--panel)}
.charthead{display:flex; align-items:center; gap:12px; margin-bottom:10px}
.charthead b{font-size:12.5px; letter-spacing:-.01em}
.seg.small{margin-left:auto; display:flex; gap:2px; padding:2px;
  background:var(--line-3); border-radius:8px}
.seg.small button{border:0; background:none; padding:4px 10px; border-radius:6px;
  font:inherit; font-size:11.5px; color:var(--ink-3); cursor:pointer}
.seg.small button:hover{color:var(--ink)}
.seg.small button.on{background:var(--panel); color:var(--ink); font-weight:600;
  box-shadow:var(--lift)}
.chartbox{position:relative}
.pchart{width:100%; height:auto; display:block; overflow:visible}
.pchart .grid{stroke:var(--grid); stroke-width:.12}
.pchart .ylab{font-size:1.5px; fill:var(--ink-3); text-anchor:end}
.pchart .xlab{font-size:1.6px; fill:var(--ink-3); text-anchor:middle}
.pchart .wick{stroke-width:.18}
.pchart .wick.sold{stroke:var(--s1)}
.pchart .wick.paid{stroke:var(--s2)}
.pchart .box.sold{fill:var(--s1); opacity:.30; stroke:var(--s1); stroke-width:.14}
.pchart .box.paid{fill:var(--s2); opacity:.30; stroke:var(--s2); stroke-width:.14}
.pchart .mid{stroke-width:.42; stroke-linecap:round}
.pchart .mid.sold{stroke:var(--s1)}
.pchart .mid.paid{stroke:var(--s2)}
.pchart .ceil{stroke:var(--ink); stroke-width:.16; stroke-dasharray:.9 .7}
.pchart .ceillab{font-size:1.6px; fill:var(--ink-2); font-weight:600}
.pchart .hit{fill:transparent}
.pchart .hit:hover{fill:var(--ink); opacity:.045}
.chartnote{margin:8px 0 0; font-size:11.5px}
.chartkey{display:flex; gap:14px; flex-wrap:wrap; margin-top:9px; font-size:11px;
  color:var(--ink-3)}
.chartkey span{display:flex; align-items:center; gap:6px}
.chartkey .sw{width:11px; height:8px; border-radius:2px; display:inline-block}
.chartkey .sw.sold{background:var(--s1)}
.chartkey .sw.paid{background:var(--s2)}
.chartkey .sw.line{height:0; border-top:2px dashed var(--ink-2); border-radius:0}
.ptip{position:absolute; top:0; transform:translate(-50%,-108%); z-index:5;
  background:var(--ink); color:var(--panel); font-size:11px; line-height:1.5;
  padding:7px 10px; border-radius:8px; white-space:nowrap; pointer-events:none;
  box-shadow:var(--lift-2)}
.ptip b{font-weight:650}

/* --------------------------------------------------------------------------
   THE LISTING READER
   -------------------------------------------------------------------------- */
.drop{margin-bottom:14px}
/* The drop zone is the second-class path now: pasting the page works for
   everybody, dropping a picture needs a key nobody has yet. It stays available
   and stops shouting. */
.dropzone{border:1px dashed var(--line); border-radius:11px; padding:12px 14px;
  text-align:center; margin-top:14px; transition:border-color .15s, background .15s}
.dropzone.over{border-color:var(--accent); background:var(--accent-soft)}
.dropzone.nokey{opacity:.6}
.dropzone .readwith{font-size:10.5px; color:var(--ink-3); margin:6px 0 0;
  line-height:1.4; opacity:.85}
.dropzone b{display:block; font-size:11.5px; margin-bottom:2px; color:var(--ink-2)}
.dropzone span{display:block; font-size:11px; color:var(--ink-3); margin-bottom:9px;
  line-height:1.45}
#scanText{width:100%; margin-top:11px; padding:9px 11px; font:inherit;
  font-size:12.5px; line-height:1.5; border-radius:9px; border:1px solid var(--line);
  background:var(--panel); color:var(--ink); resize:vertical}
#scanText:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
#scanGot{margin:9px 0 0; font-size:11.5px; line-height:1.5}

/* --------------------------------------------------------------------------
   ADMIN — PEOPLE
   -------------------------------------------------------------------------- */
/* .ut sets nth-child widths for the Units grid, so every column here has to be
   named or it quietly inherits a layout meant for stock numbers and prices. */
.peopletbl{table-layout:fixed; width:100%}
.peopletbl th:nth-child(1),.peopletbl td:nth-child(1){width:48px}
.peopletbl th:nth-child(2),.peopletbl td:nth-child(2){width:auto; min-width:170px}
.peopletbl th:nth-child(3),.peopletbl td:nth-child(3){width:118px}
.peopletbl th:nth-child(4),.peopletbl td:nth-child(4){width:230px}
.peopletbl th:nth-child(5),.peopletbl td:nth-child(5){width:110px}
.peopletbl th:nth-child(6),.peopletbl td:nth-child(6){width:112px}
.peopletbl th:nth-child(7),.peopletbl td:nth-child(7){width:96px}
.peopletbl td{overflow:hidden}
.peopletbl tr.off{opacity:.55}
.peopletbl .reach{font-size:12px; color:var(--ink-2)}
.avatar{display:inline-flex; width:26px; height:26px; border-radius:8px;
  background:var(--line-3); color:var(--ink-2); font-size:10.5px; font-weight:700;
  align-items:center; justify-content:center; letter-spacing:-.02em}
.yourow{margin-left:7px; font-style:normal; font-size:10px; font-weight:600;
  color:var(--ink-3); text-transform:uppercase; letter-spacing:.05em}
.peopletbl code{font-size:12px; color:var(--ink-2); background:var(--line-3);
  padding:2px 6px; border-radius:6px}
.mailon{display:block; font-style:normal; font-size:10.5px; color:var(--ink-3);
  margin-top:3px; overflow:hidden; text-overflow:ellipsis}
.peopletbl th:nth-child(3),.peopletbl td:nth-child(3){width:190px}
#pBody input[type=password]{width:100%; padding:9px 11px; font-size:13.5px;
  border-radius:9px; border:1px solid var(--line); background:var(--panel);
  color:var(--ink)}
#vMail input{width:100%; padding:9px 11px; font-size:13.5px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
#vMail .btnrow .atwrap{flex:1; min-width:180px}
#vMail .btnrow input{width:100%}

.chipset{display:flex; flex-wrap:wrap; gap:7px; margin:2px 0 0}
.chip{padding:6px 12px; border-radius:999px; border:1px solid var(--line);
  background:var(--panel); color:var(--ink-2); font-size:12px; cursor:pointer}
.chip:hover{border-color:var(--ink-3); color:var(--ink)}
.chip.on{background:var(--ink); border-color:var(--ink); color:var(--panel);
  font-weight:600}

.pagelist{display:flex; flex-direction:column; gap:6px; margin-top:2px}
.pagerow{display:flex; gap:11px; align-items:flex-start; padding:10px 12px;
  border:1px solid var(--line-2); border-radius:10px; cursor:pointer}
.pagerow:hover{border-color:var(--line)}
.pagerow.on{border-color:var(--accent); background:var(--accent-soft)}
.pagerow input{margin:2px 0 0; flex:0 0 auto; accent-color:var(--accent)}
.pagerow b{display:block; font-size:12.5px; font-weight:620}
.pagerow span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px;
  line-height:1.45}
.flab{display:block; font-size:11.5px; font-weight:600; color:var(--ink-2);
  margin:0 0 6px}
#pBody input[type=text]{width:100%; padding:9px 11px; font-size:13.5px;
  border-radius:9px; border:1px solid var(--line); background:var(--panel);
  color:var(--ink)}
#pBody input:disabled{opacity:.6; cursor:not-allowed}
.danger{color:var(--bad-2)}
.danger:hover{border-color:var(--bad); color:var(--bad)}

/* --------------------------------------------------------------------------
   THE SIGN-IN SCREEN
   -------------------------------------------------------------------------- */
body.gate{display:flex; align-items:center; justify-content:center; min-height:100vh;
  background:var(--bg); padding:24px}
.gatecard{width:100%; max-width:380px; background:var(--panel); border:1px solid var(--line);
  border-radius:16px; padding:28px 28px 22px; box-shadow:0 18px 50px rgba(10,12,16,.10)}
.gatemark{display:flex; align-items:center; gap:12px; margin-bottom:24px}
.gatemark .glyph{width:38px; height:38px; border-radius:11px; background:var(--accent);
  color:#fff; font-size:12px; font-weight:750; display:flex; align-items:center;
  justify-content:center; letter-spacing:-.03em}
.gatemark b{display:block; font-size:15px; letter-spacing:-.02em}
.gatemark span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:1px}
.gatecard label{display:block; font-size:11.5px; font-weight:600; color:var(--ink-2);
  margin:0 0 6px}
.gatecard input{width:100%; padding:10px 12px; font-size:14px; border-radius:10px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.gatecard input:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.gatecard input.bad{border-color:var(--bad)}
.gatecard input + label,
.gatecard .pwwrap + label,
.gatecard .fieldbad + label{margin-top:15px}
.pwwrap{position:relative}
.pwwrap input{padding-right:62px}
.peek{position:absolute; right:5px; top:5px; bottom:5px; padding:0 10px; border:0;
  border-radius:7px; background:none; color:var(--ink-3); font-size:11.5px;
  font-weight:600; cursor:pointer}
.peek:hover{background:var(--line-3); color:var(--ink)}
.fieldbad{margin:6px 0 0; font-size:11.5px; color:var(--bad-2)}
.gatego{width:100%; margin-top:20px; padding:11px; border:0; border-radius:10px;
  background:var(--ink); color:var(--panel); font-size:13.5px; font-weight:640;
  cursor:pointer}
.gatego:hover{opacity:.9}
.gatego:disabled{opacity:.55; cursor:default}
.gatemsg{margin:12px 0 0; font-size:12.5px; min-height:16px; color:var(--ink-3)}
.gatemsg.bad{color:var(--bad-2)}
.gatemsg.good{color:var(--good)}
.gatefoot{margin:20px 0 0; padding-top:16px; border-top:1px solid var(--line-2);
  font-size:11.5px; color:var(--ink-3); line-height:1.5}

/* The second half of a sign-in: the six digits. Big, spaced and centred,
   because it is typed once, in a hurry, often one-handed on a phone. */
.codewho{margin:0 0 16px; font-size:12px; color:var(--ink-3)}
.codewho b{color:var(--ink-2)}
.codebox{text-align:center; letter-spacing:.34em; font-size:20px;
  font-variant-numeric:tabular-nums; padding-left:8px}
.codebox::placeholder{letter-spacing:.28em; opacity:.35}
.gatetick{display:flex; align-items:center; gap:9px; margin:14px 0 0;
  font-size:11.5px; color:var(--ink-2); font-weight:500; cursor:pointer}
.gatetick input{width:15px; height:15px; margin:0; accent-color:var(--accent);
  flex:0 0 auto}
#codef .gatefoot{display:flex; justify-content:space-between; gap:12px;
  flex-wrap:wrap}

/* A small centred box for the one-off asks that do not belong in a page's own
   drawers — changing your own password, mostly. */
.sheet{position:fixed; inset:0; z-index:200; background:rgba(12,13,15,.42);
  display:flex; align-items:center; justify-content:center; padding:24px}
.sheetbox{width:100%; max-width:340px; background:var(--panel); border-radius:14px;
  border:1px solid var(--line); padding:22px; box-shadow:var(--lift-2)}
.sheetbox h2{margin:0 0 16px; font-size:15px; letter-spacing:-.02em}
.sheetbox label{display:block; font-size:11.5px; font-weight:600; color:var(--ink-2);
  margin:13px 0 5px}
.sheetbox label:first-of-type{margin-top:0}
.sheetbox input{width:100%; padding:9px 11px; font-size:13.5px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.sheetbox input:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.sheetrow{display:flex; gap:9px; justify-content:flex-end; margin-top:18px}

/* The "this is not one of your pages" screen. Deliberately calm — being told
   no should not look like something broke. */
.nope{max-width:440px; margin:14vh auto; text-align:center; padding:0 24px}
.nope h1{font-size:19px; letter-spacing:-.02em; margin:0 0 10px}
.nope p{color:var(--ink-3); font-size:13.5px; line-height:1.6; margin:0 0 22px}
.nope .row{display:flex; gap:9px; justify-content:center; flex-wrap:wrap}

/* --------------------------------------------------------------------------
   PHONE — last in the file on purpose.

   These rules override module CSS defined hundreds of lines below where the
   shell lives, and a media query carries no extra specificity: `.filterline
   .sortsel` inside `@media` loses to the identical selector further down the
   file. Keeping the narrow overrides at the end is the only thing that makes
   them win without inventing specificity that would be a lie.
   -------------------------------------------------------------------------- */
@media (max-width:600px){
  .cards{grid-template-columns:minmax(0,1fr); gap:10px}
  .work,.grid-cards{grid-template-columns:minmax(0,1fr)}
  .sfind input[type=search]{flex:1 1 100%}
  .filterline{flex-direction:column; gap:9px}
  .filterline .sortsel{margin-left:0; max-width:100%; width:100%}
  .statstrip{grid-template-columns:repeat(auto-fit,minmax(128px,1fr)); gap:8px}
  .stat b{font-size:20px}
  .fbar select{flex:1 1 45%}
  .topbar .seg{order:9; width:100%}
}

@media (max-width:900px){
  /* On a phone the answer comes first and the form is underneath it — you are
     standing at the truck, you already know what it is. */
  .auclayout{grid-template-columns:minmax(0,1fr); gap:14px}
  /* The tab strip takes the whole width and the two side buttons go with it,
     or the topbar wraps into three ragged lines and the first chip ends up
     underneath it. */
  #tabs{order:9; width:100%; display:flex}
  #tabs button{flex:1}
  /* Stays on the first row beside the theme toggle rather than claiming a
     third line of its own. */
  #clearBtn{order:2; margin-left:auto}
  #themeBtn{order:3}
  /* The topbar is sticky, so scrolling the answer to the top of the document
     put it underneath the topbar. This is how far down "the top" really is. */
  .bignum{scroll-margin-top:120px}
  .aucform{position:static; order:2}
  .aucout{order:1}
  .aucboxes{grid-template-columns:1fr 1fr}
  .aucres{grid-template-columns:1fr 1fr}
  .bignum b{font-size:38px}
  .buyin{width:100%}
  .aucbox .flab{min-height:0}
  .aucbox .flab{min-height:0}
}
@media (max-width:600px){
  .aucboxes{grid-template-columns:1fr}
  .comptbl th:nth-child(4),.comptbl td:nth-child(4),
  .comptbl th:nth-child(6),.comptbl td:nth-child(6),
  .comptbl th:nth-child(9),.comptbl td:nth-child(9){display:none}
  .logtbl th:nth-child(1),.logtbl td:nth-child(1),
  .logtbl th:nth-child(6),.logtbl td:nth-child(6){display:none}
}

/* --------------------------------------------------------------------------
   DIRECTION B — SHOWROOM

   Everything above this point is structure. This block is the dressing, kept in
   one place so it can be argued with or replaced without touching a layout
   rule. Three ideas:

   1. Money gets a serif. A dealership's numbers are the product; setting the
      big ones in Iowan Old Style makes them read as a figure being quoted
      rather than as a readout. Only the hero numbers — everywhere else the
      sans is easier to scan in a column.
   2. One accent, and it is brass. Blue stays where it belongs: on charts, as
      series slot 1, where it is doing a job no other colour can do.
   3. Rules instead of boxes. A hairline under a section header groups things
      more quietly than another card border, and the screens were starting to
      be all border.
   -------------------------------------------------------------------------- */
.bignum b,
.stat .v,
.aucstat b{
  font-family:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
  font-weight:600; letter-spacing:-.025em}
.bignum b{font-size:50px}
.aucstat b{font-size:23px}

/* The primary action is the ink black; brass is for the thing that is *on*. */
button.primary,.btn.primary{background:var(--ink); color:var(--panel)}
button.primary:hover,.btn.primary:hover{background:#000}
:root[data-theme="dark"] button.primary:hover{background:#fff}

.chip.on{background:var(--ink); border-color:var(--ink); color:var(--panel)}
.pagerow.on{border-color:var(--brass); background:var(--brass-soft)}
.pagerow input{accent-color:var(--brass)}
.whobtn .dot,.gatemark .glyph,.avatar.on{background:var(--brass); color:#fff}
.rail .mark .glyph{background:var(--rail-ink); color:var(--rail-panel)}
.railnav a.on{background:var(--rail-ink); color:var(--rail-panel)}
.railnav a.on .ico{opacity:1}

/* A section title is a rule with a word on it, not another box. */
.secttl{border-bottom:1px solid var(--line-2); padding-bottom:7px}

/* Long tables get a whisper of banding — reading across nine columns of numbers
   without it is where the eye slips a row. */
.ut tbody tr:nth-child(even) td{background:var(--panel-2)}
.ut thead th{border-bottom:1.5px solid var(--ink)}

/* Every column of money lines up. Proportional digits in a price column are the
   most common reason a table looks amateur. */
td,.stat .v,.aucstat b,.bignum b,.buyin,.aucbox input,
.peopletbl code,.comptbl td,.logtbl td,.dealtbl td{
  font-variant-numeric:tabular-nums}

.gatego{background:var(--ink)}
.tag.low{background:var(--brass-soft); color:#8a5f13}
:root[data-theme="dark"] .tag.low{background:#2a2113; color:#d9a441}

/* --------------------------------------------------------------------------
   ADDRESSES — everybody here is name@mmequipment.org, so the part that never
   changes is printed in the box rather than typed into it. It steps out of the
   way the moment somebody types an @ of their own.
   -------------------------------------------------------------------------- */
.atwrap{position:relative; display:block}
.atwrap input{width:100%}
.atwrap .at{position:absolute; right:11px; top:50%; transform:translateY(-50%);
  font-size:12.5px; color:var(--ink-3); pointer-events:none; user-select:none;
  background:var(--panel); padding-left:4px; max-width:60%; overflow:hidden;
  white-space:nowrap; text-overflow:ellipsis}
.atwrap.full .at{display:none}
.atwrap.full input{padding-right:11px}

/* the numbered Gmail walk-through on the Email tab */
.steps{counter-reset:s; list-style:none; margin:6px 0 20px; padding:0}
.steps li{counter-increment:s; position:relative; padding:0 0 11px 30px;
  font-size:12.5px; line-height:1.55; color:var(--ink-2)}
.steps li::before{content:counter(s); position:absolute; left:0; top:0;
  width:20px; height:20px; border-radius:50%; background:var(--accent-soft);
  color:var(--accent); font-size:11px; font-weight:700;
  display:flex; align-items:center; justify-content:center}
.steps li:last-child{padding-bottom:0}

/* The name, as it will actually appear, while you are still typing it. The
   corner of the screen is the one place a name has to work, so showing it
   there beats describing it. */
.brandprev{display:flex; align-items:center; gap:11px; padding:14px 14px;
  background:var(--rail); border:1px solid var(--line-2); border-radius:12px;
  margin:0 0 16px}
.brandprev .glyph{width:34px; height:34px; border-radius:10px; flex:none;
  display:grid; place-items:center; font-size:12.5px; font-weight:750;
  letter-spacing:-0.04em; color:var(--panel); background:var(--ink)}
.brandprev .txt{min-width:0}
.brandprev .txt b{display:block; font-size:15px; font-weight:680;
  letter-spacing:-0.025em; line-height:1.15}
.brandprev .txt span{display:block; font-size:11px; color:var(--ink-3);
  margin-top:2px}

/* The auction form's outer tile borrows .drop so a screenshot can be dropped
   anywhere on it. It is not a bare dropzone though — it holds a paragraph, a
   textarea and a button — and inheriting centred text and 42px of padding
   broke the paste instructions into a stack with the comma and the full stop
   starting lines of their own. */
.drop.panel{text-align:left; padding:20px; cursor:default}
.drop.panel .secttl{text-align:left}
.drop.panel p.cap{max-width:56ch}
.drop.panel kbd{white-space:nowrap}

/* ------------------------------------------------------------ what is sitting */
.sittbl td.n{font-variant-numeric:tabular-nums; white-space:nowrap}
.sittbl .gapbad{color:var(--bad-2); font-weight:650}
.sittbl .gapok{color:var(--ink-3)}
.sittbl .cannot{color:var(--ink-3)}
/* The reason a truck has no figure is the point of the row, not a footnote —
   but it is also the longest thing in the table, so it is held to a column
   width rather than being allowed to set the whole layout. */
.sittbl .why{font-size:11px; line-height:1.35; max-width:210px; white-space:normal}
/* Used by both the Sitting and Titles tables. Scoped to .sittbl only, it left
   the Titles rows reading "BUCKET TRUCKsold 2026-08-11" — the sub-line inline
   against the end of the description. */
.sittbl i.sitwhere, .tittbl i.sitwhere{display:block; font-size:11px;
  color:var(--ink-3); font-style:normal; margin-top:3px; font-weight:400}
.sittbl td b{font-weight:600}
.sittbl th{white-space:nowrap}
.sittbl th.n{padding-right:14px}

/* --------------------------------------------------------------- jump to it
   ⌘K from anywhere. Deliberately plain: a box, a list, and nothing that has to
   be learned. It sits above the fold rather than centred, because the list
   grows downwards and a box that moves as you type is hard to aim at. */
.jumpwrap{position:fixed; inset:0; z-index:200; background:rgba(12,11,9,.32);
  display:flex; justify-content:center; align-items:flex-start;
  padding:14vh 20px 20px; backdrop-filter:blur(2px);
  animation:jumpin .12s cubic-bezier(.2,.7,.3,1)}
@keyframes jumpin{from{opacity:0} to{opacity:1}}
/* Wider than it was. It used to list tab names, which are two words; it now
   lists trucks, and "2012 FORD F750 ALTEC LR760E70 75' OCESM CHIPPER DUMP" cut
   off at 470px tells you nothing you did not already know. */
.jumpbox{width:100%; max-width:620px; background:var(--panel);
  border:1px solid var(--line); border-radius:15px; box-shadow:var(--lift-2);
  overflow:hidden}
.jumpbox input{width:100%; border:none; border-bottom:1px solid var(--line-2);
  border-radius:0; background:transparent; padding:15px 17px; font-size:15px;
  box-shadow:none; outline:none}
.jumplist{padding:6px; max-height:52vh; overflow-y:auto}
.jumprow{display:flex; align-items:center; gap:10px; padding:8px 11px;
  border-radius:9px; cursor:pointer}
.jumprow.on{background:var(--line-3)}
.jumprow .jmain{flex:1; min-width:0}
.jumprow .jl{display:block; font-size:13.5px; font-weight:550;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* The second line is what makes a list of trucks readable: six rows all
   starting "2012 FORD F750" are told apart by the stock number and the yard,
   not by the description. */
.jumprow .js{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.jumprow .jb{flex:0 0 auto; font-size:10px; font-weight:640; letter-spacing:.06em;
  text-transform:uppercase; color:var(--ink-3); background:var(--line-3);
  padding:3px 7px; border-radius:999px}
.jumprow.on .jb{background:var(--panel)}
/* A heading between rows rather than around them, so the arrow keys only ever
   land on something you can press Enter against. */
.jumphead{padding:9px 11px 4px; font-size:10px; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3)}
.jumphead:first-child{padding-top:4px}
.jumpnone{padding:15px 12px; font-size:12.5px; color:var(--ink-3)}
.jumpnone b{display:block; font-size:13.5px; font-weight:600; color:var(--ink);
  margin-bottom:5px}
.jumpguess{padding:8px 11px 4px; font-size:11.5px; color:var(--warn-2)}
.jumpfoot{display:flex; align-items:center; gap:6px; padding:9px 13px;
  border-top:1px solid var(--line-2); font-size:11px; color:var(--ink-3)}
.jumpfoot .jkey{font-family:inherit; font-size:10.5px; font-weight:640;
  border:1px solid var(--line-2); border-radius:5px; padding:1px 5px;
  color:var(--ink-2); background:var(--line-3)}
.jumpfoot span + .jkey{margin-left:8px}

/* ==========================================================================
   WHAT EACH PERSON CHOSE
   --------------------------------------------------------------------------
   Five settings, each a single attribute on <html>, written there by the
   server before the stylesheet is even applied. Doing it from JavaScript at
   the foot of the body would mean the app was visibly the wrong colour and
   the wrong size for a frame on every page change — the same flash the theme
   switch used to have, five times over.

   The options are a fixed list on purpose. See prefs.py for why.
   ========================================================================== */
:root{
  --scale:1;
  --face:system-ui,-apple-system,"SF Pro Text","Segoe UI",sans-serif;
  --num:inherit;
  --pad:1;
}

/* ---- text size. One number, and everything set in ems of the body follows. */
html[data-text="s"]{--scale:.94}
html[data-text="m"]{--scale:1}
html[data-text="l"]{--scale:1.09}

/* ---- typeface */
html[data-type="rounded"]{
  --face:ui-rounded,"SF Pro Rounded",system-ui,-apple-system,"Segoe UI",sans-serif}
html[data-type="mono"]{
  --num:ui-monospace,SFMono-Regular,Menlo,"Cascadia Mono",monospace}
/* Money and counts only. Setting the whole app in a fixed-width face is a
   novelty that wears off by lunchtime; lining up a column of prices is the
   thing people actually wanted. */
html[data-type="mono"] .cv,
html[data-type="mono"] .stat b,
html[data-type="mono"] td.n,
html[data-type="mono"] .money,
html[data-type="mono"] .sittbl td.n{font-family:var(--num); letter-spacing:-.02em}

/* ---- density. Rows and cards tighten; type does not, because a compact view
   that is also harder to read is not compact, it is just worse. */
html[data-density="compact"]{--pad:.72}
html[data-density="compact"] .tile.pad{padding:calc(18px * var(--pad)) calc(20px * var(--pad))}
html[data-density="compact"] .ut td{padding-top:calc(11px * var(--pad));
  padding-bottom:calc(11px * var(--pad))}
html[data-density="compact"] .content{padding-top:16px}
html[data-density="compact"] .railnav a{padding-top:7px; padding-bottom:7px}
html[data-density="compact"] .chanitem{padding-top:4px; padding-bottom:4px}
html[data-density="compact"] .stat{padding:10px 13px 11px}
html[data-density="compact"] .scard{padding:12px}

/* ---- accent.
   Each accent is now two variables — one for light paper, one for dark — and one
   generic pair of rules picks between them. The old version wrote every accent
   out three times (light, [data-theme=dark], and again inside the
   prefers-colour-scheme query); at four accents that was twelve blocks, and at
   thirteen plus a colour box it would have been thirty-nine nobody could keep in
   step. --accent-soft and --brass-soft are mixed from whatever --accent ends up
   being, so a typed colour gets its own soft tint for free rather than falling
   back to brass. */
html[data-accent]{--accent:var(--acc-l); --brass:var(--acc-l)}
:root[data-theme="dark"][data-accent]{--accent:var(--acc-d); --brass:var(--acc-d)}
@media (prefers-color-scheme: dark){
  :root:where(:not([data-theme="light"]))[data-accent]{
    --accent:var(--acc-d); --brass:var(--acc-d)}
}
html[data-accent]{
  --accent-soft:color-mix(in srgb, var(--accent) 17%, transparent);
  --brass-soft:color-mix(in srgb, var(--accent) 13%, var(--panel))}

html[data-accent="brass"]  {--acc-l:#b4802a; --acc-d:#d9a441}
html[data-accent="blue"]   {--acc-l:#2a6fd6; --acc-d:#5c9bef}
html[data-accent="green"]  {--acc-l:#1c7a4f; --acc-d:#3fb681}
html[data-accent="slate"]  {--acc-l:#4a5568; --acc-d:#9aa5b8}
html[data-accent="copper"] {--acc-l:#b35a2a; --acc-d:#e08a54}
html[data-accent="teal"]   {--acc-l:#0f7480; --acc-d:#3fb8c4}
html[data-accent="indigo"] {--acc-l:#4a4bc4; --acc-d:#8f90ef}
html[data-accent="plum"]   {--acc-l:#8a3a7a; --acc-d:#c97ab8}
html[data-accent="crimson"]{--acc-l:#b32d3c; --acc-d:#e8707c}
html[data-accent="olive"]  {--acc-l:#5f7028; --acc-d:#a3b855}
html[data-accent="rust"]   {--acc-l:#a04a1e; --acc-d:#d97f4a}
html[data-accent="ink"]    {--acc-l:#1f2430; --acc-d:#e6e9f0}
/* The typed one. Both values are written onto the document by the server, which
   is where the contrast maths lives — the browser never decides whether a colour
   is readable, it only paints what came back. The fallbacks are brass, so a
   half-set custom accent is a colour rather than nothing. */
html[data-accent="custom"]{
  --acc-l:var(--accent-user-light, #b4802a);
  --acc-d:var(--accent-user-dark, #d9a441)}

/* ---- a face instead of initials, wherever initials appear */
.avatar.pic,.whobtn .dot.pic,.mepic{background-size:cover; background-position:center;
  color:transparent; overflow:hidden}

/* --------------------------------------------------------------- your setup */
.facerow{display:flex; gap:20px; align-items:center}
.mepic{width:92px; height:92px; border-radius:22px; flex:none; display:flex;
  align-items:center; justify-content:center; background:var(--accent);
  color:#fff; font-size:29px; font-weight:700; letter-spacing:-.03em}
.faceside{min-width:0}
.faceside b{display:block; font-size:16px; letter-spacing:-.02em}
.faceside .cap{display:block; margin:5px 0 0; max-width:44ch}
.chip .swatch{width:12px; height:12px; border-radius:50%; display:inline-block;
  margin-right:7px; vertical-align:-1px; box-shadow:inset 0 0 0 1px rgba(0,0,0,.12)}
/* ------------------------------------------------------------ saved searches */
.viewbar{display:flex; flex-wrap:wrap; gap:6px; margin:0 0 12px; align-items:center}
.viewbar .vchip{display:inline-flex; align-items:center; gap:7px; padding:6px 12px;
  font-size:12.5px; font-weight:550; border-radius:999px; cursor:pointer;
  background:var(--panel); border:1px solid var(--line); color:var(--ink-2);
  box-shadow:var(--lift)}
.viewbar .vchip:hover{border-color:var(--axis); color:var(--ink)}
.viewbar .vchip.on{background:var(--ink); border-color:var(--ink);
  color:var(--panel); font-weight:620}
.viewbar .vchip .shd{font-size:10px; opacity:.65}
/* The live chip is dark, so the × on it has to be light or it disappears into
   the background it is sitting on. */
.viewbar .vchip.on .vx{color:color-mix(in srgb, var(--panel) 70%, transparent)}
.viewbar .vchip.on .vx:hover{color:var(--panel);
  background:color-mix(in srgb, var(--panel) 22%, transparent)}
.viewbar .vsave{border-style:dashed; box-shadow:none; background:transparent}
.viewbar .vsave:hover{border-color:var(--accent); color:var(--accent)}

/* The quick-tabs bar lived here. It is gone — schema 14 drops the pins with it,
   and the hub at "/" is what replaced it. */

/* --------------------------------------------------------------- a truck's file */
.filesheet{align-items:flex-start; padding:6vh 20px 20px}
.filebox{width:100%; max-width:720px; max-height:86vh; background:var(--panel);
  border:1px solid var(--line); border-radius:16px; box-shadow:var(--lift-2);
  display:flex; flex-direction:column; overflow:hidden}
.filehead{position:relative; padding:20px 22px 15px; border-bottom:1px solid var(--line-2)}
.filehead h2{margin:0; font-size:16px; letter-spacing:-.02em}
.filehead .cap{margin:4px 0 0; max-width:52ch}
.filehead .x{position:absolute; top:14px; right:14px; background:none; border:none;
  box-shadow:none; font-size:15px; color:var(--ink-3); cursor:pointer; padding:5px 8px}
.filehead .x:hover{color:var(--ink)}
.filebody{padding:16px 22px 22px; overflow-y:auto}
.fileday{font-size:10px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); margin:16px 0 7px; padding-top:10px;
  border-top:1px solid var(--line-2)}
.fileday:first-of-type{border-top:none; padding-top:0; margin-top:0}
.fileline{display:flex; gap:11px; align-items:flex-start; padding:7px 8px;
  border-radius:9px}
.fileline:hover{background:var(--line-3)}
.fileline .fi{flex:0 0 18px; text-align:center; color:var(--ink-3); font-size:12px;
  line-height:1.5}
.fileline .fm{flex:1; min-width:0}
.fileline .fm b{display:block; font-size:13px; font-weight:550; line-height:1.45}
.fileline .was{display:block; font-size:11px; color:var(--ink-3); margin-top:1px}
.fileline .fr{flex:0 0 auto; text-align:right; padding-top:1px}
.fileline .fr .ft{display:block; font-size:11px; color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.fileline .fr .fb{display:block; font-size:10.5px; color:var(--ink-3); margin-top:1px}
.filebtn{border-style:dashed}
@media (max-width:620px){ .filesheet{padding:0} .filebox{max-height:100vh; border-radius:0} }
.filefind{display:flex; gap:9px; align-items:center; flex-wrap:wrap; margin:0 0 16px}
.filefind input{width:150px; padding:7px 11px; font-size:13px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
.unitlink{background:none; border:none; box-shadow:none; padding:0; font:inherit;
  color:var(--ink); cursor:pointer; text-decoration:underline;
  text-decoration-color:var(--line); text-underline-offset:3px}
.unitlink:hover{text-decoration-color:var(--accent); color:var(--accent)}

/* --------------------------------------------------------------------- titles */
.soldnotitle{border-color:var(--bad); box-shadow:0 0 0 1px var(--bad-2, var(--bad)) inset,
  var(--lift)}
.seller{margin-bottom:10px; overflow:hidden}
.sellerhead{display:flex; align-items:center; gap:12px; padding:12px 15px;
  cursor:pointer}
.sellerhead:hover{background:var(--line-3)}
.sellerhead .caret{flex:0 0 auto; width:26px; padding:2px 0; text-align:center}
.sellerhead .sw{flex:1; min-width:0}
.sellerhead .sw b{display:block; font-size:13.5px; font-weight:620;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.sellerhead .sw span{display:block; font-size:11.5px; color:var(--ink-3); margin-top:2px}
.sellerhead .sc{flex:0 0 auto; text-align:right; max-width:34%}
.sellerhead .sc .cd{display:block; font-size:11.5px; color:var(--ink-2)}
.sellerhead .sc .cd.never{color:var(--warn-2)}
.sellerhead .sc em{display:block; font-size:11px; color:var(--ink-3); font-style:normal;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:1px}
/* A seller with nothing overdue is still listed — it is not an error, it is
   just not a phone call today — so it recedes rather than disappearing. */
.quietseller{opacity:.62}
.quietseller:hover{opacity:1}
.tittbl{border-top:1px solid var(--line-2)}
.tittbl td{padding-top:9px; padding-bottom:9px}
.tittbl td.n{white-space:nowrap; font-variant-numeric:tabular-nums}
.tittbl td b{font-weight:600}
.tittbl .n button{margin-left:6px}
/* The second action needs an edge. As a bare ghost button beside a solid black
   one it read as a caption rather than as something you could press. */
.tittbl .n button.ghost{border-color:var(--line); background:var(--panel);
  box-shadow:var(--lift); color:var(--ink-2)}
.tittbl .n button.ghost:hover{border-color:var(--axis); color:var(--ink)}
.tittbl td b{display:block; line-height:1.4}

/* The page tab strip. It is the one thing in the top bar that can be allowed to
   run out of room, so it is the one thing that shrinks and scrolls.
   Without this, nine tabs on a 1280px laptop pushed the ⋯ menu and the
   light/dark button clean off the right-hand edge — Add a truck, Import and
   Photos all live behind that ⋯, so they were simply unreachable. */
.topbar > #tabs{flex:0 1 auto; min-width:0; overflow-x:auto;
  scrollbar-width:none; -ms-overflow-style:none; scroll-behavior:smooth}
.topbar > #tabs::-webkit-scrollbar{display:none}
.topbar > #tabs button{flex:0 0 auto}

/* -------------------------------------------------------------------- pricing */
.pritbl td{padding-top:9px; padding-bottom:9px}
.pritbl td.n{white-space:nowrap; font-variant-numeric:tabular-nums}
.pritbl td b{display:block; font-weight:600; line-height:1.4}
.pritbl .cannot{color:var(--ink-3)}
.pritbl i.prinote{display:block; font-size:11px; color:var(--warn-2); font-style:normal;
  margin-top:3px; max-width:52ch; line-height:1.4}
/* Wide enough for "$120,000" without clipping the last digit — it was 104px
   and "$84,500" came out as "$84,50(". */
.pribox{width:124px; padding:6px 9px; font-size:13px; border-radius:8px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink);
  text-align:right; font-variant-numeric:tabular-nums}
.pribox:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.pritbl .n button{margin-left:7px}

/* ------------------------------------------------------------------ customers */
.custq{width:190px; padding:7px 11px; font-size:13px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink)}
/* Fixed layout, or the browser gives the name column whatever is left after
   six nowrap number columns have taken what they want — which was about 120px,
   one word per line, and the action button clipped off the right edge. */
/* Written `table.custtbl`, not `.custtbl`, on purpose. This table also carries
   `ut` for the shared look, and `table.ut th:nth-child(1){width:58px}` above
   outranks a bare class selector — so the customer name column was being given
   the unit table's stock-number width, 58px asked and 137px granted, while the
   30% written right here was silently losing. Same specificity now, and this
   block comes later, so these win. */
table.custtbl{table-layout:fixed; width:100%; min-width:840px}
table.custtbl th:nth-child(1){width:30%}
table.custtbl th:nth-child(2){width:8%}
table.custtbl th:nth-child(3){width:11%}
table.custtbl th:nth-child(4){width:11%}
table.custtbl th:nth-child(5){width:12%}
table.custtbl th:nth-child(6){width:15%}
table.custtbl th:nth-child(7){width:13%}
/* A customer row does not open anything, so it should not offer a hand. */
table.custtbl tbody tr{cursor:default}
table.custtbl tbody tr:hover{background:transparent}
.custtbl td{padding-top:9px; padding-bottom:9px; overflow:hidden}
.custtbl td.n{white-space:nowrap; font-variant-numeric:tabular-nums}
.custtbl td b{display:block; font-weight:600}
.custtbl i.sitwhere{display:block; font-size:11px; color:var(--ink-3);
  font-style:normal; margin-top:3px}
.custtbl i.quietflag{display:block; font-size:10.5px; color:var(--warn-2);
  font-style:normal; margin-top:2px; font-weight:600}
.custtbl .quiet{font-size:11.5px; color:var(--ink-3)}

/* ==========================================================================
   THE PAPERWORK SCREEN

   Five documents, forty repeated facts. The screen's whole job is to make the
   gaps impossible to miss and everything else quiet — so the only colour on it
   is on the fields that are still blank.
   ========================================================================== */
.pwhead{display:flex; flex-direction:column; gap:4px; padding:12px 14px;
  border-radius:10px; margin-bottom:14px; font-size:12.5px}
.pwhead b{font-size:14px}
.pwhead.good{background:var(--good-3, var(--line-3)); color:var(--ink)}
.pwhead.bad{background:var(--warn-3, var(--line-3)); color:var(--ink)}
.pwhead span{color:var(--ink-2)}
.pwwarn{margin:0 0 10px; padding:9px 12px; border-radius:8px; font-size:12px;
  background:var(--warn-3, var(--line-3)); color:var(--ink-2)}
.pwh{margin:18px 0 8px; font-size:11px; font-weight:700; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3)}
.pwgrid{display:grid; grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));
  gap:11px}
.pwfield{display:flex; flex-direction:column; gap:3px; min-width:0}
.pwfield label{font-size:11.5px; color:var(--ink-2)}
.pwfield input{width:100%}
.pwfield i{font-style:normal; font-size:10.5px; color:var(--ink-3)}
/* The one thing on the screen that shouts. A blank on a signed document is
   discovered at the counter with a customer holding a pen, which is the single
   worst place for this app to be quiet. */
.pwfield.need input{border-color:var(--warn-2); background:var(--warn-3, transparent)}
.pwfield.need i{color:var(--warn-2); font-weight:600}
.pwmoney{border:1px solid var(--line-2); border-radius:10px; padding:4px 12px}
.pwrow{display:flex; justify-content:space-between; align-items:baseline;
  padding:6px 0; font-size:12.5px; border-bottom:1px solid var(--line-3)}
.pwrow:last-child{border-bottom:none}
.pwrow b{font-variant-numeric:tabular-nums}
.pwrow.tot{font-weight:600}
.pwrow.tot b{font-size:13.5px}
.pwpick{display:flex; flex-wrap:wrap; gap:18px}
.pwchoice{display:flex; flex-direction:column; gap:5px}
.pwchoice label{font-size:11.5px; color:var(--ink-2)}
.pwsheets{display:flex; flex-wrap:wrap; gap:8px; margin-top:6px}

/* ==========================================================================
   WANTED

   Somebody rang wanting a truck M&M has not got. The box comes before the list
   on purpose: writing it down has to be faster than not writing it down, or the
   note goes back into the salesman's phone and nobody ever sees it again.
   ========================================================================== */
.wantbox{border:1px solid var(--line-2); border-radius:12px; padding:13px 14px;
  margin:0 0 16px; background:var(--panel)}
.wantbox label{display:block; font-size:11.5px; font-weight:600;
  color:var(--ink-2); margin-bottom:6px}
.wantbox textarea{width:100%; resize:vertical; min-height:52px; font-size:14px;
  line-height:1.45}
.wantbox textarea.drop{border-color:var(--accent); background:var(--line-3)}
/* What the app thinks it heard, live. The whole defence against a parser that
   guesses wrong is that somebody sees this while the customer is still on. */
.wantread{margin:7px 0 0; font-size:12px; color:var(--ink-3); min-height:16px}
.wantread.on{color:var(--ink-2); font-weight:550}
.wantacts{display:flex; align-items:center; gap:8px; margin-top:10px}
.wantcard{border:1px solid var(--line-2); border-radius:12px; padding:13px 15px;
  margin-bottom:11px; background:var(--panel)}
/* The only loud thing on the screen: somebody to ring, about a truck that is
   on the lot, that they have not been told about. */
.wantcard.ring{border-color:var(--warn-2); box-shadow:var(--lift)}
.wtop{display:flex; justify-content:space-between; align-items:flex-start;
  gap:12px}
.wwho{display:flex; align-items:baseline; gap:10px; min-width:0; flex-wrap:wrap}
.wwho b{font-size:14.5px}
.wtel{font-size:13px; font-variant-numeric:tabular-nums; text-decoration:none;
  color:var(--ink-2); border-bottom:1px solid var(--line-2)}
.wwhen{text-align:right; flex:0 0 auto}
.wwhen span{display:block; font-size:11.5px; color:var(--ink-3)}
.wwhen i{display:block; font-style:normal; font-size:10.5px; color:var(--ink-3)}
.wchips{display:flex; flex-wrap:wrap; gap:6px; margin:9px 0 0}
.wchip{font-size:11.5px; font-weight:600; padding:3px 9px; border-radius:999px;
  background:var(--line-3); color:var(--ink-2)}
.wchip.plain{font-weight:450; background:transparent; padding-left:0}
.wsaid{margin:7px 0 0; font-size:11.5px; color:var(--ink-3); font-style:italic}
.wstale{margin:9px 0 0; font-size:11.5px; color:var(--warn-2)}
.wnone{margin:10px 0 0; font-size:12px; color:var(--ink-3)}
.whits{margin-top:11px; border-top:1px solid var(--line-3); padding-top:9px;
  display:flex; flex-direction:column; gap:7px}
.whit{display:flex; justify-content:space-between; align-items:center; gap:12px;
  padding:7px 9px; border-radius:9px; background:var(--line-3)}
.whit.near{background:transparent; border:1px dashed var(--line-2)}
.wl{min-width:0}
.wl b{display:block; font-size:12.5px; font-variant-numeric:tabular-nums}
.wl > span{display:block; font-size:11.5px; color:var(--ink-3); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; max-width:52ch}
.wtags{display:flex; flex-wrap:wrap; gap:5px; margin-top:3px}
.wtags i{font-style:normal; font-size:10.5px; padding:1px 6px; border-radius:5px}
.wtags i.good{background:var(--good-3, var(--line-3)); color:var(--ink-2)}
.wtags i.warn{background:var(--warn-3, var(--line-3)); color:var(--warn-2)}
.wr{display:flex; gap:6px; flex:0 0 auto}
.wacts{display:flex; gap:6px; margin-top:11px}
.wacts .on{background:var(--ink); color:var(--panel)}

/* ==========================================================================
   THE eBAY LISTING PREVIEW

   eBay's description box takes a web page, and M&M have a house template for
   it. Shown rendered rather than as code, because what wants checking is
   whether it reads right — and because a wall of markup gets pasted without
   being read.
   ========================================================================== */
#lPreview{width:100%; height:520px; border:1px solid var(--line-2);
  border-radius:10px; background:#fff; display:block}
#lHtmlSrc{margin-top:10px; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:11px; min-height:180px}
#lHtmlNote{margin:0 0 10px}


/* ==========================================================================
   APPEARANCE, THE LONG VERSION

   Greg: *"i want people to decide how they want the app to look so you can add
   whatever hwoever many options you want just make them good."* So: thirteen
   accents plus a colour box, five papers, five text sizes, three densities,
   five typefaces, three corner radii, three menu positions, two widths, two
   ways of drawing a truck, and a background photo.

   The guarantee that the old short list carried is now kept in prefs.py: a
   typed colour is moved until its contrast clears the floor before it ever
   reaches this file, and a background is dimmed to a level this file does not
   let go below. Nothing here can produce a screen that cannot be read.
   ========================================================================== */

/* ---- text size, the two new ends of it */
html[data-text="xs"]{--scale:.88}
html[data-text="xl"]{--scale:1.2}

/* ---- density, one step tighter again */
html[data-density="tight"]{--pad:.56}
html[data-density="tight"] .tile.pad{padding:calc(18px * var(--pad)) calc(20px * var(--pad))}
html[data-density="tight"] .ut td{padding-top:calc(11px * var(--pad));
  padding-bottom:calc(11px * var(--pad))}
html[data-density="tight"] .content{padding-top:12px; padding-bottom:56px}
html[data-density="tight"] .railnav a{padding-top:5px; padding-bottom:5px}
html[data-density="tight"] .chanitem{padding-top:2px; padding-bottom:2px}
html[data-density="tight"] .stat{padding:7px 11px 8px}
html[data-density="tight"] .scard{padding:9px}

/* ---- two more typefaces. Both are stacks of faces that ship with the machine:
   this app has no web fonts and no build step, so a named font that is not
   installed is a silent fall back to the default rather than a download. */
html[data-type="grotesk"]{
  --face:"Helvetica Neue",Inter,Roboto,"Segoe UI",system-ui,sans-serif}
html[data-type="serif"]{
  --face:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif}

/* ---- corners */
html[data-corners="square"]{--r:2px; --r-sm:2px; --r-xs:2px}
html[data-corners="round"]{--r:20px; --r-sm:15px; --r-xs:11px}
html[data-corners="round"] button,
html[data-corners="round"] .chip,
html[data-corners="round"] .tag{border-radius:999px}
html[data-corners="square"] button,
html[data-corners="square"] .chip,
html[data-corners="square"] .tag,
html[data-corners="square"] .railnav a,
html[data-corners="square"] .railfind{border-radius:2px}

/* ---- how wide. A table stretched across a 34-inch monitor is a table nobody
   can follow a row across; "keep it centred" caps it and puts the slack in the
   margins. */
html[data-width="boxed"] .content{max-width:1180px; margin-left:auto;
  margin-right:auto}
html[data-width="boxed"] .topbar{max-width:1180px; margin-left:auto;
  margin-right:auto; width:100%}

/* ---- movement off. Not a blanket `animation:none` on everything: a spinner
   that stops spinning looks like a hung app. This kills the decorative
   transitions and the slide-ins, and leaves anything that is reporting
   progress. */
html[data-motion="off"] *,
html[data-motion="off"] *::before,
html[data-motion="off"] *::after{
  transition-duration:0s !important; animation-duration:0s !important;
  animation-delay:0s !important; scroll-behavior:auto !important}
html[data-motion="off"] .spin,
html[data-motion="off"] .loading{animation-duration:.9s !important}

/* ---- the menu across the top instead of down the side.
   These rules are generated from the narrow-screen block above rather than
   written twice: that block already turns the rail into a header, it has been
   through the phone and bar-fit tests, and a hand-written second copy would
   drift from it the first time either changed. */
html[data-rail="top"] .shell{grid-template-columns:minmax(0,1fr); background:var(--bg)}
html[data-rail="top"] .rail{position:static; height:auto; min-width:0; flex-direction:row; align-items:center; overflow:visible; gap:10px 12px; padding:9px 12px 7px; background:var(--rail); box-shadow:0 1px 0 var(--line); flex-wrap:wrap}
html[data-rail="top"] .railsec, html[data-rail="top"] .rail .foot{display:none}
html[data-rail="top"] .rail .mark{flex:1 1 auto; min-width:0; padding:0; margin:0; border:none}
html[data-rail="top"] .rail .mark .glyph{width:30px; height:30px; border-radius:9px; font-size:11px}
html[data-rail="top"] .rail .mark .txt b{font-size:14px}
html[data-rail="top"] .whoami{order:2; flex:0 0 auto; margin:0; padding:0; max-width:46%}
html[data-rail="top"] .whobtn{padding:6px 8px}
html[data-rail="top"] .whobtn .rl{display:none}
html[data-rail="top"] .railnav{order:3; flex:1 0 100%; flex-direction:row; min-width:0; gap:3px; overflow-x:auto; scrollbar-width:none; -ms-overflow-style:none; margin:0 -12px; padding:2px 12px}
html[data-rail="top"] .railnav::-webkit-scrollbar{display:none}
html[data-rail="top"] .railnav a{flex:0 0 auto; white-space:nowrap; padding:7px 11px; margin-top:0}
html[data-rail="top"] .railnav a[href="/auction"], html[data-rail="top"] .railnav a.adminlink{margin-top:0; margin-left:7px}
html[data-rail="top"] .topbar{padding:10px 14px; height:auto; min-height:58px; flex-wrap:wrap; row-gap:8px}
html[data-rail="top"] .topbar h1{font-size:16px}
html[data-rail="top"] .topbar .sub{display:none}
html[data-rail="top"] .topbar .seg{max-width:100%; overflow-x:auto; scrollbar-width:none}
html[data-rail="top"] .topbar .seg::-webkit-scrollbar{display:none}
html[data-rail="top"] .content{padding:16px 14px 80px}
/* The two things that only make sense when it is a choice rather than a phone:
   a desktop with the menu on top still wants desktop padding, and the wordmark
   has room for its second line. */
@media (min-width:1081px){
  html[data-rail="top"] .content{padding:22px 34px 90px}
  html[data-rail="top"] .topbar{padding:0 34px}
  html[data-rail="top"] .rail{padding:10px 30px 8px}
  html[data-rail="top"] .railnav{flex:1 1 auto; order:0; margin:0 0 0 18px;
    padding:0}
  html[data-rail="top"] .railfind{order:1; width:auto; min-width:220px;
    max-width:320px; margin:0}
  html[data-rail="top"] .whoami{order:2}
  html[data-rail="top"] .rail .mark{flex:0 0 auto}
}

/* ---- icons only. The label lives in its own span for exactly this — a bare
   text node cannot be hidden by CSS, which is why every rail link in every page
   now wraps its words in .lb. */
@media (min-width:1081px){
  html[data-rail="mini"]{--rail-w:66px}
  html[data-rail="mini"] .rail{padding:18px 10px 22px; align-items:stretch}
  html[data-rail="mini"] .railnav a{justify-content:center; padding:10px 0}
  html[data-rail="mini"] .railnav .lb{display:none}
  html[data-rail="mini"] .railnav .ico{width:auto; font-size:15px}
  html[data-rail="mini"] .mark{justify-content:center; padding:0 0 14px}
  html[data-rail="mini"] .mark .txt{display:none}
  html[data-rail="mini"] .railfind .ftx,
  html[data-rail="mini"] .railfind .fkey{display:none}
  html[data-rail="mini"] .railfind{justify-content:center; padding:9px 0}
  html[data-rail="mini"] .rail .foot,
  html[data-rail="mini"] .railsec{display:none}
  html[data-rail="mini"] .whobtn .nm,
  html[data-rail="mini"] .whobtn .rl{display:none}
  html[data-rail="mini"] .whobtn{justify-content:center; padding:7px 0}
  /* The name on hover, because an icon-only menu that never says what the icons
     are is a puzzle rather than a menu. */
  html[data-rail="mini"] .railnav a{position:relative}
  html[data-rail="mini"] .railnav a:hover::after{
    content:attr(data-name); position:absolute; left:calc(100% + 8px); top:50%;
    transform:translateY(-50%); background:var(--ink); color:var(--bg);
    font-size:11.5px; font-weight:600; padding:4px 8px; border-radius:6px;
    white-space:nowrap; z-index:60; pointer-events:none}
}

/* ---- the paper.
   Only the neutrals move. The accent, the alert colours and the series colours
   are the same on every paper, because those carry meaning and a meaning that
   shifts with somebody's paper choice is not a meaning. */
html[data-paper="cool"]{
  --bg:#f2f4f7; --panel:#ffffff; --panel-2:#fafbfc; --rail:#f7f9fb;
  --line:#dbe1e8; --line-2:#e8edf2; --line-3:#f1f4f8;
  --ink:#15181d; --ink-2:#4e5561; --ink-3:#88909d;
  --grid:#e9edf2; --axis:#d6dce4}
:root[data-theme="dark"][data-paper="cool"]{
  --bg:#080a0d; --panel:#12151a; --panel-2:#161a20; --rail:#0d1014;
  --line:#252b33; --line-2:#1d2229; --line-3:#181c22;
  --ink:#f4f7fa; --ink-2:#a7b0bd; --ink-3:#737c8a;
  --grid:#1e2329; --axis:#2b3138}
html[data-paper="plain"]{
  --bg:#f2f2f2; --panel:#ffffff; --panel-2:#fbfbfb; --rail:#f8f8f8;
  --line:#dcdcdc; --line-2:#e9e9e9; --line-3:#f2f2f2;
  --ink:#101010; --ink-2:#525252; --ink-3:#8c8c8c;
  --grid:#ebebeb; --axis:#d8d8d8}
:root[data-theme="dark"][data-paper="plain"]{
  --bg:#000000; --panel:#121212; --panel-2:#171717; --rail:#0a0a0a;
  --line:#282828; --line-2:#1f1f1f; --line-3:#1a1a1a;
  --ink:#ffffff; --ink-2:#adadad; --ink-3:#7a7a7a;
  --grid:#1e1e1e; --axis:#2c2c2c}
html[data-paper="sepia"]{
  --bg:#f3ecdd; --panel:#fdf8ee; --panel-2:#f9f2e4; --rail:#f6efe0;
  --line:#ddd0b6; --line-2:#e9dfc9; --line-3:#f0e8d6;
  --ink:#231d12; --ink-2:#5e5340; --ink-3:#968a72;
  --grid:#e8dfcb; --axis:#d7ccb2}
:root[data-theme="dark"][data-paper="sepia"]{
  --bg:#100d08; --panel:#1a1610; --panel-2:#1f1a13; --rail:#140f0a;
  --line:#302819; --line-2:#262015; --line-3:#1f1a12;
  --ink:#faf3e4; --ink-2:#b8ab92; --ink-3:#867a63;
  --grid:#241e14; --axis:#33291a}
html[data-paper="contrast"]{
  --bg:#ffffff; --panel:#ffffff; --panel-2:#ffffff; --rail:#f4f4f4;
  --line:#8a8a8a; --line-2:#b4b4b4; --line-3:#dcdcdc;
  --ink:#000000; --ink-2:#333333; --ink-3:#575757;
  --grid:#d0d0d0; --axis:#8a8a8a;
  --lift:none; --lift-2:0 0 0 1px #8a8a8a; --inset:none}
:root[data-theme="dark"][data-paper="contrast"]{
  --bg:#000000; --panel:#0b0b0b; --panel-2:#111111; --rail:#000000;
  --line:#8f8f8f; --line-2:#6a6a6a; --line-3:#3a3a3a;
  --ink:#ffffff; --ink-2:#e0e0e0; --ink-3:#bcbcbc;
  --grid:#333333; --axis:#7a7a7a;
  --lift:none; --lift-2:0 0 0 1px #8f8f8f; --inset:none}
@media (prefers-color-scheme: dark){
  :root:where(:not([data-theme="light"]))[data-paper="cool"]{
    --bg:#080a0d; --panel:#12151a; --panel-2:#161a20; --rail:#0d1014;
    --line:#252b33; --line-2:#1d2229; --line-3:#181c22;
    --ink:#f4f7fa; --ink-2:#a7b0bd; --ink-3:#737c8a;
    --grid:#1e2329; --axis:#2b3138}
  :root:where(:not([data-theme="light"]))[data-paper="plain"]{
    --bg:#000000; --panel:#121212; --panel-2:#171717; --rail:#0a0a0a;
    --line:#282828; --line-2:#1f1f1f; --line-3:#1a1a1a;
    --ink:#ffffff; --ink-2:#adadad; --ink-3:#7a7a7a;
    --grid:#1e1e1e; --axis:#2c2c2c}
  :root:where(:not([data-theme="light"]))[data-paper="sepia"]{
    --bg:#100d08; --panel:#1a1610; --panel-2:#1f1a13; --rail:#140f0a;
    --line:#302819; --line-2:#262015; --line-3:#1f1a12;
    --ink:#faf3e4; --ink-2:#b8ab92; --ink-3:#867a63;
    --grid:#241e14; --axis:#33291a}
  :root:where(:not([data-theme="light"]))[data-paper="contrast"]{
    --bg:#000000; --panel:#0b0b0b; --panel-2:#111111; --rail:#000000;
    --line:#8f8f8f; --line-2:#6a6a6a; --line-3:#3a3a3a;
    --ink:#ffffff; --ink-2:#e0e0e0; --ink-3:#bcbcbc;
    --grid:#333333; --axis:#7a7a7a;
    --lift:none; --lift-2:0 0 0 1px #8f8f8f; --inset:none}
}

/* ---- a picture behind it all.
   Two stacked fixed layers under everything: the photograph, then a veil of the
   paper colour at --bg-dim. The veil is what makes the words on top readable,
   and prefs.py will not store a dim below its floor — so there is no setting
   that ends in an unreadable screen.

   The surfaces above go translucent so the picture is actually visible. The
   cards do not: a price on a photograph is a price nobody can read, and the
   point of the picture is the frame around the work, not the work. */
html[data-bg] body{background:transparent}
html[data-bg] body::before{
  content:""; position:fixed; inset:0; z-index:-2; background-image:var(--bg-shot);
  background-position:center; background-repeat:no-repeat; background-size:cover}
html[data-bg="tile"] body::before{background-repeat:repeat; background-size:auto}
html[data-bg="contain"] body::before{background-size:contain}
html[data-bg] body::after{
  content:""; position:fixed; inset:0; z-index:-1; background:var(--bg);
  opacity:var(--bg-dim, .72)}
html[data-bg] .shell{background:transparent}
html[data-bg] .rail{background:color-mix(in srgb, var(--rail) 76%, transparent);
  backdrop-filter:saturate(140%) blur(16px)}
html[data-bg] .topbar{background:color-mix(in srgb, var(--bg) 70%, transparent)}
html[data-bg] .tile,
html[data-bg] .hubband > .hubhead{background-color:transparent}
html[data-bg] .tile{background:color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter:blur(6px)}

/* ---- trucks as rows instead of cards.
   The Sales tab draws a card per truck with its photograph, which is the right
   default: a salesman recognises a truck by looking at it faster than by reading
   seventy characters of description. Somebody who knows the lot by stock number
   wants twenty on screen instead of six.

   Done entirely here rather than with a second renderer in inventory.js. The
   card already contains every piece of information a row needs, in the right
   order — this lays it along one line and hides the photograph. One markup, one
   set of behaviours, no chance of the two views disagreeing about what a truck
   is called. */
html[data-rows="table"] #salesHost{grid-template-columns:minmax(0,1fr); gap:0;
  border:1px solid var(--line); border-radius:var(--r-sm); overflow:hidden;
  background:var(--panel)}
html[data-rows="table"] #salesHost .scard{display:grid; align-items:center;
  grid-template-columns:minmax(0,2.1fr) minmax(0,1.4fr) auto;
  gap:16px; border:none; border-bottom:1px solid var(--line-3);
  border-radius:0; padding:7px 16px 7px 18px; box-shadow:none; min-height:0;
  line-height:1.35}
html[data-rows="table"] #salesHost .scard:last-child{border-bottom:none}
html[data-rows="table"] #salesHost .scard:hover{transform:none;
  background:var(--line-3)}
html[data-rows="table"] #salesHost .scard::before{top:0; bottom:0}
html[data-rows="table"] #salesHost .shead{display:block; margin:0; min-width:0}
html[data-rows="table"] #salesHost .sthumb{display:none}
/* One line each, not two: the point of this view is how many fit. */
html[data-rows="table"] #salesHost .sname b,
html[data-rows="table"] #salesHost .sname span{-webkit-line-clamp:1;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block}
html[data-rows="table"] #salesHost .sname b{font-size:13px; margin:0;
  line-height:1.35}
html[data-rows="table"] #salesHost .sname span{font-size:11px; margin:0;
  line-height:1.35}
html[data-rows="table"] #salesHost .snote{display:none}
html[data-rows="table"] #salesHost .sfoot{margin:0; gap:12px}
html[data-rows="table"] #salesHost .sfoot .p{font-size:14.5px}
html[data-rows="table"] #salesHost .stags{margin:0; justify-self:end;
  flex-wrap:nowrap; max-width:34ch; overflow:hidden}
html[data-rows="table"] #salesHost .scard > .stags:last-child{margin-top:0}
@media (max-width:900px){
  /* On a phone a three-column row is narrower than any one of its columns
     wants to be, so it goes back to being a card. Nobody is scanning two
     hundred trucks on a phone anyway. */
  html[data-rows="table"] #salesHost .scard{grid-template-columns:minmax(0,1fr)}
  html[data-rows="table"] #salesHost .stags{justify-self:start}
}

/* ==========================================================================
   THE HUB
   The front page. See hub.js for the three rules it is written to.
   ========================================================================== */
.hubband{margin:0 0 30px}
.hubhead{margin:0 0 13px}
.hubhead h2{margin:0; font-size:13px; font-weight:660; letter-spacing:.075em;
  text-transform:uppercase; color:var(--ink-3)}
.hubhead .cap{margin:4px 0 0}

/* the search box */
.hubsearch{margin-bottom:26px}
.hubfind{display:flex; align-items:center; gap:12px; width:100%;
  padding:15px 18px; border:1px solid var(--line); border-radius:var(--r);
  background:var(--panel); cursor:pointer; font:inherit; font-size:14.5px;
  color:var(--ink-3); text-align:left; box-shadow:var(--lift);
  transition:border-color .13s, box-shadow .13s, color .13s}
.hubfind:hover{border-color:var(--accent); color:var(--ink-2);
  box-shadow:var(--lift-2)}
.hubfind .fico{font-size:17px; opacity:.7; flex:none}
.hubfind .ftx{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap}
.hubfind .fkey{flex:none; font-size:11px; font-weight:640; opacity:.65;
  border:1px solid var(--line-2); border-radius:6px; padding:2px 6px}

/* things to do */
.hubdo{display:grid; gap:10px;
  grid-template-columns:repeat(auto-fill, minmax(232px, 1fr))}
.dotile{display:flex; align-items:flex-start; gap:12px; padding:14px 15px;
  border:1px solid var(--line-2); border-radius:var(--r-sm);
  background:var(--panel); text-decoration:none; color:var(--ink);
  transition:border-color .13s, transform .13s, box-shadow .13s}
.dotile:hover{border-color:var(--accent); transform:translateY(-1px);
  box-shadow:var(--lift)}
.dotile .di{flex:none; width:30px; height:30px; border-radius:9px; display:grid;
  place-items:center; font-size:14px; color:var(--accent);
  background:var(--accent-soft)}
.dotile .dt{min-width:0}
.dotile .dt b{display:block; font-size:13.5px; font-weight:620;
  letter-spacing:-.012em}
.dotile .dt span{display:block; margin-top:2px; font-size:11.5px;
  color:var(--ink-3); line-height:1.45}

/* the counts. Every one is an <a>: a number you cannot press is a dead end,
   which is the thing about DealerCenter's front page that most needed fixing. */
.hubnums{display:grid; gap:10px;
  grid-template-columns:repeat(auto-fill, minmax(178px, 1fr))}
.numtile{display:block; padding:14px 16px 15px; border:1px solid var(--line-2);
  border-radius:var(--r-sm); background:var(--panel); text-decoration:none;
  color:var(--ink); transition:border-color .13s, transform .13s,
  box-shadow .13s}
.numtile:hover{border-color:var(--accent); transform:translateY(-1px);
  box-shadow:var(--lift)}
.numtile .nv{display:block; font-size:27px; font-weight:670;
  letter-spacing:-.035em; line-height:1.05; font-variant-numeric:tabular-nums}
.numtile .nl{display:block; margin-top:3px; font-size:12.5px; font-weight:560;
  color:var(--ink-2)}
.numtile .nn{display:block; margin-top:5px; font-size:11px; color:var(--ink-3);
  line-height:1.4}
.numtile.warn .nv{color:var(--warn-2)}
.numtile.bad .nv{color:var(--bad-2)}
.numtile.warn{border-color:color-mix(in srgb, var(--warn) 34%, transparent)}
.numtile.bad{border-color:color-mix(in srgb, var(--bad) 30%, transparent)}

/* today, and back to what you were doing */
.hublist{display:flex; flex-direction:column; gap:1px; border:1px solid
  var(--line-2); border-radius:var(--r-sm); overflow:hidden;
  background:var(--panel)}
.hubrowlink{display:flex; align-items:center; gap:13px; padding:11px 15px;
  text-decoration:none; color:var(--ink); border-bottom:1px solid var(--line-3);
  transition:background .12s}
.hubrowlink:last-child{border-bottom:none}
.hubrowlink:hover{background:var(--line-3)}
.hubrowlink .kind{flex:none; width:44px; font-size:10px; font-weight:680;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3)}
.hubrowlink .kind.call, .hubrowlink .kind.person{color:var(--s1)}
.hubrowlink .kind.title{color:var(--warn-2)}
.hubrowlink .kind.deal{color:var(--pos)}
.hubrowlink .kind.truck{color:var(--accent)}
.hubrowlink .mid{flex:1; min-width:0}
.hubrowlink .mid b{display:block; font-size:13.5px; font-weight:600;
  letter-spacing:-.012em; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis}
.hubrowlink .mid span{display:block; font-size:11.5px; color:var(--ink-3);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.hubrowlink .late, .hubrowlink .when{flex:none; font-size:11.5px;
  color:var(--ink-3); white-space:nowrap}
.hubrowlink .late.bad{color:var(--bad-2); font-weight:600}
.hubclear{padding:16px 18px; border:1px solid var(--line-2);
  border-radius:var(--r-sm); background:var(--panel)}
.hubclear b{display:block; font-size:14px; color:var(--pos)}
.hubclear span{display:block; margin-top:3px; font-size:12px;
  color:var(--ink-3)}

/* A saved search on the hub is a link rather than a button, and a link brings
   an underline with it that the chip was never drawn for. */
.hubband .chipset a.chip{text-decoration:none}
.hubband .chipset a.chip:hover{border-color:var(--accent); color:var(--ink)}

.hubempty{padding:34px 30px; text-align:center; border:1px dashed var(--line);
  border-radius:var(--r); background:var(--panel-2)}
.hubempty b{display:block; font-size:16px}
.hubempty p{margin:6px 0 16px; color:var(--ink-3); font-size:13px}

@media (max-width:900px){
  .hubnums{grid-template-columns:repeat(auto-fill, minmax(140px, 1fr))}
  .numtile .nv{font-size:23px}
  .hubdo{grid-template-columns:1fr}
  .hubrowlink .kind{width:38px}
}

/* --------------------------------------------------- your setup, the new bits */
.pickrow{display:flex; align-items:center; gap:11px; margin-top:14px;
  padding-top:14px; border-top:1px solid var(--line-3); flex-wrap:wrap}
.pickrow label{font-size:12.5px; color:var(--ink-2); font-weight:560}
.pickrow input[type="color"]{width:44px; height:32px; padding:2px; border:1px
  solid var(--line); border-radius:var(--r-xs); background:var(--panel);
  cursor:pointer}
/* .pickrow scoping on purpose: the generic input rule in this sheet is
   width:100%, and an unscoped .hexbox lost to it — the colour box stretched the
   width of the card. */
.pickrow .hexbox{width:104px; flex:0 0 auto;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12.5px; text-transform:lowercase}
.dimmer{width:100%; max-width:420px; accent-color:var(--accent)}

.hubrow{display:flex; align-items:center; gap:13px; padding:10px 0;
  border-bottom:1px solid var(--line-3)}
.hubrow:last-child{border-bottom:none}
.hubrow .hn{flex:none; width:22px; height:22px; border-radius:7px; display:grid;
  place-items:center; font-size:11px; font-weight:660; color:var(--accent);
  background:var(--accent-soft)}
.hubrow .hl{flex:1; min-width:0}
.hubrow .hl b{display:block; font-size:13.5px; letter-spacing:-.012em}
.hubrow .hl .cap{display:block; margin:2px 0 0}
.hubrow .hb{flex:none; display:flex; gap:6px}

.vwcard{padding:16px 18px 15px; border:1px solid var(--line-2);
  border-radius:var(--r-sm); background:var(--panel); margin-bottom:11px}
.vwhead{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.vwname{font-size:15px; font-weight:640; letter-spacing:-.02em}

/* The × on a saved-search button, on the page where the button lives. Greg
   asked "how do you delete a saved search?" while looking straight at one —
   there was a Delete on a settings tab he had no reason to open, which is the
   same as there not being one. */
.vchip{position:relative}
.vchip .vx{display:none; margin-left:7px; padding:0 2px; border:none;
  background:none; color:var(--ink-3); font:inherit; font-size:14px;
  line-height:1; cursor:pointer; border-radius:4px}
.vchip:hover .vx, .vchip:focus-within .vx{display:inline-block}
.vchip .vx:hover{color:var(--bad-2); background:var(--line-3)}
/* No hover on a touch screen, so it is simply always there. */
@media (hover:none){ .vchip .vx{display:inline-block} }

/* --------------------------------------------------------- the business card
   The preview is the real PDF in a frame — see me.js for why that is worth the
   awkwardness of styling an <iframe>. 3.5 x 2 is 1.75:1, so the frame is given
   that ratio and the browser's own PDF viewer fits the page inside it. */
.cardframe{display:block; width:100%; max-width:620px; aspect-ratio:1.78/1;
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--panel-2)}
.cardwarn{margin:0 0 14px; padding:11px 14px; border-radius:var(--r-sm);
  background:color-mix(in srgb, var(--warn) 14%, var(--panel));
  border:1px solid color-mix(in srgb, var(--warn) 34%, transparent)}
.cardwarn b{display:block; font-size:13.5px}
.cardwarn span{display:block; margin-top:3px; font-size:12.5px;
  color:var(--ink-2)}

/* The two download links on the card screen are anchors on purpose — a download
   wants a real href so it can be middle-clicked, copied, or opened in a new tab.
   a.btn already exists above and does the rest. */

/* A swatch that shows what each background actually looks like, rather than a
   dot of the accent for all eight. */
.chip .swatch.cardsw{width:18px; height:12px; border-radius:3px;
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--ink) 22%, transparent)}
.chip .swatch.cardsw.white{background:#fff}
.chip .swatch.cardsw.edge{background:#fff;
  box-shadow:inset 0 0 0 1px #999, inset 0 0 0 3px #fff}
.chip .swatch.cardsw.band{background:linear-gradient(to bottom,
  var(--accent) 0 34%, #fff 34% 100%)}
.chip .swatch.cardsw.corner{background:linear-gradient(135deg,
  #fff 0 62%, var(--accent) 62% 100%)}
.chip .swatch.cardsw.ink{background:#141312}
.chip .swatch.cardsw.charcoal{background:#312f2d}
.chip .swatch.cardsw.colour{background:var(--accent)}
.chip .swatch.cardsw.photo{background:linear-gradient(120deg,
  #2b4a6f, #8a6a3a 60%, #1c1b18)}

/* The version, in the corner. Quiet on purpose — it is not information anybody
   needs while they work, and it is the first thing anybody needs the moment they
   ask "did that upgrade actually happen?" That question used to be unanswerable
   from any screen in the app, which is how an upgrade that silently did not take
   effect stayed invisible for a morning. */
.ver{display:inline-block; margin-left:8px; font-size:10px; font-weight:600;
  letter-spacing:.04em; color:var(--ink-3); opacity:.65;
  font-variant-numeric:tabular-nums}
.rail .foot .ver{display:block; margin:4px 0 0}
.gatefoot .ver{margin-left:10px}

/* the sign-in screen's forgotten-password sheet */
.linkish{border:none; background:none; padding:0; font:inherit;
  color:var(--accent); text-decoration:underline; text-underline-offset:2px;
  cursor:pointer}
.linkish:hover{color:var(--ink)}
.fgstep{margin:14px 0; padding:12px 14px; border-radius:var(--r-sm);
  background:var(--panel-2); border:1px solid var(--line-3)}
.fgstep b{display:block; font-size:13px; letter-spacing:-.01em}
.fgstep p{margin:5px 0 0; font-size:12.5px; color:var(--ink-2); line-height:1.55}

/* --- Your setup → Security ------------------------------------------------
   Lists of devices and one QR square. Plain rows rather than a table: there are
   rarely more than a handful and each one carries a sentence, not a figure. */
.safehead{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.safehead b{font-size:13px}
.safelist{display:flex; flex-direction:column; gap:2px}
.safeitem{display:flex; align-items:center; gap:12px; padding:10px 12px;
  border:1px solid var(--line-2); border-radius:10px; background:var(--panel-2)}
.safeitem + .safeitem{margin-top:6px}
.safeitem > div{flex:1 1 auto; min-width:0}
.safeitem b{display:block; font-size:12.5px}
.safeitem .cap{display:block; margin:2px 0 0; font-size:11px}
.safeitem.isme{border-color:color-mix(in srgb,var(--accent) 45%,transparent)}
.warnline{color:var(--bad-2)}

/* The square itself. White plate whatever the theme is: a QR code inverted in
   dark mode is a QR code half the phones in the shop will not read. */
.qrbox{display:flex; justify-content:center; padding:14px; border-radius:12px;
  background:#fff; border:1px solid var(--line-2); margin:0 0 10px}
.qrbox svg{display:block; width:208px; height:208px}
.qrkey{display:block; padding:9px 11px; border-radius:8px; background:var(--panel-2);
  border:1px solid var(--line-2); font-size:12.5px; letter-spacing:.08em;
  word-break:break-all; text-align:center}
.reclist{display:grid; grid-template-columns:1fr 1fr; gap:7px 12px; margin:0 0 4px}
.reclist code{padding:7px 8px; border-radius:7px; background:var(--panel-2);
  border:1px solid var(--line-2); font-size:12.5px; text-align:center;
  letter-spacing:.04em}
.sheetbox.wide{max-width:400px}
@media (max-width:420px){ .reclist{grid-template-columns:1fr} }

/* Admin → Security. Four numbers across the top of the sign-in log, and a tint
   on the rows that were refused so a page of them reads at a glance. */
.signinstats{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:10px; margin:0 0 14px}
.signinstat{padding:12px 14px; border:1px solid var(--line-2); border-radius:11px;
  background:var(--panel)}
.signinstat b{display:block; font-size:21px; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums}
.signinstat span{display:block; margin-top:2px; font-size:11px; color:var(--ink-3)}
.signinstat.warn{border-color:color-mix(in srgb,var(--bad) 40%,transparent)}
.signinstat.warn b{color:var(--bad-2)}
tr.rowbad td{background:color-mix(in srgb,var(--bad) 6%,transparent)}

/* ============================================================ saying hello
   The one moment in the day when somebody is looking at this app and not yet
   doing anything with it. A new iPhone spends that moment writing one word on
   nothing, and it works because of what is missing: no chrome, no buttons
   competing for the eye, no logo. So this is the time of day, the person's
   name written across the screen, and then their own morning counting itself
   in underneath.

   It happens once every six hours at most. Everything in this block is
   one-shot; none of it runs on an ordinary page change. */
.hello{position:fixed; inset:0; z-index:400; display:flex;
  align-items:center; justify-content:center; overflow:hidden;
  background:var(--bg); opacity:1;
  transition:opacity .6s ease, transform .6s cubic-bezier(.5,0,.75,0)}
/* Up and away, not out. A greeting that fades in place has been dismissed; one
   that leaves upward has been got past, which is what somebody is doing. */
.hello.gone{opacity:0; transform:translateY(-3vh) scale(1.06); pointer-events:none}
.hellobox{position:relative; z-index:1; text-align:center; padding:0 28px;
  max-width:min(92vw,720px)}

/* ---- the atmosphere. Three slow blurred lights in the shop's own colour,
   drifting behind everything at a speed nobody can catch in the act. The
   whole trick is the period: twenty seconds and it reads as light, two and
   it reads as a screensaver. */
/* Same rule as the ambient layer below: no blur. This one is over a still
   screen for four seconds rather than over a page being scrolled, so it could
   afford one — but two ways of drawing the same light is one more thing to
   keep in step. */
.aurora{position:absolute; inset:-20%; z-index:0; pointer-events:none;
  opacity:0; transition:opacity 1.1s ease}
.hello.go .aurora{opacity:.68}
.aurora i{position:absolute; display:block; border-radius:50%;
  width:46vmax; height:46vmax}
.aurora i:nth-child(1){left:-8%; top:-14%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--accent) 52%,transparent) 0%, color-mix(in srgb,var(--accent) 18%,transparent) 36%, transparent 70%);
  animation:drift1 26s ease-in-out infinite}
.aurora i:nth-child(2){right:-14%; top:6%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--good) 34%,transparent) 0%, color-mix(in srgb,var(--good) 12%,transparent) 36%, transparent 70%);
  animation:drift2 33s ease-in-out infinite}
.aurora i:nth-child(3){left:24%; bottom:-26%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--accent) 32%,transparent) 0%, color-mix(in srgb,var(--accent) 11%,transparent) 36%, transparent 68%);
  animation:drift3 29s ease-in-out infinite}
@keyframes drift1{ 0%,100%{transform:none}
                   50%{transform:translate3d(7vw,5vh,0) scale(1.14)} }
@keyframes drift2{ 0%,100%{transform:none}
                   50%{transform:translate3d(-6vw,8vh,0) scale(.88)} }
@keyframes drift3{ 0%,100%{transform:none}
                   50%{transform:translate3d(4vw,-7vh,0) scale(1.1)} }

/* ---- "Good morning," — the small half of the sentence, first, so the name
   lands into a sentence that has already been started. */
.hellowhen{font-size:clamp(15px,2vw,20px); letter-spacing:.02em;
  color:var(--ink-3); opacity:0}
.hello.go .hellowhen{animation:hirise .7s var(--in) .1s both}
@keyframes hirise{ from{opacity:0; transform:translateY(10px)}
                   to{opacity:1; transform:none} }

/* ---- the name, written.
   Two copies of the same word stacked: one stroked, which is the nib, and one
   filled, which is the ink drying behind it. A mask sweeps left to right
   across the pair, so the letters appear in the order a hand would make them
   rather than all at once — which is the whole difference between handwriting
   and a fade. Snell Roundhand is on every Mac in this shop. */
.helloscript{margin:2px auto 0; width:min(600px,86vw)}
.hsvg{display:block; width:100%; height:auto; overflow:visible}
.hsvg text{font-family:"Snell Roundhand","Apple Chancery","Segoe Script",
  "Brush Script MT",cursive;
  font-size:112px; font-weight:600}
.hsvg text.ink{fill:none; stroke:var(--accent); stroke-width:1.15;
  stroke-linejoin:round; opacity:.85}
.hsvg text.solid{fill:var(--ink); opacity:1}

/* Still: the finished word, no sweep, nothing to wait for. */
.hello.plain .hsvg text.ink{opacity:0}

.hello.go .helloscript{
  -webkit-mask-image:linear-gradient(90deg,#000 0 40%,
    rgba(0,0,0,.55) 48%, transparent 56%);
  mask-image:linear-gradient(90deg,#000 0 40%,
    rgba(0,0,0,.55) 48%, transparent 56%);
  -webkit-mask-size:260% 100%; mask-size:260% 100%;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  animation:write 1.5s cubic-bezier(.35,.62,.3,1) .34s both}
@keyframes write{ from{-webkit-mask-position:100% 0; mask-position:100% 0}
                  to{-webkit-mask-position:0 0; mask-position:0 0} }
/* The nib itself: the dash pattern lets out at the same rate the mask moves,
   so what is uncovered is ink being laid down rather than ink already there.
   `--len` is measured in JavaScript, because the length of a word in a script
   face cannot be guessed from its letter count. */
.hello.go .hsvg text.ink{animation:nib 1.5s cubic-bezier(.35,.62,.3,1) .34s both}
@keyframes nib{ from{stroke-dashoffset:var(--len,900px)}
                to{stroke-dashoffset:0} }
/* And then the stroke goes, leaving the word solid. Ink drying. */
.hello.go .hsvg text.solid{animation:hifill 1.1s ease .5s both}
@keyframes hifill{ from{opacity:0} to{opacity:1} }

.hellodate{margin-top:6px; font-size:12.5px; letter-spacing:.06em;
  text-transform:uppercase; color:var(--ink-3); opacity:0}
.hello.go .hellodate{animation:hirise .7s var(--in) 1.15s both}

/* ---- the numbers of the morning. They count from zero, which is the
   difference between a figure and a figure arriving. Three, because a fourth
   is one nobody reads. */
.hellotiles{display:flex; justify-content:center; flex-wrap:wrap; gap:10px;
  margin:26px auto 0}
.ht{min-width:118px; padding:12px 16px 11px; border-radius:14px;
  background:color-mix(in srgb,var(--panel) 70%,transparent);
  border:1px solid color-mix(in srgb,var(--line) 70%,transparent);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); opacity:0}
.hello.go .ht{animation:hirise .62s var(--spring) calc(1.25s + var(--i,0)*.09s) both}
.hello.plain .ht{opacity:1; animation:none}
.ht b{display:block; font-size:26px; letter-spacing:-.035em; line-height:1.05;
  font-variant-numeric:tabular-nums}
.ht span{display:block; margin-top:3px; font-size:11px; letter-spacing:.03em;
  color:var(--ink-3)}
.ht.good b{color:var(--pos)}
/* On a phone the three tiles have to stay one row. Two and then a lonely
   third reads as something having gone wrong with the layout rather than as
   three figures. */
@media (max-width:560px){
  .hellotiles{flex-wrap:nowrap; width:100%; gap:7px}
  .ht{min-width:0; flex:1 1 0; padding:11px 6px 10px}
  .ht b{font-size:22px}
  .ht span{font-size:10px; letter-spacing:0}
}
.ht.warn b{color:var(--warn-2)}
.ht.bad b{color:var(--bad-2)}

/* ---- what changed, and the one job.
   Centred chips rather than the full-width rows these are on the RIG page.
   Three bars stretched across a dark screen read as an error state; three
   short sentences sitting in the middle read as news. */
.hello .briefline{margin-top:20px; display:flex; flex-direction:column;
  align-items:center; gap:7px}
.hello .briefline .bl{opacity:0; width:auto; display:inline-flex;
  align-items:center; gap:8px; padding:7px 15px; border-radius:999px;
  font-size:12.5px;
  background:color-mix(in srgb,var(--panel) 66%,transparent);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px)}
.hello .briefline .bl::before{content:""; width:6px; height:6px; flex:0 0 auto;
  border-radius:50%; background:var(--ink-3)}
.hello .briefline .bl.bad::before{background:var(--bad)}
.hello .briefline .bl.warn::before{background:var(--warn)}
.hello .briefline .bl.good::before{background:var(--good)}
.hello.go .briefline .bl{animation:hirise .6s var(--in)
  calc(1.6s + var(--i,0)*.12s) both}
.hello.plain .briefline .bl{opacity:1; animation:none}
.hello .briefdo{opacity:0; display:flex; flex-direction:column;
  align-items:center; text-align:center; width:fit-content;
  margin:18px auto 0; padding:13px 22px;
  background:color-mix(in srgb,var(--panel) 78%,transparent);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px)}
.hello.go .briefdo{animation:hirise .6s var(--in) 1.95s both}
.hello .briefask{opacity:0; display:block; margin:14px auto 0}
.hello.go .briefask{animation:hirise .6s var(--in) 2.1s both}
.hello.plain .briefdo, .hello.plain .briefask{opacity:1; animation:none}

/* The way out, said quietly. Somebody who has read it should not have to work
   out that the screen is waiting on them. */
.hellohint{margin-top:22px; font-size:11px; letter-spacing:.05em;
  color:var(--ink-4,var(--ink-3)); opacity:0}
.hello.go .hellohint{animation:hihint 3s ease 2.5s both}
@keyframes hihint{ 0%{opacity:0} 24%{opacity:.75} 100%{opacity:.75} }
.hello.plain .hellohint{opacity:.75; animation:none}

/* Movement off, or a machine asking for less of it. Same greeting, same
   words, same length of time to read them — it simply arrives rather than
   assembling. Deciding not to greet somebody at all because their machine
   prefers less movement is how the last two versions of this failed. */
@media (prefers-reduced-motion:reduce){
  .hello, .hello *{animation:none !important; transition:opacity .2s ease !important}
  .hello .aurora{display:none}
  .hello .hellowhen, .hello .hellodate, .hello .ht, .hello .briefline .bl,
  .hello .briefdo, .hello .briefask{opacity:1}
  .hello .hellohint{opacity:.75}
  .hello .hsvg text.ink{opacity:0}
  .hello .helloscript{-webkit-mask-image:none; mask-image:none}
}

/* Every page arrives rather than appearing. Two hundred milliseconds and
   opacity only — no movement, nothing to wait for. A page that slides or
   scales on every single navigation stops being smooth and starts being slow
   somewhere around the fourth click of the morning, and people click this app
   a few hundred times a day. */
.shell{animation:appear .2s ease-out both}
@keyframes appear{ from{opacity:0} to{opacity:1} }

/* The page underneath the greeting. Held still while it is up so nothing is
   half-drawn behind it, then brought in properly — this one can afford the
   movement because it happens once, at sign-in. */
body.holding .shell{animation:none; opacity:0}
body.arrived .shell{animation:pagein .6s cubic-bezier(.22,.68,.24,1) both}
@keyframes pagein{ from{opacity:0; transform:translateY(8px) scale(.995)}
                   to{opacity:1; transform:none} }

/* The front page's bands, arriving a beat apart. Capped at six: past that the
   last one is waiting on an animation rather than on the app, and the delay
   somebody notices is the one at the bottom of a long page. */
.hubband{animation:bandin .34s ease-out both}
.hubband:nth-child(2){animation-delay:.05s}
.hubband:nth-child(3){animation-delay:.1s}
.hubband:nth-child(4){animation-delay:.15s}
.hubband:nth-child(5){animation-delay:.2s}
.hubband:nth-child(n+6){animation-delay:.24s}
@keyframes bandin{ from{opacity:0; transform:translateY(7px)}
                   to{opacity:1; transform:none} }

/* ====================================================== things that need you
   A quiet stack at the bottom corner. Deliberately not centred, not modal and
   not blocking: it is the app tapping somebody on the shoulder, not stopping
   them. Bottom-right rather than top-right because the top-right of every page
   in this app is already the theme button and the tabs. */
.nudges{position:fixed; right:18px; bottom:18px; z-index:180;
  display:flex; flex-direction:column; gap:9px; align-items:flex-end;
  pointer-events:none; max-width:min(340px,90vw)}
.nudge{position:relative; display:block; width:100%; pointer-events:auto;
  padding:12px 34px 12px 14px; border-radius:12px; text-decoration:none;
  background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--lift-2); color:var(--ink);
  animation:nudgein .42s cubic-bezier(.2,.75,.3,1) both}
.nudge b{display:block; font-size:12.5px; letter-spacing:-.01em}
.nudge span{display:block; margin-top:2px; font-size:11px; color:var(--ink-3)}
.nudge:hover{border-color:var(--accent)}
/* A stripe rather than a coloured card: the whole point is that it can be
   ignored for ten seconds without being ugly. */
.nudge::before{content:""; position:absolute; left:0; top:10px; bottom:10px;
  width:3px; border-radius:3px; background:var(--ink-3)}
.nudge.warn::before{background:var(--accent)}
.nudge.bad::before{background:var(--bad-2)}
.nudge .nx{position:absolute; top:7px; right:7px; width:20px; height:20px;
  padding:0; border:0; background:none; color:var(--ink-3); font-size:11px;
  line-height:1; border-radius:6px; cursor:pointer}
.nudge .nx:hover{background:var(--panel-2); color:var(--ink)}
.nudge.going{animation:nudgeout .3s ease both}
@keyframes nudgein{ from{opacity:0; transform:translateX(22px) scale(.97)}
                    to{opacity:1; transform:none} }
@keyframes nudgeout{ from{opacity:1; transform:none}
                     to{opacity:0; transform:translateX(22px) scale(.97)} }
@media (max-width:520px){
  .nudges{left:12px; right:12px; bottom:12px; max-width:none}
}

/* A count that moved gets a beat of its own colour, then goes back to normal.
   Only on a change — see countAll. */
.numtile.moved{animation:tilemoved 1.5s ease both}
@keyframes tilemoved{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 55%,transparent)}
  18%{box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 22%,transparent)}
  100%{box-shadow:0 0 0 0 transparent} }
.numtile .nv{font-variant-numeric:tabular-nums}

/* Somebody who has asked their Mac for less movement gets the same deal as
   somebody who set it in this app. This was missing entirely: the setting
   existed, the operating system's own preference was ignored. */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    transition-duration:0s !important; animation-duration:0s !important;
    animation-delay:0s !important; scroll-behavior:auto !important}
  .spin, .loading{animation-duration:.9s !important}
}

/* ========================================================== the answer box
   What comes back when the search box is given a question rather than a name.
   Sits above the results, never instead of them: a question that turns out to
   have been a search still finds the truck underneath. */
.answerbox{padding:14px 17px; border-bottom:1px solid var(--line-2);
  background:color-mix(in srgb,var(--accent) 5%,var(--panel));
  animation:appear .22s ease-out both}
.asaid b{font-size:14px; line-height:1.4; letter-spacing:-.01em;
  display:block; color:var(--ink)}
.ahow{margin-top:5px; font-size:11px; color:var(--ink-3); line-height:1.45}
.arows{margin-top:10px; display:flex; flex-direction:column; gap:1px}
.arow{display:flex; align-items:baseline; gap:9px; padding:5px 7px;
  border-radius:7px; text-decoration:none; color:var(--ink); font-size:12px}
.arow:hover{background:var(--panel-2)}
.arow b{flex:0 0 auto; font-variant-numeric:tabular-nums; min-width:46px}
.arow .aw{flex:1 1 auto; color:var(--ink-2); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.arow .an{flex:0 0 auto; color:var(--ink-3); font-variant-numeric:tabular-nums}
.atips{display:flex; flex-wrap:wrap; gap:6px; margin-top:11px}
.aopen{display:inline-block; margin-top:11px; font-size:11.5px;
  color:var(--accent); text-decoration:none; font-weight:600}
.aopen:hover{text-decoration:underline}
/* The example questions under the front page's search bar. Small and quiet:
   they are a hint about what the box can do, not a menu. */
.hubasks{display:flex; flex-wrap:wrap; gap:7px; margin:10px 2px 0}
.hubfind .ftx2{margin-left:auto; margin-right:12px; font-size:11.5px;
  color:var(--ink-3); white-space:nowrap}
@media (max-width:700px){ .hubfind .ftx2{display:none} }

/* ================================================================== RIG
   The part of the app that talks to you. A panel down the right rather than a
   box over the middle: it is a conversation, and a conversation you cannot look
   away from is an interrogation. The page behind it stays live and usable. */
.rigbtn{display:flex; align-items:center; gap:10px; width:100%; margin:14px 0 0;
  padding:9px 10px; border-radius:11px; cursor:pointer; text-align:left;
  border:1px solid var(--line-2); background:var(--panel-2); color:var(--ink)}
.rigbtn:hover{border-color:var(--accent)}
.rigbtn .lb{flex:1 1 auto; font-size:12.5px; font-weight:600}
.rigbtn .fkey{font-size:10px; color:var(--ink-3)}
html[data-rail="mini"] .rigbtn .lb, html[data-rail="mini"] .rigbtn .fkey{display:none}

/* The mark. Three letters in a box — it needs to be recognisable at 18px in a
   sidebar and at 26px on a briefing, so it is type rather than a picture. */
/* Outlined rather than filled. A solid accent chip needs a text colour that
   reads on it, and the accent is whatever each person picked — white on brass
   is washed out and black on the "ink" accent is invisible. The accent as the
   text and the border works against every one of the thirteen. */
.rigmark{display:inline-flex; align-items:center; justify-content:center;
  min-width:34px; height:22px; padding:0 7px; border-radius:6px;
  background:color-mix(in srgb,var(--accent) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);
  color:var(--accent); font-size:10.5px; font-weight:800;
  letter-spacing:.1em; flex:0 0 auto}
.rigmark.sm{min-width:30px; height:20px; font-size:10px}

.rigpanel{position:fixed; top:0; right:0; bottom:0; width:min(420px,100vw);
  z-index:260; display:flex; flex-direction:column;
  background:var(--panel); border-left:1px solid var(--line);
  box-shadow:-18px 0 44px rgba(0,0,0,.18);
  animation:rigin .26s cubic-bezier(.2,.75,.3,1) both}
.rigpanel.going{animation:rigout .22s ease both}
@keyframes rigin{ from{transform:translateX(26px); opacity:0} to{transform:none; opacity:1} }
@keyframes rigout{ from{transform:none; opacity:1} to{transform:translateX(26px); opacity:0} }

/* The rail button, now that it carries a face rather than a badge. */
.rigbtn .orb{--orb:22px}
.righead .orb{--orb:30px}

.righead{display:flex; align-items:center; gap:11px; padding:14px 14px 13px;
  border-bottom:1px solid var(--line-2); flex:0 0 auto}
.rigwho{flex:1 1 auto; min-width:0}
.rigwho b{display:block; font-size:13px; letter-spacing:.01em}
.rigwho span{display:block; font-size:10.5px; color:var(--ink-3)}
.rigmute, .rigx{width:28px; height:28px; padding:0; border-radius:8px;
  border:1px solid var(--line-2); background:transparent; color:var(--ink-3);
  cursor:pointer; font-size:11px; flex:0 0 auto}
.rigmute:hover, .rigx:hover{color:var(--ink); border-color:var(--line)}
.rigmute.on{background:color-mix(in srgb,var(--accent) 18%,transparent);
  border-color:var(--accent); color:var(--accent)}

.rigthread{flex:1 1 auto; overflow-y:auto; padding:14px;
  display:flex; flex-direction:column; gap:12px}
.rigmsg{max-width:100%; animation:rigmsg .22s ease-out both}
@keyframes rigmsg{ from{opacity:0; transform:translateY(5px)} to{opacity:1; transform:none} }
.rigmsg.me{align-self:flex-end; max-width:88%}
.rigmsg.me span{display:inline-block; padding:8px 12px;
  border-radius:13px 13px 4px 13px; font-size:12.5px; line-height:1.45;
  background:color-mix(in srgb,var(--accent) 16%,var(--panel-2));
  border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);
  color:var(--ink)}
.rigmsg.rig{align-self:flex-start; width:100%; padding:11px 13px;
  border-radius:13px 13px 13px 4px; background:var(--panel-2);
  border:1px solid var(--line-2)}
.rigsaid{display:block; font-size:13px; line-height:1.5; letter-spacing:-.01em}
/* What a follow-up was taken to mean. Above the answer, quieter than it, and
   only there when something was inherited. */
.rigread{display:block; margin-bottom:6px; font-size:10.5px; font-style:italic;
  color:var(--ink-3)}
.righow{display:block; margin-top:6px; font-size:10.5px; line-height:1.5;
  color:var(--ink-3)}
.rigrow{display:flex; align-items:baseline; gap:8px; margin-top:5px;
  padding:5px 7px; border-radius:7px; text-decoration:none; color:var(--ink);
  font-size:11.5px}
.rigrow:hover{background:var(--panel)}
.rigrow b{flex:0 0 auto; min-width:44px; font-variant-numeric:tabular-nums}
.rigrow .rw{flex:1 1 auto; color:var(--ink-2); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.rigrow .rn{flex:0 0 auto; color:var(--ink-3); font-variant-numeric:tabular-nums}
.rigopen{display:inline-block; margin-top:9px; font-size:11px; font-weight:600;
  color:var(--accent); text-decoration:none}
.rigopen:hover{text-decoration:underline}
.rignext{display:flex; flex-wrap:wrap; gap:6px; margin-top:11px}

/* Thinking. Three dots, because the honest thing to show while a question is
   being worked out is that it is being worked out. */
.rigdots{display:inline-flex; gap:4px; padding:3px 2px}
.rigdots i{width:5px; height:5px; border-radius:50%; background:var(--ink-3);
  animation:rigdot 1.1s ease-in-out infinite}
.rigdots i:nth-child(2){animation-delay:.16s}
.rigdots i:nth-child(3){animation-delay:.32s}
@keyframes rigdot{ 0%,60%,100%{opacity:.28; transform:translateY(0)}
                   30%{opacity:1; transform:translateY(-3px)} }

.rigfoot{flex:0 0 auto; display:flex; gap:8px; align-items:flex-end;
  padding:12px 13px; border-top:1px solid var(--line-2)}
.rigin{flex:1 1 auto; resize:none; max-height:120px; padding:9px 11px;
  font-size:12.5px; line-height:1.45; border-radius:10px;
  border:1px solid var(--line-2); background:var(--panel-2); color:var(--ink)}
.rigin:focus{outline:none; border-color:var(--accent)}
.rigsend{width:34px; height:34px; flex:0 0 auto; padding:0;
  border:1px solid var(--accent); border-radius:10px; cursor:pointer;
  background:color-mix(in srgb,var(--accent) 18%,transparent);
  color:var(--accent); font-size:14px; font-weight:700}
.rigsend:hover{opacity:.9}

@media (max-width:560px){
  .rigpanel{width:100vw; border-left:0}
}

/* --------------------------------------------------------- the briefing
   What the greeting is built out of. The staging — what arrives when — is up
   in the "saying hello" block; this is only what the pieces look like.

   There was a second set of rules here for an earlier greeting that is gone
   (.briefbox, .briefhi, .bwhen, .bname, .briefsub), still animating against
   keyframes that had been deleted with it. Removed: a rule that refers to an
   animation which no longer exists does nothing, silently, which is exactly
   the kind of thing somebody spends an afternoon on later. */
.briefline{display:flex; flex-direction:column; gap:6px; margin:20px auto 0;
  max-width:420px}
.briefline .bl{font-size:13px; color:var(--ink-2); padding:7px 12px;
  border-radius:9px; background:var(--panel); border:1px solid var(--line-2)}
.briefline .bl.bad{border-color:color-mix(in srgb,var(--bad) 42%,transparent)}
.briefline .bl.warn{border-color:color-mix(in srgb,var(--accent) 42%,transparent)}
.briefdo{display:block; margin:18px auto 0; max-width:420px; padding:11px 14px;
  border-radius:11px; text-decoration:none; text-align:left;
  background:var(--panel); border:1px solid var(--line)}
.briefdo b{display:block; font-size:13px; color:var(--ink)}
.briefdo span{display:block; margin-top:2px; font-size:11px; color:var(--ink-3)}
.briefdo:hover{border-color:var(--accent)}
.briefask{margin:18px auto 0; padding:8px 16px; border-radius:20px;
  border:1px solid var(--line-2); background:transparent; color:var(--ink-2);
  font-size:11.5px; cursor:pointer}
.briefask:hover{border-color:var(--accent); color:var(--ink)}
/* ---------------------------------------------------------- the RIG page */
.rigmark.big{min-width:52px; height:34px; font-size:15px; border-radius:9px}
.rigwelcome{display:flex; gap:14px; align-items:flex-start; padding:16px 18px;
  border-radius:14px; background:color-mix(in srgb,var(--accent) 6%,var(--panel));
  border:1px solid var(--line-2); margin:0 0 22px}
.rigwelcome b{display:block; font-size:16px; letter-spacing:-.02em}
.rigwelcome span{display:block; margin-top:5px; font-size:13px; line-height:1.55;
  color:var(--ink-2)}
.rigsec{margin:0 0 26px}
.rigsec > button.sm{margin-top:12px}
.rigsay{margin:0 0 6px; font-size:13.5px; line-height:1.6; color:var(--ink)}
.rigsec .righow{margin:0 0 12px; font-size:11px; line-height:1.5;
  color:var(--ink-3)}
.riglist{display:flex; flex-direction:column; gap:2px}
.rigline{display:flex; align-items:center; gap:12px; padding:10px 13px;
  border:1px solid var(--line-2); border-radius:10px; background:var(--panel);
  text-decoration:none; color:var(--ink)}
.rigline + .rigline{margin-top:5px}
.rigline > div{flex:1 1 auto; min-width:0}
.rigline b{display:block; font-size:12.5px}
.rigline .cap{display:block; margin-top:2px; font-size:11px; line-height:1.45}
.rigline .rigfig{flex:0 0 auto; font-size:12px; font-weight:600;
  font-variant-numeric:tabular-nums; color:var(--ink-2)}
.rigline:hover{border-color:var(--accent)}
.rigline.bad{border-left:3px solid var(--bad-2)}
.rigline.warn{border-left:3px solid var(--accent)}
.rigline.good{border-left:3px solid var(--good)}

/* ==========================================================================
   MOVEMENT
   ==========================================================================
   Greg: *"theres no flow or movement tapping between tabs is just hitchy and
   looks bad and everythings is just lifeless."*

   Half of that turned out not to be animation at all. Switching to the contact
   list built thirty-two thousand elements and took 1,929 milliseconds —
   measured — which is sixty dropped frames in a row. Animating over the top of
   a blocked main thread makes it worse, not better, so the lists were windowed
   first (see virtualRows in common.js) and only then was this written.

   The rules, and they are what stop movement becoming noise:

   - **Small.** Six to ten pixels. Anything that travels far has to travel
     slowly, and slow is the opposite of what is wanted.
   - **Fast.** 160-320ms. The hello is the one exception and it happens once.
   - **One curve for arriving, one for moving.** Arriving decelerates hard, so
     it feels like it was already on its way. Moving is symmetrical.
   - **Staggered, but capped.** Rows come in 26ms apart for the first dozen and
     together after that, because the delay somebody notices is always the one
     at the bottom of a long list.
   - **Everything here dies under data-motion="off" or reduce motion.** That
     rule is a hundred lines further down and applies to all of it.            */

:root{
  /* Arriving: hard deceleration. Reads as something that was already moving. */
  --in: cubic-bezier(.16,.84,.28,1);
  /* Moving between two places it belongs. Symmetrical. */
  --move: cubic-bezier(.4,0,.2,1);
  /* A little overshoot, for the few things that should feel physical. */
  --spring: cubic-bezier(.34,1.42,.5,1);
  --quick: .17s;
  --calm: .28s;
}

/* ---- arriving -----------------------------------------------------------
   `.enter` is stamped on a container by MM.enter, and `--i` on each child. */
@keyframes rise{ from{opacity:0; transform:translateY(9px)} to{opacity:1; transform:none} }
@keyframes fade{ from{opacity:0} to{opacity:1} }
@keyframes popin{ from{opacity:0; transform:scale(.965)} to{opacity:1; transform:none} }

.enter > *{
  animation:rise var(--calm) var(--in) both;
  animation-delay:calc(var(--i, 0) * 26ms);
}

/* ---- tabs ---------------------------------------------------------------
   The pane that has just been shown lifts in. Cheap enough to be honest now
   that nothing behind it takes two seconds to build. */
.tabin{animation:rise .26s var(--in) both}

/* The strip itself: a bar that travels to the tab you pressed rather than
   appearing under it. The travelling is the whole point — it is what says the
   two tabs are the same strip and you moved along it. */
.tabs{position:relative}
.tabs .tabmark{position:absolute; bottom:-1px; height:2px; border-radius:2px;
  background:var(--accent); pointer-events:none; opacity:0;
  transition:transform .3s var(--move), width .3s var(--move), opacity .2s ease;
  transform-origin:left center; left:0}
.tabs .tabmark.on{opacity:1}

/* The segmented control: the raised pill is the thing that moves. The pressed
   button keeps its own look until the mark is installed — `.slid` is added by
   the script — so a browser that never ran it still shows which tab is on. */
.seg{position:relative}
.seg .segmark{position:absolute; top:3px; bottom:3px; left:0; border-radius:8px;
  background:var(--panel); box-shadow:var(--lift); pointer-events:none;
  opacity:0; transition:transform .3s var(--move), width .3s var(--move),
                        opacity .18s ease}
.seg .segmark.on{opacity:1}
.seg button{position:relative; z-index:1}
.seg.slid button[aria-pressed="true"]{background:transparent; box-shadow:none}

/* ---- pressing things ----------------------------------------------------
   Every control gives a little under the finger and comes back. On a trackpad
   it is barely there; on a phone it is the difference between a button and a
   picture of a button. */
button, .btn, .chip, .numtile, .dotile, .rigline, .arow, .rigrow, .jumprow,
.safeitem, .nudge, .pcell{
  transition:transform var(--quick) var(--move),
             border-color var(--quick) ease,
             background-color var(--quick) ease,
             box-shadow var(--quick) ease, opacity var(--quick) ease}
button:active, .btn:active, .chip:active, .numtile:active, .dotile:active,
.rigline:active, .rigsend:active, .briefdo:active{transform:scale(.972)}
.numtile:hover, .dotile:hover, .rigline:hover, .briefdo:hover{transform:translateY(-2px)}

/* Cards on the truck list lift a little on hover, which is what makes a grid
   of them feel like objects rather than a printed page. */
.ucard{transition:transform var(--quick) var(--move),
                  box-shadow var(--calm) ease, border-color var(--quick) ease}
.ucard:hover{transform:translateY(-3px); box-shadow:var(--lift-2)}

/* ---- the rail -----------------------------------------------------------
   The current page is marked by a bar that slides between entries. */
.railnav{position:relative}
.railnav .railmark{position:absolute; left:0; width:3px; border-radius:0 3px 3px 0;
  background:var(--accent); opacity:0; pointer-events:none;
  transition:transform .32s var(--spring), height .32s var(--move),
             opacity .2s ease}
.railnav .railmark.on{opacity:1}
.railnav a{transition:background-color var(--quick) ease, color var(--quick) ease,
                      padding-left var(--quick) var(--move)}
.railnav a:hover{padding-left:14px}

/* ---- drawers and sheets -------------------------------------------------- */
.drawer{transition:transform .34s var(--in), opacity .24s ease}
.sheet{animation:fade .18s ease both}
.sheetbox{animation:popin .26s var(--spring) both}

/* ---- numbers ------------------------------------------------------------ */
.nv, .signinstat b, .rigfig, .statbig{font-variant-numeric:tabular-nums}

/* ---- something is loading -----------------------------------------------
   A shimmer where the content will be, rather than an empty screen followed by
   everything at once. Only shown after 200ms: below that it is a flash, and a
   flash reads as a fault. */
@keyframes shimmer{ from{background-position:-420px 0} to{background-position:420px 0} }
.skel{border-radius:9px; min-height:14px;
  background:linear-gradient(90deg,
    var(--panel-2) 0%, color-mix(in srgb,var(--ink) 7%,var(--panel-2)) 46%,
    var(--panel-2) 92%);
  background-size:420px 100%;
  animation:shimmer 1.15s linear infinite}
.skelwrap{display:flex; flex-direction:column; gap:9px; opacity:0;
  animation:fade .2s ease .2s forwards}
.skelwrap .skel:nth-child(1){height:34px; width:44%}
.skelwrap .skel:nth-child(n+2){height:56px}


/* ---- the light behind everything ---------------------------------------
   Three blurred lights in the shop's own colour, drifting behind the whole
   app on periods long enough that nobody can catch one moving. It is the
   difference between a screen that is off and a screen that is idling.

   Fixed, behind everything, and it never touches layout: transform and
   opacity on its own compositor layer, so the cost is a texture upload once
   rather than work on every frame. It does not appear at all when somebody
   has put their own photograph behind the app, and not at all when movement
   is off — a blurred layer that does not move is a cost with nothing bought. */
/* No `filter: blur()`. The first version had one and it cost 117ms a frame
   on a page being scrolled — seven frames a second, measured, against 16.7ms
   with the layer removed. A blur on a full-screen fixed layer is recomputed
   every time anything underneath it moves, and the whole point of this is
   that it moves constantly.

   A radial gradient that fades to nothing by two-thirds of its radius is
   already softer than a 90px blur and costs a texture rather than a filter
   pass. The look is the same; the difference is only in what it is made of. */
.ambient{position:fixed; inset:-25%; z-index:0; pointer-events:none;
  opacity:.42}
.ambient i{position:absolute; display:block; border-radius:50%;
  width:62vmax; height:62vmax; will-change:transform}
.ambient i:nth-child(1){left:-16%; top:-20%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--accent) 34%,transparent) 0%,
    color-mix(in srgb,var(--accent) 12%,transparent) 34%,
    transparent 68%);
  animation:sky1 44s ease-in-out infinite}
.ambient i:nth-child(2){right:-20%; top:14%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--good) 20%,transparent) 0%,
    color-mix(in srgb,var(--good) 7%,transparent) 34%,
    transparent 68%);
  animation:sky2 57s ease-in-out infinite}
.ambient i:nth-child(3){left:32%; bottom:-34%;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in srgb,var(--accent) 24%,transparent) 0%,
    color-mix(in srgb,var(--accent) 9%,transparent) 34%,
    transparent 66%);
  animation:sky3 51s ease-in-out infinite}
@keyframes sky1{ 0%,100%{transform:none}
                 50%{transform:translate3d(6vw,4vh,0) scale(1.16)} }
@keyframes sky2{ 0%,100%{transform:none}
                 50%{transform:translate3d(-5vw,7vh,0) scale(.86)} }
@keyframes sky3{ 0%,100%{transform:none}
                 50%{transform:translate3d(3vw,-6vh,0) scale(1.12)} }
/* Light paper wants less of it: the same wash that reads as depth on a dark
   screen reads as a stain on a pale one. */
:root[data-theme="light"] .ambient{opacity:.34}
@media (prefers-reduced-motion:reduce){ .ambient{display:none} }

/* The app sits above the light, on its own surface, so text is never read
   through a gradient. */
.shell{position:relative; z-index:1}

/* The page lets the light through. Only while there is a light to let
   through — every colour in this app is opaque by default, because a whole
   interface built out of translucency is one that cannot be read in a
   workshop with the door open. This is the one layer that gives. */
:root.lit .shell{background:linear-gradient(to right,
  color-mix(in srgb,var(--rail) 86%,transparent) 0 var(--rail-w),
  color-mix(in srgb,var(--bg) 78%,transparent) var(--rail-w))}
:root.lit[data-theme="light"] .shell{background:linear-gradient(to right,
  color-mix(in srgb,var(--rail) 92%,transparent) 0 var(--rail-w),
  color-mix(in srgb,var(--bg) 88%,transparent) var(--rail-w))}

/* WHY THE SCENE GETS THE CONTENT AREA TO ITSELF
 *
 * Greg, twice: "the aurora doesnt work", and then "aurora still doesnt work".
 *
 * The two rules immediately above lay the page's own colour over the whole
 * window at 78% in the dark and 88% on paper. They were written for `.ambient`,
 * the drifting light, and for that they are right: a moving wash wants to be
 * barely there. But the scenes paint on `body`, which is underneath this as
 * well, so every scene in the app was being shown through an almost-solid sheet
 * of the background colour. Twenty-two per cent of a deliberately gentle
 * gradient is nothing, and nothing is what it measured: two values out of 255
 * between the corner of the window and the middle of it.
 *
 * So where there is a scene, the content area stops painting over it. The rail
 * keeps its own band — it has a material setting of its own and needs a surface
 * to be made of — and every card, tile, panel and menu is opaque in its own
 * right, so no text is ever read through a gradient. What changes is only the
 * space between them, which is the space a light behind the app is supposed to
 * occupy.
 *
 * `[data-scene="off"]` keeps the old wash on purpose: somebody who turned the
 * light off asked for a flat page and should get one. */
:root.lit:not([data-scene="off"]) .shell{
  background:linear-gradient(to right,
    color-mix(in srgb,var(--rail) 86%,transparent) 0 var(--rail-w),
    transparent var(--rail-w));
}
:root.lit[data-theme="light"]:not([data-scene="off"]) .shell{
  background:linear-gradient(to right,
    color-mix(in srgb,var(--rail) 92%,transparent) 0 var(--rail-w),
    transparent var(--rail-w));
}
/* With the menu across the top there is no rail column to protect, so the whole
   width is the scene's. Without this the flat `var(--bg)` set for that layout
   further down paints straight over everything above. */
:root.lit[data-rail="top"]:not([data-scene="off"]) .shell{ background:none }
.uppill:hover{border-color:var(--accent)}
.uppill[hidden]{display:none}
.uppill .uptxt{font-size:12.5px; letter-spacing:-.01em; display:block}
.uppill .upsub{font-size:10.5px; color:var(--ink-3); display:block; margin-top:1px}
.upspin{width:15px; height:15px; flex:0 0 auto; border-radius:50%;
  border:2px solid color-mix(in srgb,var(--accent) 30%,transparent);
  border-top-color:var(--accent); animation:upspin .8s linear infinite}
@keyframes upspin{ to{transform:rotate(360deg)} }
@media (prefers-reduced-motion:reduce){ .upspin{animation:none} }

/* ---- RIG's line in the rail -------------------------------------------
   One true thing at a time, swapped every few seconds. It is the only part
   of the app that moves without being touched, which is the point: a screen
   with a live line on it is awake. */
.rigtick{display:block; position:relative; overflow:hidden;
  margin:6px 0 0; padding:9px 11px; border-radius:10px; min-height:34px;
  text-decoration:none; color:var(--ink-2); font-size:11.5px; line-height:1.35;
  background:color-mix(in srgb,var(--panel) 55%,transparent);
  border:1px solid transparent}
.rigtick:hover{border-color:var(--line); color:var(--ink)}
.rigtick .tkline{display:block;
  transition:transform .26s var(--in), opacity .26s ease}
.rigtick .tkline.in{transform:translateY(10px); opacity:0}
.rigtick .tkline.out{position:absolute; left:11px; right:11px; top:9px;
  transform:translateY(-10px); opacity:0}
.rigtick.bad{color:var(--bad-2)}
.rigtick.warn{color:var(--warn-2)}
.rigtick.good{color:var(--pos)}
.rigtick.bad:hover, .rigtick.warn:hover, .rigtick.good:hover{color:var(--ink)}
:root[data-rail="icons"] .rigtick{display:none}
@media (max-width:900px){ .rigtick{display:none} }

/* ---- the microphone -----------------------------------------------------
   On the search boxes and on RIG. Lit while it is listening, and the box it
   types into is lit with it so there is never a doubt about where the words
   are going to land. */
.micbtn{flex:0 0 auto; width:36px; height:36px; padding:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:50%; border:1px solid var(--line);
  background:var(--panel); color:var(--ink-3);
  transition:border-color .16s ease, color .16s ease, transform .12s ease}
.micbtn:hover{border-color:var(--accent); color:var(--ink)}
.micbtn:active{transform:scale(.94)}
.micbtn .micglyph{font-size:11px; line-height:1}
.micbtn.on{border-color:var(--bad); color:var(--bad-2);
  background:color-mix(in srgb,var(--bad) 12%,var(--panel))}
.micbtn.on .micglyph{animation:micpulse 1.1s ease-in-out infinite}
@keyframes micpulse{ 0%,100%{opacity:1; transform:scale(1)}
                     50%{opacity:.45; transform:scale(.72)} }
@media (prefers-reduced-motion:reduce){ .micbtn.on .micglyph{animation:none} }
input.hearing, textarea.hearing{border-color:var(--bad) !important;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--bad) 14%,transparent)}

/* The one-time question about where the sound goes. */
.micask .micbox{max-width:440px; margin:12vh auto; padding:26px 26px 22px;
  border-radius:16px; background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--lift-2)}
.micask h2{margin:0 0 10px; font-size:19px; letter-spacing:-.02em}
.micask p{margin:0 0 10px; font-size:13px; line-height:1.6; color:var(--ink-2)}
.micask p.cap{font-size:11.5px; color:var(--ink-3)}
.micacts{display:flex; gap:9px; align-items:center; margin:16px 0 12px}

/* ---- what RIG is about to do -------------------------------------------
   The sentence somebody is agreeing to. It has to be readable at a glance
   from a phone held in one hand, because that is where a spoken instruction
   comes from. */
.rigplan{align-self:flex-start; max-width:92%; margin:2px 0 6px;
  padding:13px 15px 12px; border-radius:14px;
  background:color-mix(in srgb,var(--accent) 7%,var(--panel));
  border:1px solid color-mix(in srgb,var(--accent) 34%,transparent);
  animation:rise .26s var(--in) both}
.rigplan.snag{background:color-mix(in srgb,var(--bad) 7%,var(--panel));
  border-color:color-mix(in srgb,var(--bad) 34%,transparent)}
.rigplan .plansay{display:block; font-size:14.5px; line-height:1.4;
  letter-spacing:-.015em}
.rigplan .plantruck{display:block; margin-top:4px; font-size:11px;
  color:var(--ink-3)}
.rigplan .plandetail{display:block; margin-top:7px; font-size:12.5px;
  line-height:1.5; color:var(--ink-2)}
.planswap{display:flex; align-items:baseline; gap:10px; margin-top:10px;
  flex-wrap:wrap; font-variant-numeric:tabular-nums}
.planswap .was{font-size:13px; color:var(--ink-3);
  text-decoration:line-through; text-decoration-thickness:1px}
.planswap .arrow{font-size:12px; color:var(--ink-3)}
.planswap .now{font-size:17px; font-weight:640; letter-spacing:-.02em}
.rigplan .planwarn{display:block; margin-top:9px; padding:7px 10px;
  border-radius:8px; font-size:11.5px; line-height:1.45;
  background:color-mix(in srgb,var(--warn) 14%,transparent);
  color:var(--warn-2)}
.planacts{display:flex; gap:8px; align-items:center; margin-top:13px}
.rigplan .plandone{display:block; margin-top:11px; font-size:12px;
  color:var(--pos)}
.rigplan.dropped{opacity:.6}
.rigplan.dropped .plandone{color:var(--ink-3)}
.rigplan.done{border-color:color-mix(in srgb,var(--pos) 40%,transparent)}

/* ============================================================== RIG's face
   Two arcs and a core. Every state below is something that is actually true
   at the moment it is showing — see the note in common.js. */
.orb{--orb:26px; display:inline-block; flex:0 0 auto;
  width:var(--orb); height:var(--orb); line-height:0}
.orbsvg{display:block; width:100%; height:100%; overflow:visible}
.orbtrack{stroke:color-mix(in srgb,var(--accent) 16%,transparent)}
.orbout{stroke:var(--accent); transform-origin:50% 50%}
.orbin{stroke:color-mix(in srgb,var(--accent) 62%,transparent);
  transform-origin:50% 50%}
.orbcore{fill:color-mix(in srgb,var(--accent) 26%,transparent);
  transform-origin:50% 50%}
.orbbars{opacity:0}
.orbbars rect{fill:var(--accent); transform-origin:50% 50%}

/* ---- resting. Slow enough that it reads as breathing rather than working. */
.orb.rest .orbout{animation:orbturn 14s linear infinite}
.orb.rest .orbin{animation:orbturn 19s linear infinite reverse}
.orb.rest .orbcore{animation:orbbreathe 4.4s ease-in-out infinite}

/* ---- listening. Only while the microphone is open. Wider, faster, and the
   core opens up — the shape of something waiting for you to finish. */
.orb.listen .orbout{animation:orbturn 4s linear infinite;
  stroke:var(--bad)}
.orb.listen .orbin{animation:orbturn 6s linear infinite reverse;
  stroke:color-mix(in srgb,var(--bad) 70%,transparent)}
.orb.listen .orbtrack{stroke:color-mix(in srgb,var(--bad) 18%,transparent)}
.orb.listen .orbcore{fill:color-mix(in srgb,var(--bad) 34%,transparent);
  animation:orbhear 1s ease-in-out infinite}

/* ---- thinking. Both arcs the same way and quick: this one is allowed to
   read as work, because it is. */
.orb.think .orbout{animation:orbturn 1.1s linear infinite}
.orb.think .orbin{animation:orbturn 1.6s linear infinite}
.orb.think .orbcore{animation:orbbreathe 1.1s ease-in-out infinite}

/* ---- talking. The arcs settle and the bars take over. */
.orb.talk .orbout{animation:orbturn 9s linear infinite}
.orb.talk .orbin{animation:orbturn 12s linear infinite reverse}
.orb.talk .orbcore{fill:transparent}
.orb.talk .orbbars{opacity:1}
.orb.talk .orbbars rect{animation:orbbar .62s ease-in-out infinite;
  animation-delay:calc(var(--b, 0) * .13s)}

/* ---- alert. Not flashing: a colour and a slow swell. Something on this lot
   is wrong and will still be wrong in an hour; a strobe would be a lie about
   how urgent that is, and unbearable to sit next to all day. */
.orb.alert .orbtrack{stroke:color-mix(in srgb,var(--warn) 20%,transparent)}
.orb.alert .orbout{stroke:var(--warn); animation:orbturn 8s linear infinite}
.orb.alert .orbin{stroke:color-mix(in srgb,var(--warn) 66%,transparent);
  animation:orbturn 11s linear infinite reverse}
.orb.alert .orbcore{fill:color-mix(in srgb,var(--warn) 30%,transparent);
  animation:orbbreathe 2.6s ease-in-out infinite}

@keyframes orbturn{ to{transform:rotate(360deg)} }
@keyframes orbbreathe{ 0%,100%{transform:scale(.82); opacity:.75}
                       50%{transform:scale(1.06); opacity:1} }
@keyframes orbhear{ 0%,100%{transform:scale(.7)} 50%{transform:scale(1.12)} }
@keyframes orbbar{ 0%,100%{transform:scaleY(.35)} 50%{transform:scaleY(1)} }

/* Movement off: the face is still his face, it simply holds still. The state
   is still readable, because the state is a colour as well as a motion. */
@media (prefers-reduced-motion:reduce){
  .orb *{animation:none !important}
  .orb.talk .orbbars{opacity:1}
}
:root[data-motion="off"] .orb *{animation:none !important}

/* ---- where he appears --------------------------------------------------
   Three layouts over one panel. The markup is identical in all three, so
   nothing about what he can do depends on where he is standing. */

/* The middle of the screen. Everything else dimmed, because a card floating
   over a page somebody is still reading is neither one thing nor the other. */
.rigveil{position:fixed; inset:0; z-index:255;
  background:color-mix(in srgb,#000 46%,transparent);
  backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
  animation:veilin .22s ease both}
.rigveil.going{animation:veilout .2s ease both}
@keyframes veilin{ from{opacity:0} to{opacity:1} }
@keyframes veilout{ from{opacity:1} to{opacity:0} }

.rigpanel.rig-popup{
  top:50%; left:50%; right:auto; bottom:auto;
  width:min(620px,94vw); height:min(660px,86vh);
  border-radius:20px; border:1px solid var(--line);
  box-shadow:0 32px 90px rgba(0,0,0,.45);
  animation:popin .3s cubic-bezier(.2,.9,.3,1.05) both}
.rigpanel.rig-popup.going{animation:popout .2s ease both}
@keyframes popin{ from{opacity:0; transform:translate(-50%,-46%) scale(.94)}
                  to{opacity:1; transform:translate(-50%,-50%) scale(1)} }
@keyframes popout{ from{opacity:1; transform:translate(-50%,-50%) scale(1)}
                   to{opacity:0; transform:translate(-50%,-52%) scale(.97)} }
.rigpanel.rig-popup .righead{padding:18px 20px 16px}
.rigpanel.rig-popup .righead .orb{--orb:38px}
.rigpanel.rig-popup .rigwho b{font-size:15px}
.rigpanel.rig-popup .rigthread{padding:18px 20px}
.rigpanel.rig-popup .rigfoot{padding:14px 18px 18px}

/* The corner. Small on purpose — this is the one for somebody who wants him
   there but not in the way. */
.rigpanel.rig-corner{
  top:auto; right:18px; bottom:18px; left:auto;
  width:min(380px,92vw); height:min(520px,72vh);
  border-radius:16px; border:1px solid var(--line);
  box-shadow:0 22px 60px rgba(0,0,0,.38);
  animation:cornerin .26s cubic-bezier(.2,.85,.3,1) both}
.rigpanel.rig-corner.going{animation:cornerout .2s ease both}
@keyframes cornerin{ from{opacity:0; transform:translateY(16px) scale(.97)}
                     to{opacity:1; transform:none} }
@keyframes cornerout{ from{opacity:1; transform:none}
                      to{opacity:0; transform:translateY(10px) scale(.98)} }

/* A phone has one layout whatever was chosen: there is no side to be down and
   no corner to be in. */
@media (max-width:560px){
  .rigpanel.rig-popup, .rigpanel.rig-corner{
    top:0; left:0; right:0; bottom:0; width:100vw; height:100dvh;
    border-radius:0; transform:none; animation:rigin .26s ease both}
}

/* The empty popup. Room to be somebody before being a text box. */
.rigthread.opening{display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:0; text-align:center}
.rigbig{display:flex; flex-direction:column; align-items:center; gap:12px;
  margin-bottom:22px}
.rigbig .orb{--orb:76px}
.rigbig b{font-size:20px; letter-spacing:-.02em}
.rigbig span{font-size:13px; color:var(--ink-3); max-width:34ch; line-height:1.5}
.rignext.big{display:flex; flex-wrap:wrap; gap:8px; justify-content:center;
  max-width:440px}
.rignext.big .chip{animation:rise .3s var(--in) both}
.rignext.big .chip:nth-child(2){animation-delay:.05s}
.rignext.big .chip:nth-child(3){animation-delay:.1s}
.rignext.big .chip:nth-child(4){animation-delay:.15s}

/* ---- him saying something without being asked --------------------------
   Bottom left, opposite the nudges, small, and it goes on its own. */
.rigsay{position:fixed; left:18px; bottom:18px; z-index:200;
  display:flex; align-items:center; gap:11px; max-width:min(400px,88vw);
  padding:11px 12px 11px 13px; border-radius:14px;
  background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--lift-2);
  animation:sayin .38s cubic-bezier(.2,.8,.3,1.05) both}
.rigsay.going{animation:sayout .24s ease both}
@keyframes sayin{ from{opacity:0; transform:translateY(14px) scale(.96)}
                  to{opacity:1; transform:none} }
@keyframes sayout{ from{opacity:1; transform:none}
                   to{opacity:0; transform:translateY(8px) scale(.98)} }
.rigsay .sayline{flex:1 1 auto; min-width:0; font-size:12.5px; line-height:1.4;
  color:var(--ink); text-decoration:none}
.rigsay .sayline:hover{color:var(--accent)}
.rigsay.bad{border-color:color-mix(in srgb,var(--bad) 40%,transparent)}
.rigsay.warn{border-color:color-mix(in srgb,var(--warn) 40%,transparent)}
.sayx{flex:0 0 auto; width:22px; height:22px; padding:0; border-radius:6px;
  border:0; background:transparent; color:var(--ink-3); cursor:pointer;
  font-size:11px}
.sayx:hover{color:var(--ink)}
@media (max-width:900px){ .rigsay{display:none} }

/* ---- links on a truck --------------------------------------------------
   Copy is the loud button on every row. What somebody is doing here is
   pasting a link into a message with a customer waiting on the other end. */
.lwrap{display:block}
.llist{display:flex; flex-direction:column; gap:7px; margin-bottom:10px}
.lrow{display:flex; align-items:center; gap:10px; padding:9px 10px;
  border-radius:10px; background:var(--panel-2,var(--panel));
  border:1px solid var(--line-2)}
.lrow:hover{border-color:var(--line)}
.lkind{flex:0 0 auto; width:22px; height:22px; border-radius:6px;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:11px; color:var(--accent);
  background:color-mix(in srgb,var(--accent) 12%,transparent)}
.lrow.video .lkind{color:var(--bad-2);
  background:color-mix(in srgb,var(--bad) 12%,transparent)}
.lmid{flex:1 1 auto; min-width:0}
.lmid b{display:block; font-size:12.5px; letter-spacing:-.01em}
.lurl{display:block; font-size:10.5px; color:var(--ink-3);
  text-decoration:none; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap}
.lurl:hover{color:var(--accent); text-decoration:underline}
.lrow .sm.ok{border-color:var(--pos); color:var(--pos)}
.lx{width:28px; padding:0}
.lacts{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:12px}
.lform{display:flex; gap:8px; flex-wrap:wrap; align-items:center}
.lform input{flex:1 1 150px; min-width:0}
.lform input:nth-child(2){flex:2 1 240px}

/* While the page is moving, the light stands aside — see the note by
   restWhileScrolling() in common.js. Painting one opaque layer instead of
   compositing two is the difference between 60 frames a second and 30. */
:root.lit.scrolling .shell{background:linear-gradient(to right,
  var(--rail) 0 var(--rail-w), var(--bg) var(--rail-w))}
:root.scrolling .ambient{opacity:0; transition:none}
.ambient{transition:opacity .5s ease}

/* ---- the backup that is somewhere else ----------------------------------
   A dot and a sentence, because this is the one screen where somebody needs
   to know where they stand before they read a word of it. Green is not
   decoration here: it is the difference between having the business backed up
   and only believing you have. */
.bkhead{display:flex; align-items:center; gap:10px}
.bkdot{flex:0 0 auto; width:11px; height:11px; border-radius:50%;
  background:var(--ink-3); box-shadow:0 0 0 3px color-mix(in srgb,var(--ink-3) 18%,transparent)}
.bkdot.good{background:var(--good,#3fb984);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--good,#3fb984) 22%,transparent)}
.bkdot.bad{background:var(--bad,#e0574c);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--bad,#e0574c) 22%,transparent)}
.bkdot.none{background:var(--warn,#d69b3c);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--warn,#d69b3c) 22%,transparent)}

/* A checkbox with its question beside it, on the Paperwork tab. Not `.tick`,
   which already means an SVG label on a chart and the corner mark on a page
   cell — a third meaning for one class name is how a style starts landing
   somewhere nobody intended. */
.optline{display:flex; align-items:center; gap:9px; margin:4px 0 0;
  cursor:pointer; font-size:13px; color:var(--ink)}
.optline input{width:auto; min-width:0; margin:0}

/* ---------------------------------------------------- reading a lot from a link
 *
 * The address box and the paste box are two ways of doing one thing, and the
 * layout has to say which is which without a paragraph explaining it. The link
 * is first because it is less work; the rule between them says "or", so that
 * when the link cannot work — and on a site that builds its pages in the
 * browser it cannot — the box underneath reads as the other way rather than as
 * a consolation.
 */
.urlrow{display:flex; gap:8px; align-items:stretch}
.urlrow input{flex:1 1 auto; min-width:0}
.urlrow button{flex:0 0 auto; white-space:nowrap}

.orline{display:flex; align-items:center; gap:12px; margin:16px 0 13px;
  color:var(--ink-3); font-size:12px; letter-spacing:.01em}
.orline::before,.orline::after{content:""; flex:1 1 auto; height:1px;
  background:var(--line)}

/* Thumbnails rather than a count. "Found 8 photographs" is a claim; eight
 * pictures is the claim and the evidence at once, and it is how somebody spots
 * that six of them are the seller's other trucks before any of it is saved
 * onto a unit. Broken ones remove themselves, so a dead image never leaves a
 * grey square implying a photo that is not there. */
.gotpix{margin-top:12px}
.pixstrip{display:flex; gap:7px; overflow-x:auto; padding-bottom:4px;
  scrollbar-width:thin}
.pixstrip img{flex:0 0 auto; width:82px; height:62px; object-fit:cover;
  border-radius:8px; border:1px solid var(--line); background:var(--line-3)}

/* ---------------------------------------------------------------- the sale board
 *
 * One card per lot, in the order worth working through. The verdict is a word
 * rather than a colour on its own: red and green mean opposite things to the
 * eight per cent of men who cannot tell them apart, and this is a screen for a
 * trade that is nearly all men.
 */
.salerow{margin-top:12px}
.salehead{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap}
.salehead b{font-size:15.5px; letter-spacing:-0.015em}
.vpill{font-size:11.5px; padding:3px 9px; border-radius:999px;
  border:1px solid var(--line); color:var(--ink-2); white-space:nowrap}
.vpill.room{border-color:var(--good); color:var(--good-2)}
.vpill.open{border-color:var(--accent); color:var(--accent)}
.vpill.tight{border-color:var(--warn); color:var(--warn)}
.vpill.over,.vpill.unread{border-color:var(--bad); color:var(--bad)}

/* A stripe down the edge, so the shape of the list is readable from across a
 * shed without reading any of it. */
.salerow.room{border-left:3px solid var(--good)}
.salerow.open{border-left:3px solid var(--accent)}
.salerow.tight{border-left:3px solid var(--warn)}
.salerow.over,.salerow.unread{border-left:3px solid var(--bad)}
.salerow.unknown{border-left:3px solid var(--line)}

.salenums{display:flex; flex-wrap:wrap; gap:22px; margin-top:11px}
.salenums > div{display:flex; flex-direction:column; gap:1px}
.salenums b{font-size:18px; font-variant-numeric:tabular-nums;
  letter-spacing:-0.02em}
.salenums .cap{font-size:11.5px}

@media (max-width:640px){
  .salenums{gap:14px}
  .salenums b{font-size:16px}
}

/* "We bought it" comes in shut. Adding a truck to the lot should take two
 * deliberate presses: the mistake it prevents — a truck on the lot that was
 * never actually bought — is one somebody finds three weeks later, standing in
 * the yard looking for it. */
.boughtbox{margin-top:14px}
.boughtbox > summary{cursor:pointer; font-weight:650; font-size:14.5px;
  letter-spacing:-0.012em; list-style:none}
.boughtbox > summary::-webkit-details-marker{display:none}
.boughtbox > summary::before{content:"+ "; color:var(--ink-3)}
.boughtbox[open] > summary::before{content:"\2212 "}
.boughtbox[open] > summary{margin-bottom:4px}

/* A fact on the row, not a wall underneath it. Greg buys 2016 and newer as a
 * rule, but "i said wed like to chase newer trucks for good deals not forget
 * all good deals" -- a lot older than that still sorts on its own money and
 * just says so, in the same pill row as the verdict. */
.agepill{font-size:11.5px; padding:3px 9px; border-radius:999px;
  border:1px dashed var(--line); color:var(--ink-3); white-space:nowrap}

/* A quiet qualifier beside a label. The link box works on sites that hand the
 * page over and not on ones that check for a real browser, and a label that
 * promises more than that is a label somebody trusts once. */
.flab .hint{font-weight:450; font-size:11.5px; color:var(--ink-3);
  margin-left:6px; letter-spacing:0}

/* ------------------------------------------------------- what the room is paying
 *
 * Greg: "i need this auction calculator to have some type of live feed to go
 * off of". This is that figure, and it sits beside the ceiling rather than
 * inside it — the ceiling is what M&M may pay, this is what the trade has
 * actually been paying, and when the second is above the first the answer is to
 * go home.
 */
.marketline{margin:14px 0 0; padding:13px 15px; border-radius:var(--r);
  background:var(--sur-2); border:1px solid var(--edge)}
.marketline.over{border-color:var(--warn); background:var(--warn-bg)}
.mkhead{display:flex; align-items:baseline; gap:12px; flex-wrap:wrap}
.mkhead b{font-size:16px; letter-spacing:-0.018em}
.mkhead .mkn{font-size:12px; color:var(--ink-3)}
.marketline p{margin:7px 0 0; font-size:13.5px; color:var(--ink-2)}
.mklots{margin-top:10px; border-top:1px solid var(--line); padding-top:8px}
.mklot{display:flex; align-items:baseline; gap:12px; padding:5px 0;
  border-bottom:1px solid var(--line-3); font-size:13px}
.mklot > span:first-child{flex:0 0 auto; font-weight:600}
.mklot .cap{flex:1 1 auto; font-size:11.5px}
.mklot b{flex:0 0 auto; font-variant-numeric:tabular-nums}

.marketbox{margin-top:14px}
.marketbox > summary{cursor:pointer; font-weight:650; font-size:14.5px;
  letter-spacing:-0.012em; list-style:none}
.marketbox > summary::-webkit-details-marker{display:none}
.marketbox > summary::before{content:"+ "; color:var(--ink-3)}
.marketbox[open] > summary::before{content:"\2212 "}
.marketbox > summary .hint{font-weight:450; font-size:11.5px;
  color:var(--ink-3); margin-left:8px}

/* ============================================================ v83 — craft

   Four things were wrong at once, and none of them was a missing setting:
   the icons were font dingbats, the card shadow was 4% black (measurable,
   invisible), every number on the front page had the same weight, and there
   was no colour anywhere. This file is those four. */

/* ---- icons ---- */
.ico.drawn, .fico.drawn, .di.drawn{display:inline-grid; place-items:center;
  line-height:0}
.ico svg, .fico svg, .di svg{display:block; overflow:visible}

.railnav .ico{width:20px; height:20px; opacity:1; color:var(--ink-3);
  transition:color .16s cubic-bezier(.2,.7,.3,1), transform .16s cubic-bezier(.2,.7,.3,1)}
.railnav .ico svg{width:19px; height:19px}
.railnav a:hover .ico{color:var(--ink-2)}
.railnav a:hover .ico svg{transform:translateX(.5px)}
/* The live page is the one thing in this column allowed colour. */
.railnav a.on .ico{color:inherit}
.railnav a.on .ico svg{stroke-width:2}
.railfind .fico{width:15px; height:15px; opacity:.8}
.railfind .fico svg{width:15px; height:15px}
.hubfind .fico{width:19px; height:19px}
.hubfind .fico svg{width:19px; height:19px}



/* ---- action tiles: the chip fills with the accent on hover ---- */
.dotile{transition:border-color .16s, transform .16s cubic-bezier(.2,.7,.3,1),
  box-shadow .16s}
.dotile .di{width:34px; height:34px; border-radius:10px;
  color:var(--accent); background:var(--accent-soft);
  transition:background .16s, color .16s, transform .18s cubic-bezier(.2,.7,.3,1)}
.dotile .di svg{width:18px; height:18px}
.dotile:hover{box-shadow:var(--lift-2); transform:translateY(-2px)}
.dotile:hover .di{background:var(--accent); color:#fff; transform:scale(1.06)}
:root[data-theme="dark"] .dotile:hover .di,
:root:where(:not([data-theme="light"])) .dotile:hover .di{color:#14120e}
@media (prefers-color-scheme: light){
  :root:where(:not([data-theme="dark"])) .dotile:hover .di{color:#fff}
}

/* ---- the numbers, ranked ----
   A tile that means something is wrong should look wrong. The app already knew
   which were which; it was drawing them the same. */
.numtile{position:relative; overflow:hidden;
  transition:border-color .16s, transform .16s cubic-bezier(.2,.7,.3,1),
    box-shadow .16s, background .16s}
.numtile::before{content:""; position:absolute; left:0; top:0; bottom:0;
  width:3px; background:transparent; transition:background .16s}
.numtile:hover{box-shadow:var(--lift-2); transform:translateY(-2px)}
.numtile.bad{background:linear-gradient(180deg,
    color-mix(in srgb, var(--bad) 7%, var(--panel)), var(--panel) 60%);
  border-color:color-mix(in srgb, var(--bad) 34%, var(--line-2))}
.numtile.bad::before{background:var(--bad)}
.numtile.warn{background:linear-gradient(180deg,
    color-mix(in srgb, var(--warn) 10%, var(--panel)), var(--panel) 60%);
  border-color:color-mix(in srgb, var(--warn) 36%, var(--line-2))}
.numtile.warn::before{background:var(--warn)}
.numtile.bad:hover{border-color:var(--bad)}
.numtile.warn:hover{border-color:var(--warn-2)}

.numtile::before{background:var(--line-2)}
.numtile.bad::before{background:var(--bad)}
.numtile.warn::before{background:var(--warn)}
.numtile .nv{font-size:29px; letter-spacing:-.04em}
.numtile{padding-left:19px}

/* ---- band headings: a rule running out from the words ---- */
.hubhead h2{display:flex; align-items:center; gap:14px}
.hubhead h2::after{content:""; flex:1; height:1px;
  background:linear-gradient(to right,
    color-mix(in srgb, var(--ink) 17%, transparent) 0%,
    color-mix(in srgb, var(--ink) 4%, transparent) 45%, transparent 85%)}

/* ---- arrival: 22ms a step, all in under a third of a second ---- */
@media (prefers-reduced-motion: no-preference){
  :root[data-motion="on"] .hubdo .dotile,
  :root[data-motion="on"] .hubnums .numtile{
    animation:tilein .38s cubic-bezier(.16,.84,.36,1) both}
  .hubdo .dotile:nth-child(1),.hubnums .numtile:nth-child(1){animation-delay:.00s}
  .hubdo .dotile:nth-child(2),.hubnums .numtile:nth-child(2){animation-delay:.022s}
  .hubdo .dotile:nth-child(3),.hubnums .numtile:nth-child(3){animation-delay:.044s}
  .hubdo .dotile:nth-child(4),.hubnums .numtile:nth-child(4){animation-delay:.066s}
  .hubdo .dotile:nth-child(5),.hubnums .numtile:nth-child(5){animation-delay:.088s}
  .hubdo .dotile:nth-child(6),.hubnums .numtile:nth-child(6){animation-delay:.11s}
  .hubdo .dotile:nth-child(7),.hubnums .numtile:nth-child(7){animation-delay:.132s}
  .hubdo .dotile:nth-child(8),.hubnums .numtile:nth-child(8){animation-delay:.154s}
  .hubdo .dotile:nth-child(9),.hubnums .numtile:nth-child(9){animation-delay:.176s}
  .hubdo .dotile:nth-child(n+10),
  .hubnums .numtile:nth-child(n+10){animation-delay:.198s}
}
@keyframes tilein{
  from{opacity:0; transform:translateY(9px) scale(.985)}
  to{opacity:1; transform:none}
}

/* ---- top bar: a shadow only once the page has moved ---- */
.topbar{transition:box-shadow .18s, border-color .18s}
body.scrolled .topbar{box-shadow:0 1px 0 var(--line),
  0 6px 18px -10px rgba(var(--sh-key, 18,17,15), .28)}

/* ---- tabular numerals, so columns of money line up ---- */
.money, .nv, .cv, .price, .scard .pv, td.num, .numcell,
.statv, .bignum, .kpi b{font-variant-numeric:tabular-nums}

/* ==================================================== the modern platform

   Things the browser now does better than the JavaScript that used to do them,
   or that nothing was doing at all. Every one degrades to what the app already
   looked like on a browser that has not caught up, so none of it is a floor. */

/* Headings that break into even lines instead of leaving one orphan word, and
   body copy that never leaves a widow. */
h1, h2, h3, .topbar h1, .secttl, .hubempty b, .empty b,
.dotile .dt b, .numtile .nl{text-wrap:balance}
p, .cap, .numtile .nn, .dotile .dt span{text-wrap:pretty}

/* Scrollbars in the app's own colours, in one line, everywhere — rather than a
   -webkit- pair per element. */
html{scrollbar-color:var(--line) transparent; scrollbar-width:thin}
.rail{scrollbar-width:thin}

/* A scroll inside a panel stops at the panel instead of taking the page with
   it once it reaches the end. */
.drawer, .popmenu, .sheet, .content, .rail, .jumpwrap,
.railnav, .chanlist{overscroll-behavior:contain}

/* Text boxes that grow with what is typed, with no resize handler. */
textarea#scanText, textarea.grow, .notein textarea{field-sizing:content;
  min-height:5.2em; max-height:22em}

/* A long list of trucks is thousands of nodes. The browser can skip laying out
   the ones nobody has scrolled to; the size hint keeps the scrollbar honest. */
.scardgrid > .scard, .hublist > .hubrowlink{
  content-visibility:auto; contain-intrinsic-size:auto 118px}

/* Height animates to `auto` — which it could not until interpolate-size. */
@media (prefers-reduced-motion: no-preference){
  :root{interpolate-size:allow-keywords}
}

/* Menus and drawers get their entrance from the browser rather than from a
   class somebody has to remember to add and take off. */
@starting-style{
  .popmenu{opacity:0; transform:translateY(-6px) scale(.985)}
}
.popmenu{transition:opacity .13s, transform .13s, overlay .13s allow-discrete,
  display .13s allow-discrete}

/* Native controls in the app's accent instead of the operating system's blue. */
:root{accent-color:var(--accent)}

/* The focus ring, once, for everything — and only for the keyboard, so a
   mouse click does not leave a ring behind it. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:6px}

/* A row that contains something wrong should look wrong without JavaScript
   having to put a class on it. */
.aurow:has(> div > .fieldbad:not([hidden])) label{color:var(--bad-2)}
label:has(+ input:required:invalid:not(:placeholder-shown)){color:var(--bad-2)}

/* ---- moving between pages ----
   This app is ordinary links and ordinary page loads — no router, no build.
   The browser will now cross-fade one page into the next by itself, which is
   the single biggest reason a plain multi-page app used to feel less finished
   than a single-page one. The rail is named, so it holds still while the
   content changes underneath it. */
@view-transition{navigation:auto}
.rail{view-transition-name:rail}
.topbar{view-transition-name:topbar}
::view-transition-old(root){animation:vtout .14s ease both}
::view-transition-new(root){animation:vtin .2s cubic-bezier(.2,.7,.3,1) both}
@keyframes vtout{to{opacity:0}}
@keyframes vtin{from{opacity:0; transform:translateY(5px)} to{opacity:1}}
:root[data-motion="off"] ::view-transition-old(root),
:root[data-motion="off"] ::view-transition-new(root){animation:none}
@media (prefers-reduced-motion: reduce){
  ::view-transition-old(root), ::view-transition-new(root){animation:none}
}

/* ---- the three settings added in v83 ---- */

/* How heavy the drawings are. */
:root[data-icons="bold"] .ico svg, :root[data-icons="bold"] .fico svg,
:root[data-icons="bold"] .di svg{stroke-width:2.15}
:root[data-icons="bold"] .railnav a.on .ico svg{stroke-width:2.5}
:root[data-icons="soft"] .railnav .ico{width:26px; height:26px; border-radius:8px;
  background:color-mix(in srgb, var(--accent) 12%, transparent)}
:root[data-icons="soft"] .railnav .ico svg{width:16px; height:16px}
:root[data-icons="soft"] .railnav a.on .ico{background:rgba(255,255,255,.14)}
:root[data-icons="off"] .railnav .ico,
:root[data-icons="off"] .dotile .di{display:none}
:root[data-icons="off"] .railnav a{padding-left:13px}
/* Except where the icon is the only thing there is. */
:root[data-icons="off"][data-rail="mini"] .railnav .ico{display:inline-grid}

/* How loudly the front page says something is wrong. */
:root[data-alerts="plain"] .numtile.bad,
:root[data-alerts="plain"] .numtile.warn{background:var(--panel);
  border-color:var(--line-2)}
:root[data-alerts="plain"] .numtile::before{display:none}
:root[data-alerts="plain"] .numtile{padding-left:16px}
:root[data-alerts="loud"] .numtile.bad{background:var(--bad); border-color:var(--bad)}
:root[data-alerts="loud"] .numtile.warn{background:var(--warn-2);
  border-color:var(--warn-2)}
:root[data-alerts="loud"] .numtile.bad *,
:root[data-alerts="loud"] .numtile.warn *{color:#fff !important}
:root[data-alerts="loud"] .numtile.bad::before,
:root[data-alerts="loud"] .numtile.warn::before{background:rgba(255,255,255,.55)}

/* What happens between one screen and the next. */
:root[data-pageturn="none"] ::view-transition-old(root),
:root[data-pageturn="none"] ::view-transition-new(root){animation:none}
:root[data-pageturn="slide"] ::view-transition-new(root){
  animation:vtslide .26s cubic-bezier(.16,.84,.36,1) both}
@keyframes vtslide{from{opacity:0; transform:translateY(16px)} to{opacity:1}}

/* ---- dropping a backup folder in ---- */
.bkdrop{margin:0 0 4px; display:flex; flex-direction:column; align-items:center;
  gap:7px; padding:20px 18px; text-align:center;
  transition:border-color .15s, background .15s, transform .15s}
.bkdrop .cap{max-width:46ch}
.bkdrop.over{border-color:var(--accent); border-style:solid;
  background:var(--accent-soft); transform:scale(1.005)}

/* ---- the skin picker ----
   Drawn as a wall of little screens rather than a row of chips: the choice is
   what the app will look like, so the control should look like it. */
.skingrid{display:grid; gap:12px; margin:0 0 8px;
  grid-template-columns:repeat(auto-fill, minmax(196px, 1fr))}
.skincard{display:flex; flex-direction:column; align-items:stretch; gap:0;
  padding:0; overflow:hidden; text-align:left; background:var(--panel);
  border:1px solid var(--line-2); border-radius:var(--r-sm); cursor:pointer;
  font:inherit; color:var(--ink); box-shadow:var(--lift);
  transition:border-color .15s, transform .16s cubic-bezier(.2,.7,.3,1), box-shadow .16s}
.skincard:hover{transform:translateY(-2px); box-shadow:var(--lift-2);
  border-color:var(--accent)}
.skincard.on{border-color:var(--accent);
  box-shadow:var(--lift), 0 0 0 2px color-mix(in srgb, var(--accent) 34%, transparent)}
.skinpv{position:relative; display:block; height:74px; width:100%;
  border-bottom:1px solid var(--line-2)}
.skinpv .skinbar{position:absolute; left:9px; top:9px; bottom:9px; width:26px;
  border-radius:4px; opacity:.9}
.skinpv .skindot{position:absolute; left:44px; top:13px; width:20px; height:20px;
  border-radius:6px}
.skinpv .skinline{position:absolute; left:44px; top:41px; right:12px; height:7px;
  border-radius:4px}
.skintx{display:flex; align-items:baseline; gap:8px; padding:11px 13px 0}
.skintx b{font-size:13.5px; font-weight:620; letter-spacing:-.014em}
.skinmood{font-size:9.5px; font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink-3)}
.skinnote{display:block; padding:4px 13px 13px; font-size:11.5px;
  color:var(--ink-3); line-height:1.45}
@media (max-width:760px){ .skingrid{grid-template-columns:repeat(2,minmax(0,1fr))} }

/* Square skins mean square: the rail pill and the chips carry their own radii
   rather than the token, so they stayed rounded while everything else changed. */
:root[data-skin="newsprint"][data-skin] .railnav a,
:root[data-skin="newsprint"][data-skin] .chip,
:root[data-skin="newsprint"][data-skin] .railfind,
:root[data-skin="newsprint"][data-skin] button, :root[data-skin="newsprint"][data-skin] .btn,
:root[data-skin="terminal"][data-skin] .railnav a,
:root[data-skin="terminal"][data-skin] .chip,
:root[data-skin="terminal"][data-skin] .railfind,
:root[data-skin="terminal"][data-skin] button, :root[data-skin="terminal"][data-skin] .btn{border-radius:0}
:root[data-skin="blueprint"][data-skin] .railnav a,
:root[data-skin="blueprint"][data-skin] .chip,
:root[data-skin="blueprint"][data-skin] .railfind,
:root[data-skin="blueprint"][data-skin] button, :root[data-skin="blueprint"][data-skin] .btn,
:root[data-skin="steel"][data-skin] .railnav a,
:root[data-skin="steel"][data-skin] .chip,
:root[data-skin="steel"][data-skin] .railfind,
:root[data-skin="steel"][data-skin] button, :root[data-skin="steel"][data-skin] .btn,
:root[data-skin="ledger"][data-skin] .railnav a,
:root[data-skin="ledger"][data-skin] .chip,
:root[data-skin="ledger"][data-skin] .railfind,
:root[data-skin="ledger"][data-skin] button, :root[data-skin="ledger"][data-skin] .btn{border-radius:3px}

/* ---- selecting many trucks, and the bar that acts on them ---- */
/* Selected has to be unmissable across a grid of sixty. A ring alone was not:
   on the wide row layout it reads as a focus outline, which is a different
   thing that also happens on this screen. Ring, tint and a filled mark. */
.scard.picked{box-shadow:var(--lift), inset 0 0 0 2px var(--accent);
  background:color-mix(in srgb, var(--accent) 9%, var(--panel))}
.scard.picked::after{content:"\2713"; position:absolute; top:9px; right:9px;
  width:19px; height:19px; border-radius:50%; background:var(--accent);
  color:var(--panel); font-size:11px; font-weight:700; line-height:19px;
  text-align:center}
.scard{position:relative}
.scard.here{outline:2px solid color-mix(in srgb, var(--accent) 55%, transparent);
  outline-offset:2px}

.pickbar{position:fixed; left:50%; bottom:24px; transform:translateX(-50%);
  z-index:140; display:flex; align-items:center; gap:9px; padding:9px 11px 9px 15px;
  border-radius:14px; background:var(--panel); color:var(--ink);
  box-shadow:var(--lift-2), 0 0 0 1px var(--line-2);
  animation:pickin .18s cubic-bezier(.16,.84,.36,1) both; max-width:min(96vw, 1180px);
  /* One line. A bar that wraps puts "Clear" under the buttons it belongs
     beside and doubles its own height over the list it is acting on. */
  flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none}
.pickbar::-webkit-scrollbar{display:none}
.pickbar button{flex:0 0 auto}
@keyframes pickin{from{opacity:0; transform:translate(-50%,10px)}
                  to{opacity:1; transform:translate(-50%,0)}}
.pickbar .pn{font-size:16px; font-weight:680; font-variant-numeric:tabular-nums;
  letter-spacing:-.03em}
.pickbar .pw{font-size:12.5px; color:var(--ink-2); margin-right:5px; white-space:nowrap}
.pickbar button{white-space:nowrap}
.pickbar .pinput{width:130px; padding:5px 9px; border-radius:var(--r-xs);
  border:1px solid var(--line); background:var(--panel-2); color:var(--ink);
  font:inherit; font-size:13px}
.pickbar .pmsg{font-size:12px; color:var(--ink-3)}
.pickbar .pmsg.err{color:var(--bad-2)}
.pickbar .pclear{margin-left:4px}
@media (max-width:720px){
  .pickbar{left:12px; right:12px; transform:none; bottom:12px}
  @keyframes pickin{from{opacity:0; transform:translateY(10px)}
                    to{opacity:1; transform:none}}
}

/* ---- what the keys are ---- */
.keysheet{position:fixed; inset:0; z-index:200; display:grid; place-items:center;
  background:color-mix(in srgb, var(--ink) 42%, transparent);
  backdrop-filter:blur(3px)}
.keysheet .kbox{width:min(420px, 92vw); padding:22px 24px 18px;
  background:var(--panel); border-radius:var(--r); box-shadow:var(--lift-2)}
.keysheet h3{margin:0 0 14px; font-size:15px; font-weight:650;
  letter-spacing:-.02em}
.keysheet .krow{display:grid; grid-template-columns:92px 1fr; gap:12px;
  align-items:baseline; padding:5px 0; font-size:13px; color:var(--ink-2)}
.keysheet kbd{font:inherit; font-size:11.5px; font-weight:600; text-align:center;
  padding:3px 0; border-radius:6px; background:var(--panel-2);
  box-shadow:inset 0 0 0 1px var(--line-2); color:var(--ink)}
.keysheet .kbox > button{margin-top:14px}

/* ============================================ the studio front page
   A second composition, not a second palette. The hero is where the app looks
   most like the design boards Greg sent: a deep panel of real colour carrying
   the greeting, the search and the three figures that decide the day. What is
   NOT taken from those boards is the 40-point type everywhere — they are
   drawing screens that hold six items, and this one holds 502. */
html[data-layout="studio"] .topbar h1,
html[data-layout="studio"] .topbar .sub{display:none}

.hubhero{margin-bottom:26px}
/* The masthead, and the one place a skin gets to compose rather than recolour.
   It used to be this exact gradient recipe in every skin, with only --accent
   swapped -- which is precisely what Greg meant by *"all it does is change the
   font and colours"*. Eleven skins, one glossy blob, eleven times. Every part
   of it is a token now, so Newsprint can print a masthead with a black rule
   under it, Ledger can rule a white box, Blueprint can draw a grid, and the
   showy ones keep the gradient because it suits them. */
.hero{position:relative; overflow:hidden; border-radius:var(--hero-r, calc(var(--r) + 8px));
  padding:26px 28px 22px; color:var(--hero-ink, #fff); isolation:isolate;
  border:var(--hero-border, none);
  background:var(--hero,
    radial-gradient(120% 140% at 88% -20%,
      color-mix(in srgb, var(--accent) 62%, #ffffff) 0%, transparent 58%),
    radial-gradient(110% 130% at 4% 118%,
      color-mix(in srgb, var(--accent) 40%, #000000) 0%, transparent 62%),
    linear-gradient(142deg,
      color-mix(in srgb, var(--accent) 88%, #000000),
      color-mix(in srgb, var(--accent) 62%, #000000)));
  box-shadow:var(--hero-shadow, var(--lift-2),
    0 26px 60px -30px color-mix(in srgb, var(--accent) 70%, transparent))}
/* A faint drawn field, so a large area of flat colour has something in it.
   A skin that is not a large area of flat colour turns it off, or draws
   something else entirely -- Blueprint draws its own grid here. */
.hero::before{content:""; position:absolute; inset:0; z-index:-1;
  opacity:var(--hero-field-o, .16);
  background-image:var(--hero-field,
    radial-gradient(circle at 1px 1px,#fff 1px,transparent 0));
  background-size:var(--hero-field-size, 22px 22px)}

.herotop{display:flex; align-items:flex-start; gap:16px}
.herohi{flex:1; min-width:0}
.herohi b{display:block; font-size:29px; font-weight:660; letter-spacing:-.035em;
  line-height:1.08; text-wrap:balance}
.herohi span{display:block; margin-top:5px; font-size:13px;
  color:var(--hero-ink-2, rgba(255,255,255,.72))}
.heroask{flex:none; display:inline-flex; align-items:center; gap:8px;
  padding:8px 14px; border-radius:999px; border:none; cursor:pointer;
  font:inherit; font-size:13px; font-weight:560; color:var(--hero-ink, #fff);
  background:var(--hero-face, rgba(255,255,255,.16));
  box-shadow:inset 0 0 0 1px var(--hero-edge, rgba(255,255,255,.26));
  transition:background .15s}
.heroask:hover{background:var(--hero-face-2, rgba(255,255,255,.26))}
.heroask .ico svg{width:16px; height:16px}

.herofind{display:flex; align-items:center; gap:11px; width:100%; margin-top:20px;
  padding:13px 16px; border-radius:14px; border:none; cursor:pointer; font:inherit;
  text-align:left; color:var(--hero-ink-2, rgba(255,255,255,.9));
  background:var(--hero-face, rgba(255,255,255,.14));
  box-shadow:inset 0 0 0 1px var(--hero-edge, rgba(255,255,255,.2));
  transition:background .15s}
.herofind:hover{background:var(--hero-face-2, rgba(255,255,255,.22))}
.herofind .ftx{flex:1; min-width:0; font-size:14px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.herofind .fkey{flex:none; font-size:10.5px; font-weight:620; padding:2px 7px;
  border-radius:6px; background:var(--hero-key, rgba(0,0,0,.22))}
.herofind .ico svg{width:17px; height:17px}

.herostrip{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:11px;
  margin-top:18px}
.herostat{display:block; padding:14px 16px 13px; border-radius:14px;
  text-decoration:none; color:var(--hero-ink, #fff);
  background:var(--hero-face, rgba(255,255,255,.13));
  box-shadow:inset 0 0 0 1px var(--hero-edge, rgba(255,255,255,.18));
  transition:background .16s, transform .16s cubic-bezier(.2,.7,.3,1)}
.herostat:hover{background:var(--hero-face-2, rgba(255,255,255,.22)); transform:translateY(-2px)}
.herostat b{display:block; font-size:32px; font-weight:670; letter-spacing:-.045em;
  line-height:1; font-variant-numeric:tabular-nums}
.herostat span{display:block; margin-top:6px; font-size:12.5px;
  color:var(--hero-ink-2, rgba(255,255,255,.78)); line-height:1.35}
/* Trouble reads as trouble even inside a coloured panel. */
.herostat.bad b{color:var(--hero-bad, #ffd9d4)}
.herostat.warn b{color:var(--hero-warn, #ffe8b8)}

/* Under the hero the page tightens up, because the rest of it is a list of
   things to do rather than a headline. */
html[data-layout="studio"] .hubdo{gap:12px;
  grid-template-columns:repeat(auto-fill, minmax(246px, 1fr))}
html[data-layout="studio"] .dotile{padding:16px 17px; border-radius:calc(var(--r) + 2px)}
html[data-layout="studio"] .dotile .di{width:38px; height:38px; border-radius:12px}
html[data-layout="studio"] .numtile{padding:13px 15px 14px 18px}
html[data-layout="studio"] .numtile .nv{font-size:25px}

@media (max-width:820px){
  .herostrip{grid-template-columns:repeat(3, minmax(0,1fr)); gap:8px}
  .herostat{padding:11px 12px}
  .herostat b{font-size:24px}
  .herostat span{font-size:11px}
  .herohi b{font-size:23px}
}

/* ==================================================== the phone
   Greg: "we need this to have a mobile ui as well ... shipped as an actual app
   we use daily".

   Below 760 the sidebar goes away entirely. Where you are is a slim bar at the
   top; where you can go is along the bottom, where a thumb reaches. */
@media (max-width:760px){

  /* The seam. `.shell` paints a rail-coloured band 248px wide down the left,
     which is right on a laptop and was still being painted on a phone where
     the rail is not there — a pale stripe down 60% of every screen that looked
     like a rendering fault. The mobile rule for it existed and was outbid by
     the scene rules, which are more specific and come later. */
  :root .shell,
  :root.lit .shell,
  :root.lit[data-theme="light"] .shell,
  :root.lit:not([data-scene="off"]) .shell,
  :root.lit[data-theme="light"]:not([data-scene="off"]) .shell{
    background:none; grid-template-columns:minmax(0,1fr)}

  .rail{display:none}

  .topbar{position:sticky; top:0; padding:0 12px; min-height:52px; height:52px;
    gap:9px; flex-wrap:nowrap; row-gap:0;
    background:color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter:saturate(180%) blur(14px)}
  .topbar h1{font-size:16.5px; letter-spacing:-.026em}
  .topbar > #tabs{margin-left:auto}

  .content{padding-left:14px; padding-right:14px}

  /* Room for the bar, plus whatever the phone's own home indicator wants. */
  body.hastabs .content{padding-bottom:calc(78px + env(safe-area-inset-bottom))}
  body.hastabs .pickbar{bottom:calc(74px + env(safe-area-inset-bottom))}
  body.hastabs #mmtoast{bottom:calc(74px + env(safe-area-inset-bottom))}

  .tabbar{position:fixed; left:0; right:0; bottom:0; z-index:150;
    display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
    padding:6px 4px calc(6px + env(safe-area-inset-bottom));
    background:color-mix(in srgb, var(--panel) 92%, transparent);
    backdrop-filter:saturate(180%) blur(18px);
    box-shadow:0 -1px 0 var(--line-2), 0 -10px 30px -18px rgba(0,0,0,.4)}
  .tabbar .tb{display:flex; flex-direction:column; align-items:center; gap:3px;
    /* 44 is the floor a thumb needs; this is 52 and it is not negotiable
       downwards, whatever the density setting says. */
    min-height:52px; padding:6px 2px 4px; border:none; background:none;
    cursor:pointer; font:inherit; text-decoration:none; color:var(--ink-3);
    border-radius:12px; transition:color .14s, background .14s}
  .tabbar .tb.on{color:var(--accent)}
  .tabbar .tb:active{background:var(--line-3)}
  .tabbar .tbi{display:grid; place-items:center; height:23px}
  .tabbar .tbi svg{width:22px; height:22px}
  .tabbar .tb.on .tbi svg{stroke-width:2}
  .tabbar .tbl{font-size:10.5px; font-weight:560; letter-spacing:-.005em;
    max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

  /* Everything behind More, as a sheet off the bottom rather than a menu in
     the corner — the corner is the one place a thumb cannot get to. */
  .moresheet{position:fixed; inset:0; z-index:190; display:flex;
    align-items:flex-end; background:rgba(0,0,0,.4);
    animation:mfade .16s ease both}
  @keyframes mfade{from{opacity:0}to{opacity:1}}
  .moresheet .mbox{width:100%; background:var(--panel);
    border-radius:20px 20px 0 0; padding:8px 10px calc(14px + env(safe-area-inset-bottom));
    box-shadow:0 -14px 40px -18px rgba(0,0,0,.5);
    animation:mup .2s cubic-bezier(.16,.84,.36,1) both}
  @keyframes mup{from{transform:translateY(16px)}to{transform:none}}
  .moresheet .mgrab{width:38px; height:4px; border-radius:2px; margin:4px auto 10px;
    background:var(--line)}
  .moresheet .mrow{display:flex; align-items:center; gap:14px; width:100%;
    min-height:50px; padding:0 12px; border:none; background:none; font:inherit;
    text-align:left; text-decoration:none; color:var(--ink); border-radius:12px;
    cursor:pointer}
  .moresheet .mrow:active{background:var(--line-3)}
  .moresheet .mrow.on{color:var(--accent)}
  .moresheet .mrow b{font-size:15px; font-weight:540}
  .moresheet .mi{flex:none; display:grid; place-items:center; width:24px;
    color:var(--ink-3)}
  .moresheet .mrow.on .mi{color:var(--accent)}
  .moresheet .mi svg{width:20px; height:20px}

  /* The studio hero at phone width: the greeting stops trying to be a poster
     and the three figures stay on one row, because two rows of them push the
     first job off the screen. */
  .hero{padding:20px 18px 18px; border-radius:var(--r)}
  .herohi b{font-size:22px}
  .herotop{gap:10px}
  .heroask .tbl, .heroask span:not(.ico){font-size:12px}
  .herofind{margin-top:15px; padding:11px 13px}
  .herofind .ftx{font-size:13px}
  .herostrip{gap:7px; margin-top:14px}
  .herostat{padding:10px 10px 9px; border-radius:11px}
  .herostat b{font-size:22px; letter-spacing:-.04em}
  .herostat span{font-size:10.5px; margin-top:3px}

  /* One column for anything that was a grid of cards. Two 180px columns on a
     390 screen is two columns of nothing. */
  .hubdo, .hubnums, .skingrid{grid-template-columns:minmax(0,1fr)}
  html[data-layout="studio"] .hubdo{grid-template-columns:minmax(0,1fr)}
}

@media (max-width:760px){
  /* The studio layout hides the page name because its hero carries the
     greeting. On a phone the hero scrolls away and the bar was left with
     nothing in it but "Change this page", so the name comes back. */
  html[data-layout="studio"] .topbar h1{display:block}

  /* The More tab is a <button> and was picking up the app's button chrome —
     a white pill sitting in the middle of the bar. Nothing in this bar is a
     button-shaped thing. */
  .tabbar button.tb, .tabbar a.tb{background:none; border:none; box-shadow:none;
    -webkit-appearance:none; appearance:none; font-weight:inherit}
  .tabbar button.tb:hover, .tabbar a.tb:hover{background:none;
    border-color:transparent; box-shadow:none}
  .tabbar button.tb:active, .tabbar a.tb:active{background:var(--line-3);
    transform:none}
}

@media (max-width:760px){
  /* The page tabs get their own row.
     They were the last thing in a one-line top bar, so on a phone they were
     pushed off the right edge behind the theme button — "…ales" showing and
     nothing else. Titles, Pricing and Sold were simply unreachable from a
     phone, which is most of what the office does on one. */
  .topbar{height:auto; min-height:52px; flex-wrap:wrap; row-gap:0;
    padding:0 12px 0}
  .topbar > #tabs{order:9; flex:1 0 100%; margin:0 -12px; padding:0 12px 7px;
    max-width:none; overflow-x:auto; scrollbar-width:none;
    /* A fade at the right edge so it is obvious there is more along there. */
    mask-image:linear-gradient(to right, #000 calc(100% - 26px), transparent);
    -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 26px), transparent)}
  .topbar > #tabs::-webkit-scrollbar{display:none}
  .topbar > #tabs button{flex:0 0 auto; min-height:36px}
  .topbar .sp{min-width:0}

  /* The filter chips were made a one-line sideways scroller here and it is
     gone again: `#specChips` holds several rows and forcing nowrap on them
     widened the page to 1028px on a 390px phone. A page that scrolls sideways
     is a worse fault than a chip row that wraps, and the honest fix is fewer
     chips on a phone rather than cleverer overflow. */
  #specChips .chip{font-size:12.5px; padding:6px 10px}
}

/* ---------- what a closed sale says back ------------------------------------
   The market book used to be write-only: a box you pasted a sale into and a
   count of how many rows went in. These three panels are the half that answers
   -- what was buyable, how hot the room was, how far the book's own figure was
   out, and which trucks have been round before. */
.numtile.good .nv{color:var(--pos)}
.numtile.good{border-color:color-mix(in srgb, var(--good) 34%, transparent)}

/* Its own scroller. `table.ut` is 962px wide by design because it holds nine
   columns; this one holds five and must not inherit that, or the panel pushes
   the whole page sideways on a laptop. */
.tblwrap{overflow-x:auto; -webkit-overflow-scrolling:touch;
  border:1px solid var(--line-2); border-radius:var(--r-sm); margin-top:8px}
.chasetbl{border-collapse:collapse; width:100%; font-size:13px; min-width:560px}
.chasetbl th,.chasetbl td{padding:9px 12px; border-bottom:1px solid var(--line-3);
  text-align:left; vertical-align:middle}
.chasetbl tr:last-child td{border-bottom:none}
.chasetbl th{color:var(--ink-3); font-weight:660; font-size:9.5px;
  letter-spacing:.085em; text-transform:uppercase; white-space:nowrap;
  background:var(--panel-2)}
.chasetbl td.n{text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums}
.chasetbl td.room{color:var(--pos); font-weight:640}
.chasetbl td:nth-child(2){color:var(--ink-2)}

.seenrow{display:flex; flex-direction:column; gap:2px; padding:9px 0;
  border-top:1px solid var(--line-3)}
.seenrow:first-of-type{border-top:none}
.seenrow b{font-size:13px; font-weight:620; letter-spacing:-.012em}

/* Each sale in the book, with the button that scores it.
   Named `.bookrow` and not the obvious `.salerow`, because `.salerow` is
   already a row of the sale board four hundred lines up. Reusing it put a
   second, hidden `.salerow button` on the page and the board's own test
   grabbed mine instead -- a selector that had been correct for months
   silently started pointing at a different button. */
.bookrow{display:flex; align-items:center; gap:14px; padding:9px 0;
  border-top:1px solid var(--line-3)}
.bookrow:first-of-type{border-top:none}
.booktx{flex:1; min-width:0; display:flex; flex-direction:column; gap:2px}
.booktx b{font-size:13px; font-weight:620; letter-spacing:-.012em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.bookrow > .btn{flex:none}
@media (max-width:560px){
  .bookrow{flex-direction:column; align-items:stretch; gap:8px}
  .bookrow > .btn{width:100%}
}
/* Why a lot was cheap, in the sale's own words. */
.chasetbl td.why{color:var(--warn-2); font-size:12px}

/* A box that will take a dropped file says so while one is over it. */
.dropping{outline:2px dashed var(--accent); outline-offset:4px;
  background:var(--accent-soft)}

/* A destructive button says so, and says it twice before it does anything. */
button.sm.ghost.warn{color:var(--bad-2)}
button.sm.ghost.warn:hover{border-color:var(--bad); background:var(--warn-bg)}
button.sm.ghost.armed{background:var(--bad); border-color:var(--bad); color:#fff}

/* A row that opens the thing it is about. */
.clickrow{cursor:pointer}
.clickrow:hover{background:var(--line-3)}
