/* Balenton — the front desk.
 *
 * Taken verbatim from mockups/atelier.html. The design was settled there; copying it rather
 * than rebuilding it by eye is the only way the running app and the approved mockup stay the
 * same thing.
 */

/* ══ three modes ═══════════════════════════════════════════
   clair   — office, daylight
   atelier — the shop floor: dimmed, warm, less glare off a tablet under a lift
   sombre  — night, and the back bays
   ═════════════════════════════════════════════════════════ */
:root[data-theme="light"]{
  --ink:#151823;--ink-2:#3d4354;--ink-3:#6b7280;--ink-4:#9aa1ae;
  /* The label half of a detail row, and empty-state text. Used in fourteen places and defined in none until now. */
  --dim:var(--ink-3);
  --line:#e6e8ee;--line-2:#f0f2f6;--bg:#f7f8fa;--panel:#fff;--panel-2:#fcfcfd;
  --rail:#171a26;--rail-ink:#c9cdd8;--rail-hov:#232735;--rail-dim:#767d8e;--rail-line:#262a38;
  /* Neutral on purpose: until the shop row arrives, the accent must not be another
     company's colour. applyShopTheme replaces these, and <head> replays the remembered
     values before the first paint on every visit after the first. Balenton's own purple
     lives in --ba-mark, which the shop colour never touches. */
  --brand:#2b2f3a;--brand-600:#1d2029;--brand-50:#f2f3f6;--brand-100:#e1e4ea;--brand-ink:#3d4354;--brand-on:#ffffff;
  --ok:#0e9f6e;--ok-bg:#e7f6f0;--ok-ink:#0a5c41;
  --warn:#c2740b;--warn-bg:#fdf3e3;--warn-ink:#7c4a06;
  --urgent:#d5342f;--urgent-bg:#fdecec;--urgent-ink:#8d1f1b;
  --info:#2563eb;--info-bg:#e8f0fe;--info-ink:#12356e;
  --off:#b45309;--off-bg:#fef6e7;--off-line:#f6d9a8;
  --shadow:0 1px 2px rgba(20,24,40,.06),0 4px 12px rgba(20,24,40,.05);
}
:root[data-theme="mid"]{
  --ink:#e8eaf0;--ink-2:#b9bec9;--ink-3:#8b919e;--ink-4:#6d7380;
  /* The label half of a detail row, and empty-state text. Used in fourteen places and defined in none until now. */
  --dim:var(--ink-3);
  --line:#3a3f4c;--line-2:#333845;--bg:#23262f;--panel:#2b2f3a;--panel-2:#272b35;
  --rail:#1b1e26;--rail-ink:#b9bec9;--rail-hov:#2b2f3a;--rail-dim:#7a8192;--rail-line:#333845;
  --brand:#c9cdd8;--brand-600:#dfe2e9;--brand-50:#2b2f3a;--brand-100:#363b47;--brand-ink:#e8eaf0;--brand-on:#151823;
  --ok:#3ecf9a;--ok-bg:#1f3d36;--ok-ink:#8ee7c7;
  --warn:#e0a44a;--warn-bg:#3d3222;--warn-ink:#f2cd93;
  --urgent:#f0655f;--urgent-bg:#42262a;--urgent-ink:#ffb3ad;
  --info:#5b9bf8;--info-bg:#23324d;--info-ink:#a8caff;
  --off:#e0a44a;--off-bg:#3a3122;--off-line:#5a4a2c;
  --shadow:0 1px 2px rgba(0,0,0,.28),0 4px 14px rgba(0,0,0,.24);
}
:root[data-theme="dark"]{
  --ink:#eceef4;--ink-2:#b4bac6;--ink-3:#838a98;--ink-4:#636a78;
  /* The label half of a detail row, and empty-state text. Used in fourteen places and defined in none until now. */
  --dim:var(--ink-3);
  --line:#262a35;--line-2:#1f222b;--bg:#0e1015;--panel:#161920;--panel-2:#12151b;
  --rail:#0a0c10;--rail-ink:#b4bac6;--rail-hov:#1c202a;--rail-dim:#6b7280;--rail-line:#1f232d;
  --brand:#c9cdd8;--brand-600:#dfe2e9;--brand-50:#23262f;--brand-100:#2f333d;--brand-ink:#e8eaf0;--brand-on:#151823;
  --ok:#3ecf9a;--ok-bg:#12302a;--ok-ink:#7fe3c2;
  --warn:#e0a44a;--warn-bg:#33291a;--warn-ink:#f0c688;
  --urgent:#f0655f;--urgent-bg:#371d21;--urgent-ink:#ffaba5;
  --info:#5b9bf8;--info-bg:#17263d;--info-ink:#9cc3ff;
  --off:#e0a44a;--off-bg:#2e2718;--off-line:#4a3d22;
  --shadow:0 1px 2px rgba(0,0,0,.5),0 6px 18px rgba(0,0,0,.4);
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{font-family:Inter,system-ui,-apple-system,"Segoe UI",sans-serif;background:var(--bg);color:var(--ink);
  font-size:14px;line-height:1.45;-webkit-font-smoothing:antialiased;display:flex;overflow:hidden;
  transition:background .2s,color .2s}
.r{border-radius:10px}

/* rail */
.rail{width:236px;flex:0 0 236px;background:var(--rail);color:var(--rail-ink);display:flex;flex-direction:column;height:100vh}
.rail-top{padding:18px 16px 14px;display:flex;align-items:center;gap:10px}
/* The purple tile carries the letters BA, and nothing here ever said what colour they are. In
   the rail that went unnoticed: the text inherits .rail's light --rail-ink and reads white on
   purple by accident. On the sign-in card it inherits .signin-card's `color: var(--ink)` —
   near-black on a dark purple gradient — so the mark looked empty, as though BA were sitting
   behind the tile. Stated outright rather than inherited, because this tile appears on three
   surfaces with three different text colours around it. */
.logo{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,var(--ba-mark,#6d28d9),var(--ba-mark-2,#b98cff));
  display:grid;place-items:center;flex:0 0 30px;
  color:#fff;font-weight:800;font-size:12.5px;letter-spacing:.04em;line-height:1}
.logo svg{width:17px;height:17px;stroke:#fff;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
.brand-name{color:#fff;font-weight:600;font-size:14.5px;letter-spacing:-.01em;line-height:1.15}
.brand-sub{font-size:11px;color:var(--rail-dim)}
.nav{padding:6px 10px;flex:1;overflow-y:auto}
.nav-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--rail-dim);padding:14px 8px 6px;font-weight:600}
/* THE RAIL WEARS THE GARAGE'S COLOUR AS ITS STANDARD, not only under the mouse. Every item
   rests on a wash of it, so the menu reads as this garage's own the moment the screen opens
   rather than only while something is being pointed at.
   Three rungs, and they have to stay apart or the colour costs more than it gives:
     rest   8%  — present, legible, not competing with the work
     hover  22% — the mouse has to say something, and it cannot if it matches rest
     on    100% — the page you are actually on is a solid block, unmistakable at a glance
   --brand-rail, not --brand: the rail is near-black, and a dark brand on it is barely a shape;
   shop-theme.js lightens the colour into --brand-rail for exactly this surface.
   The .on rule follows at equal specificity, so hovering the current item keeps its solid block
   rather than dropping back to a tint.
   margin-bottom is 2px rather than 1: once every item carries a background they are bars rather
   than words, and bars need a gap or they read as one slab. */
.nav a{display:flex;align-items:center;gap:10px;padding:8px 9px;border-radius:7px;color:var(--rail-ink);
  text-decoration:none;font-size:13.5px;font-weight:500;margin-bottom:2px;cursor:pointer;
  background:color-mix(in srgb, var(--brand-rail, var(--rail-hov)) 8%, var(--rail))}
.nav a:hover{background:color-mix(in srgb, var(--brand-rail, var(--rail-hov)) 22%, var(--rail));
  color:var(--brand-rail,#fff)}
.nav a.on{background:var(--brand);color:var(--brand-on,#fff)}
.nav a svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:.9}
.nav .count{margin-left:auto;font-size:11px;background:var(--rail-hov);padding:1px 6px;border-radius:20px;font-weight:600}
.nav a.on .count{background:rgba(255,255,255,.22)}
.rail-foot{padding:11px 12px;border-top:1px solid var(--rail-line)}
.who-row{display:flex;align-items:center;gap:9px;cursor:pointer;padding:5px;border-radius:7px}
.who-row:hover{background:var(--rail-hov)}
.avatar{width:28px;height:28px;border-radius:50%;background:#3b415a;color:#fff;display:grid;place-items:center;
  font-size:11px;font-weight:700;flex:0 0 28px}
.avatar.mech{background:#0e5c48}
.who-row .who{font-size:12.5px;color:#fff;font-weight:500;line-height:1.2}
.who-row .role{font-size:11px;color:var(--rail-dim)}
.switch-hint{font-size:10.5px;color:var(--rail-dim);padding:4px 5px 0}

/* main */
.main{flex:1;display:flex;flex-direction:column;min-width:0;height:100vh}
.top{background:var(--panel);border-bottom:1px solid var(--line);padding:0 20px;
  display:flex;align-items:center;gap:12px;height:58px;flex:0 0 58px}
h1{font-size:17px;font-weight:650;letter-spacing:-.015em;white-space:nowrap}
.today-pill{font-size:12.5px;font-weight:600;padding:5px 11px;border-radius:7px;background:var(--brand-50);
  color:var(--brand-ink);white-space:nowrap}
.spacer{flex:1}
.btn{padding:7px 13px;border-radius:8px;border:1px solid var(--line);background:var(--panel);font-family:inherit;
  font-size:13px;font-weight:550;cursor:pointer;color:var(--ink-2);display:flex;align-items:center;gap:6px;white-space:nowrap}
.btn:hover{background:var(--line-2)}
.btn-primary{background:var(--brand);border-color:var(--brand-line, var(--brand));color:var(--brand-on,#fff)}
.btn-primary:hover{background:var(--brand-600)}
.btn-stop{background:var(--urgent);border-color:var(--urgent);color:#fff}
.btn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round}
.btn:disabled{opacity:.45;cursor:not-allowed}
.seg{display:flex;border:1px solid var(--line);border-radius:7px;overflow:hidden}
.seg button{padding:5px 9px;border:0;background:var(--panel);font-family:inherit;font-size:11.5px;font-weight:650;
  cursor:pointer;color:var(--ink-3)}
.seg button.on{background:var(--ink);color:var(--panel)}
.seg button svg{width:13px;height:13px;stroke:currentColor;stroke-width:2;fill:none;display:block}
.net{display:flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:8px;padding:5px 10px;
  cursor:pointer;font-size:12.5px;font-weight:600;color:var(--ok);background:var(--panel)}
.net .led{width:7px;height:7px;border-radius:50%;background:var(--ok)}
.net.off{color:var(--off);border-color:var(--off-line);background:var(--off-bg)}
.net.off .led{background:var(--off)}

.notice{padding:10px 20px;font-size:12.5px;display:flex;align-items:center;gap:9px;
  background:var(--brand-50);border-bottom:1px solid var(--brand-100);color:var(--brand-ink)}
.notice svg{width:15px;height:15px;stroke:var(--brand);stroke-width:2;fill:none;flex:0 0 15px}
.notice b{font-weight:650}
.notice.offline{background:var(--off-bg);border-bottom-color:var(--off-line);color:var(--warn-ink)}
.notice.offline svg{stroke:var(--off)}
.q-chip{margin-left:auto;background:var(--panel);border:1px solid var(--off-line);border-radius:20px;
  padding:2px 10px;font-weight:650;font-size:11.5px;color:var(--off)}

.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);border-bottom:1px solid var(--line)}
.stat{background:var(--panel);padding:13px 20px}
.stat .k{font-size:11px;color:var(--ink-3);font-weight:600;text-transform:uppercase;letter-spacing:.05em}
.stat .v{font-size:23px;font-weight:680;letter-spacing:-.03em;margin-top:2px;display:flex;align-items:baseline;gap:7px}
.stat .v small{font-size:12px;font-weight:550;color:var(--ink-3);letter-spacing:0}
.bar{height:4px;background:var(--line-2);border-radius:3px;margin-top:7px;overflow:hidden;max-width:180px}
.bar i{display:block;height:100%;background:var(--brand);border-radius:3px}

.wrap{flex:1;overflow:auto;padding:16px 20px 26px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow);overflow:hidden}
.board{min-width:1060px}
.card-head{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--line)}
.card-head h2{font-size:14px;font-weight:650}
.card-head .hint{font-size:12px;color:var(--ink-3)}
.hours{display:grid;grid-template-columns:120px repeat(11,1fr);border-bottom:1px solid var(--line);
  background:var(--panel-2);position:sticky;top:0;z-index:3}
.hours div{padding:6px 0 6px 7px;font-size:11px;color:var(--ink-3);font-weight:600;border-left:1px solid var(--line-2)}
.hours div:first-child{border-left:0}
.row{display:grid;grid-template-columns:120px repeat(11,1fr);border-bottom:1px solid var(--line-2);min-height:48px}
.row:last-child{border-bottom:0}
.bay{padding:0 12px;display:flex;align-items:center;gap:8px;border-right:1px solid var(--line);
  position:sticky;left:0;background:var(--panel);z-index:2}
.bay-n{width:22px;height:22px;border-radius:6px;background:var(--line-2);color:var(--ink-2);
  display:grid;place-items:center;font-size:11px;font-weight:700;flex:0 0 22px}
.bay-name{font-size:12.5px;font-weight:550;white-space:nowrap}
.bay-note{font-size:10.5px;color:var(--ink-4)}
.lane{grid-column:2 / -1;position:relative;display:grid;grid-template-columns:repeat(11,1fr)}
.lane i{border-left:1px solid var(--line-2)}
.job{position:absolute;top:5px;bottom:5px;border-radius:7px;padding:4px 8px;cursor:pointer;overflow:hidden;
  border-left:3px solid;display:flex;flex-direction:column;justify-content:center;transition:transform .08s,box-shadow .08s}
.job:hover{transform:translateY(-1px);box-shadow:0 3px 10px rgba(0,0,0,.18);z-index:4}
.job .t{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}
.job .s{font-size:10.5px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  display:flex;gap:5px;align-items:center}
.job .hrs{font-weight:700;opacity:.95}
.j-prog{background:var(--info-bg);border-color:var(--info);color:var(--info-ink)}
.j-wait{background:var(--warn-bg);border-color:var(--warn);color:var(--warn-ink)}
.j-done{background:var(--ok-bg);border-color:var(--ok);color:var(--ok-ink)}
.j-book{background:var(--line-2);border-color:var(--ink-4);color:var(--ink-2)}
.job.mine{box-shadow:inset 0 0 0 2px var(--ok)}
.job .over{position:absolute;right:5px;top:4px;font-size:9.5px;font-weight:800;background:var(--urgent);
  color:#fff;border-radius:4px;padding:0 4px}
.now{position:absolute;top:0;bottom:0;width:2px;background:var(--urgent);z-index:5;pointer-events:none}
.now::before{content:'';position:absolute;top:-1px;left:-4px;width:10px;height:10px;border-radius:50%;background:var(--urgent)}

.req{display:flex;align-items:center;gap:12px;padding:10px 16px;border-bottom:1px solid var(--line-2)}
.req:last-child{border-bottom:0}
.req .time{font-size:12px;font-weight:650;width:52px;color:var(--ink-2)}
.req .who{font-size:13px;font-weight:550}
.req .car{font-size:12px;color:var(--ink-3)}
.req .svc{font-size:12px;color:var(--ink-2);margin-left:auto;margin-right:10px}
.src{font-size:10.5px;padding:2px 7px;border-radius:20px;background:var(--brand-50);color:var(--brand-ink);font-weight:650}
.src.cal{background:var(--ok-bg);color:var(--ok-ink)}
.src.q{background:var(--off-bg);color:var(--off)}

/* mechanic */
.mywrap{flex:1;overflow:auto;padding:18px 20px 26px;max-width:880px}
.clockbar{background:linear-gradient(135deg,#0e5c48,#127a5f);color:#fff;border-radius:10px;
  padding:16px 18px;display:flex;align-items:center;gap:16px;margin-bottom:16px;box-shadow:var(--shadow)}
.clockbar .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.06em;opacity:.8;font-weight:650}
.clockbar .nm{font-size:16px;font-weight:650;margin-top:2px}
.clockbar .car{font-size:12.5px;opacity:.85}
.elapsed{margin-left:auto;text-align:right}
.elapsed .t{font-size:27px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.elapsed .l{font-size:11px;opacity:.8}
.clockbar.idle{background:var(--line-2);color:var(--ink-2)}
.jobcard{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px 16px;
  margin-bottom:10px;display:flex;align-items:center;gap:13px;box-shadow:var(--shadow)}
.jobcard.run{border-color:var(--ok)}
.jobcard .no{font-size:11.5px;color:var(--ink-3);font-weight:650}
.jobcard .cl{font-size:14px;font-weight:600;margin-top:1px}
.jobcard .cr{font-size:12.5px;color:var(--ink-3)}
.jobcard .cm{font-size:12.5px;color:var(--ink-2);font-style:italic;margin-top:4px}
.jobcard .rt{margin-left:auto;text-align:right;display:flex;flex-direction:column;gap:5px;align-items:flex-end}
.chip{font-size:11px;font-weight:650;padding:2px 9px;border-radius:20px;white-space:nowrap}
.c-bay{background:var(--line-2);color:var(--ink-2)}
.c-run{background:var(--ok-bg);color:var(--ok-ink)}
.c-off{background:var(--off-bg);color:var(--off)}
.c-hrs{background:var(--brand-50);color:var(--brand-ink)}
.c-over{background:var(--urgent-bg);color:var(--urgent-ink)}
.c-msg{background:var(--info-bg);color:var(--info-ink)}

/* drawer */
.scrim{position:fixed;inset:0;background:rgba(5,7,12,.5);opacity:0;pointer-events:none;transition:opacity .18s;z-index:40}
.scrim.on{opacity:1;pointer-events:auto}
.drawer{position:fixed;top:0;right:0;bottom:0;width:508px;max-width:95vw;background:var(--panel);z-index:50;
  transform:translateX(100%);transition:transform .22s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;
  box-shadow:-8px 0 30px rgba(0,0,0,.28)}
.drawer.on{transform:none}
.d-head{padding:15px 18px;border-bottom:1px solid var(--line);display:flex;align-items:flex-start;gap:12px}
.d-head h3{font-size:16px;font-weight:650;letter-spacing:-.01em}
.d-head .sub{font-size:12.5px;color:var(--ink-3);margin-top:2px}
.icon-btn{width:28px;height:28px;border:1px solid var(--line);background:var(--panel);border-radius:7px;
  display:grid;place-items:center;cursor:pointer;color:var(--ink-2);flex:0 0 28px}
.icon-btn:hover{background:var(--line-2)}
.icon-btn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
.tabs{display:flex;border-bottom:1px solid var(--line);padding:0 18px;gap:18px;background:var(--panel)}
.tabs button{border:0;background:none;font-family:inherit;font-size:13px;font-weight:600;color:var(--ink-3);
  padding:10px 0;cursor:pointer;border-bottom:2px solid transparent}
.tabs button.on{color:var(--brand);border-bottom-color:var(--brand)}
.tabs .badge{background:var(--urgent);color:#fff;font-size:10px;border-radius:20px;padding:0 5px;margin-left:5px;font-weight:700}
.d-body{flex:1;overflow-y:auto;padding:16px 18px 24px}
.sec{margin-bottom:18px}
.sec h4{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);font-weight:650;margin-bottom:9px}
.kv{display:grid;grid-template-columns:132px 1fr;gap:5px 10px;font-size:13px}
.kv dt{color:var(--ink-3)}
.kv dd{font-weight:500}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:650;padding:2px 9px;border-radius:20px}
.p-prog{background:var(--info-bg);color:var(--info-ink)} .p-wait{background:var(--warn-bg);color:var(--warn-ink)}
.p-done{background:var(--ok-bg);color:var(--ok-ink)}     .p-book{background:var(--line-2);color:var(--ink-2)}
.quote{background:var(--panel-2);border:1px solid var(--line);border-left:3px solid var(--brand-line, var(--brand));border-radius:7px;
  padding:10px 12px;font-size:13px;line-height:1.5}
.quote .lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-3);font-weight:650;margin-bottom:3px}
.insp{border:1px solid var(--line);border-radius:8px;overflow:hidden}
.insp-row{display:flex;align-items:center;gap:9px;padding:8px 11px;border-bottom:1px solid var(--line-2);font-size:12.5px}
.insp-row:last-child{border-bottom:0}
.dot{width:8px;height:8px;border-radius:50%;flex:0 0 8px}
.d-ok{background:var(--ok)} .d-att{background:var(--warn)} .d-urg{background:var(--urgent)}
.insp-row .m{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--ink-3);font-size:12px}
.rec{border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin-bottom:7px}
.rec .rtop{display:flex;align-items:center;gap:8px;margin-bottom:3px}
.rec .d{font-size:13px;font-weight:550}
.rec .p{margin-left:auto;font-weight:650;font-variant-numeric:tabular-nums}
.rec .why{font-size:12px;color:var(--ink-3)}
.u-now{background:var(--urgent-bg);color:var(--urgent-ink)} .u-soon{background:var(--warn-bg);color:var(--warn-ink)}
.u-mon{background:var(--line-2);color:var(--ink-2)}
.d-foot{padding:12px 18px;border-top:1px solid var(--line);display:flex;gap:9px;background:var(--panel-2)}
.lock{display:flex;gap:9px;align-items:flex-start;background:var(--brand-50);border:1px solid var(--brand-100);
  border-radius:8px;padding:10px 12px;font-size:12.5px;color:var(--brand-ink);line-height:1.45}
.lock svg{width:15px;height:15px;stroke:var(--brand);stroke-width:2;fill:none;flex:0 0 15px;margin-top:1px}
.lock.amber{background:var(--off-bg);border-color:var(--off-line);color:var(--warn-ink)}
.lock.amber svg{stroke:var(--off)}

/* thread */
.thread{display:flex;flex-direction:column;gap:10px}
.msg{max-width:83%;padding:9px 12px;border-radius:12px;font-size:13px;line-height:1.45}
.msg .meta{font-size:10.5px;opacity:.72;margin-top:4px}
.m-shop{align-self:flex-start;background:var(--line-2);border-bottom-left-radius:4px}
.m-cli{align-self:flex-end;background:var(--brand);color:var(--brand-on,#fff);border-bottom-right-radius:4px}
.m-sys{align-self:center;background:transparent;color:var(--ink-3);font-size:11.5px;text-align:center;padding:2px}
.photo{width:100%;height:132px;border-radius:8px;margin-bottom:7px;position:relative;overflow:hidden;
  background:linear-gradient(135deg,#4a4f5c,#2b2f3a);display:grid;place-items:center}
.photo span{font-size:11px;color:rgba(255,255,255,.62);font-weight:600}
.photo svg{width:26px;height:26px;stroke:rgba(255,255,255,.4);stroke-width:1.6;fill:none}
.askcard{border:1px solid var(--warn);background:var(--warn-bg);border-radius:10px;padding:11px 12px;margin-top:7px}
.askcard .q{font-size:12.5px;font-weight:600;color:var(--warn-ink)}
.askcard .pr{font-size:17px;font-weight:700;color:var(--warn-ink);margin:3px 0 8px;font-variant-numeric:tabular-nums}
.askcard .acts{display:flex;gap:7px}
.askcard .acts .btn{flex:1;justify-content:center;font-size:12.5px;padding:6px}
.decided{background:var(--ok-bg);border-color:var(--ok)}
.decided .q,.decided .pr{color:var(--ok-ink)}
.composer{padding:11px 18px;border-top:1px solid var(--line);background:var(--panel-2);display:flex;gap:8px;align-items:center}
.composer input{flex:1;padding:9px 12px;border:1px solid var(--line);border-radius:20px;font-family:inherit;
  font-size:13px;background:var(--panel);color:var(--ink);outline:none}
.composer input:focus{border-color:var(--brand-line, var(--brand))}
.hide{display:none!important}

/* ── sign in ────────────────────────────────────────────────────────────────
   Not in the mockup, which starts already signed in. Built from the same tokens so it belongs to
   the same product rather than looking like a different one bolted on the front. */
/* Two rows, not two centred children: the card keeps the whole 1fr row and stays optically
   centred, while the attribution sits on the floor of the viewport. With a bare place-items the
   two would have split the height between them and the card would have ridden high. */
.signin-wrap {
  min-height: 100dvh; display: grid; grid-template-rows: 1fr auto;
  place-items: center; padding: 24px;
}

/* Whose software this is.
   When somebody opens app.balaut.com/<slug>, atelier.js replaces the card's h1 with the
   GARAGE's name — so without this line the product's own name appears nowhere on the screen a
   person is standing in front of. Faint on purpose: the garage's name leads and this only has
   to be findable. The legal name in full, because that is what an attribution is for. */
.bal-by {
  font-size: 11px; line-height: 1.4; letter-spacing: .02em;
  color: var(--ink-4); text-align: center;
}
.signin-wrap > .bal-by { padding-top: 20px; align-self: end; }

/* ── the shop window, as a column ────────────────────────────────────────────
   A flex sibling of .rail and .main, not an overlay. .main is flex:1, so this simply takes 340px
   and the work reflows into what is left — which is the difference between a panel that is part
   of the screen and one that sits on top of it hiding the board.
   Below 1200px it stops being a column and becomes the screen — see the breakpoint at the foot
   of this block. A narrow screen has no width to give away, and squeezing a 1060px bay board to
   make room for a feed would be the wrong trade. */
.vitrine {
  width: 340px; flex: 0 0 340px; height: 100vh; overflow: hidden;
  background: var(--panel); border-left: 1px solid var(--line);
  display: flex; flex-direction: column;
}
/* Taking 340px out of .main leaves the top bar crowded on a laptop: the first render wrapped
   "En ligne" onto two lines and clipped the FR/EN toggle. The header gives up its two least
   load-bearing pieces instead — the date, which the datePill repeats from a calendar everybody
   has, and the theme switch, which is set once and never touched. Only while the column is
   open, and only when the width actually calls for it. */
body.vit-open .top .today-pill,
body.vit-open .top #themeSeg { display: none }
@media (min-width: 1700px) {
  body.vit-open .top .today-pill,
  body.vit-open .top #themeSeg { display: flex }
}
.vitrine .vit-head {
  display: flex; align-items: center; gap: 10px; padding: 0 14px;
  height: 58px; flex: 0 0 58px; border-bottom: 1px solid var(--line);
  font-weight: 700; font-size: 15px;
}
.vitrine .vit-head .sp { flex: 1 }
.vitrine .vit-x {
  border: 1px solid var(--line); background: var(--panel); border-radius: 8px;
  width: 28px; height: 28px; display: grid; place-items: center; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--ink-3);
}
.vitrine .vit-x:hover { border-color: var(--brand-line, var(--brand)); color: var(--ink); }
/* The feed fills the column to the floor. It used to be a grid of auto-height rows with the
   frame given a hardcoded 620px, which left a slab of empty panel below it on any screen taller
   than that — and a number in the markup that could only ever be right on one display.
   Now the chips take the height they need and the frame takes everything else. min-height:0 on
   both the body and the frame is the part that is easy to miss: a flex child's default
   min-height is auto, so without it an iframe refuses to shrink below its content and pushes
   the column taller than the window instead of fitting inside it. */
.vitrine .vit-body {
  padding: 13px 14px; flex: 1; min-height: 0;
  display: flex; flex-direction: column; gap: 12px;
}
.vitrine .vit-chips { display: flex; gap: 7px; flex-wrap: wrap; flex: 0 0 auto; }
.vitrine .vit-frame { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 8px; }
.vitrine .vit-frame iframe { flex: 1; min-height: 0; }
.vitrine .vit-frame > a { flex: 0 0 auto; }
/* ── the shop window on a phone ──────────────────────────────────────────────
   This used to be `display: none !important` below 1200px, which made the control dead rather
   than absent: the Vitrine link stays in the rail at every width, so tapping it set the panel
   visible and this rule took it straight back away. Nothing happened, with no error — the worst
   of the three possible behaviours, and exactly what got reported.

   So at narrow widths the column becomes a sheet over the whole viewport. There is no width to
   share, so it stops trying to share: the feed gets the screen while it is open, and the × in
   .vit-head is the way back to the board.

   Note what is NOT declared here: `display`. The panel is opened and closed by its `hidden`
   attribute, and line 425's `[hidden] { display: none !important }` is what enforces that. Set
   a display here and a closed Vitrine would cover the desk permanently. The base rule's
   display:flex applies when it is open, [hidden] wins when it is not, and this rule only ever
   changes where it sits.

   100dvh after 100vh for iOS: 100vh there is the viewport with the URL bar retracted, so the
   foot of the sheet — and the close button's breathing room — ends up under the browser chrome. */
@media (max-width: 1200px) {
  .vitrine {
    position: fixed; inset: 0; z-index: 80;
    width: auto; flex: none;
    height: 100vh; height: 100dvh;
    border-left: 0;
  }
  /* A touch target at a thumb's scale, and far enough from the edge to miss the swipe-back gutter. */
  .vitrine .vit-x { width: 36px; height: 36px; font-size: 18px; }
  .vitrine .vit-head { height: 54px; flex: 0 0 54px; padding: 0 12px; }
  .vitrine .vit-body { padding: 11px 12px; }
}

/* Signed in: a bar on the floor of the window, under the scrolling view, whose rule continues
   the rail's own foot rule across the screen as one unbroken line.
   The height is NOT a number written here. .rail-foot has to clear a 28px avatar and grows
   again when the shop picker appears for somebody who works at two garages, so --footbar is
   measured from it on every render by syncFootbar() in atelier-ui.js. The fallback is the
   height that rail measures today, so a browser without ResizeObserver still lands on it. */
.main > .bal-by {
  flex: 0 0 auto;
  height: var(--footbar, 64px);
  display: flex; align-items: center; justify-content: center;
  border-top: 1px solid var(--line);
  background: var(--panel);
}
.signin-card {
  width: 100%; max-width: 400px; text-align: center;
  /* --panel, not --card. `--card` is not defined in this stylesheet and never has been, so the
     fallback made this white in every theme while the text inside followed the theme — white
     on white in the dark ones. */
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 18px; padding: 32px 28px;
  box-shadow: 0 1px 2px rgba(0,0,0,.05), 0 18px 48px rgba(0,0,0,.14);
}
/* Larger here than in the rail: this is the one screen whose job is to say what this software
   is. 46px matches the max-height a shop's own logo gets when atelier.js swaps the tile out for
   an uploaded image, so a garage with a logo and one without produce the same card height. */
.signin-card .logo {
  margin: 0 auto 16px; width: 46px; height: 46px; flex-basis: 46px;
  border-radius: 13px; font-size: 18px;
}
.signin-card h1 { font-size: 21px; font-weight: 820; color: var(--ink); margin: 0 0 7px; letter-spacing: -.4px; }
.signin-card p { font-size: 13.5px; color: var(--ink-3); margin: 0 0 20px; line-height: 1.5; }
.signin-card form { display: grid; gap: 11px; margin-top: 16px; }
.signin-card input {
  font: inherit; width: 100%; padding: 13px 14px; border-radius: 11px;
  border: 1px solid var(--line, #e6e2ec); background: var(--bg, #fbfafc); color: inherit;
}
.signin-card input:focus { outline: none; border-color: var(--brand-line, var(--brand)); }
.signin-card button { width: 100%; justify-content: center; padding: 13px; }
.signin-card #signinMsg { margin-top: 14px; font-size: 13.5px; min-height: 20px; line-height: 1.45; }
.signin-card #signinMsg.bad { color: #b42318; }
.signin-card #signinMsg.ok  { color: #0e8a4f; }

/* The hidden attribute must win over any display rule set later, or a "hidden" step renders anyway. */
[hidden] { display: none !important; }

/* The sign-in screen and the app shell are wrapped in containers the mockup did not have, because
   the mockup starts already signed in. body is display:flex and expects .rail and .main as its own
   children, so the wrapper must not take part in layout at all. */
#shell { display: contents; }
/* Both doors, not only one. body is display:flex, so a wrapper with no flex:1 is sized to its
   own contents — and #onboard never had this rule, so the invitation screen has been rendering
   in a 420px column jammed against the left edge of the window while the sign-in card beside
   it centred correctly. It is the first thing a new member ever sees of this product. */
#signin:not([hidden]), #onboard:not([hidden]) { flex: 1; }

/* The vehicle render inside a list row.
   NOT .car — the mockup already uses that class for the vehicle NAME text in a .req row, so an
   <img class="car"> inherits text styling and no dimensions, and renders at its natural size. */
.carimg { width: 92px; height: 56px; object-fit: contain; display: block; }

/* Who is on the bay. Sits beside the bay name rather than under it, so a twelve-row board does
   not grow a second line per bay. */
.bay-mech {
  display: inline-grid; place-items: center; margin-left: 7px;
  width: 19px; height: 19px; border-radius: 50%;
  background: var(--brand-50); color: var(--brand-ink, var(--brand));
  font-size: 9.5px; font-weight: 800; letter-spacing: .2px; vertical-align: middle;
}

/* The BA lettermark is Balenton's, in Balenton's colour, whatever the shop's palette is. The
   printed invoice follows the same rule in reverse: it carries the garage's identity and none of
   ours. */
.logo, .signin-card .logo { background: linear-gradient(135deg, var(--ba-mark, #6d28d9), var(--ba-mark-2, #b98cff)) !important; }

/* …unless the garage has a logo of its own, in which case the tile is not a tile at all — the
   shop's mark stands on the card by itself. Needs its own !important only because the rule it
   is cancelling has one; and the fixed 30/46px box has to go too, or a wide logo is squeezed
   into a square. This replaces two inline styles that could never have worked. */
.logo.shoplogo, .signin-card .logo.shoplogo {
  background: none !important; box-shadow: none;
  width: auto; height: auto; flex-basis: auto; border-radius: 0;
  max-width: 100%;
}
.btn-primary { color: var(--brand-on, #fff); }

/* A job against its clock. Deliberately a left border and a tint rather than a wholly different
   fill: the status colour still has to read, and a board where twelve blocks are all shouting is a
   board nobody looks at. */
.job.j-soon { box-shadow: inset 3px 0 0 var(--warn); }
.job.j-late { box-shadow: inset 3px 0 0 var(--urgent); background: var(--urgent-bg); }
.job.j-late .t { color: var(--urgent-ink); }
.job .over { background: var(--urgent); color: #fff; }

/* Bay state.
   A dot and a tinted left edge, not a filled row: twelve saturated rows is a board nobody reads,
   and the job blocks inside still have to be legible. */
.bay-dot { display:inline-block; width:7px; height:7px; border-radius:50%;
           margin-right:6px; vertical-align:middle; background: var(--ink-4); }
.row.bay-free   .bay { box-shadow: inset 3px 0 0 var(--ok); }
.row.bay-free   .bay-dot { background: var(--ok); }
.row.bay-busy   .bay { box-shadow: inset 3px 0 0 var(--info); }
.row.bay-busy   .bay-dot { background: var(--info); }
.row.bay-late   .bay { box-shadow: inset 3px 0 0 var(--urgent); background: var(--urgent-bg); }
.row.bay-late   .bay-dot { background: var(--urgent); }
.row.bay-late   .bay-name { color: var(--urgent-ink); }
/* Closed is drawn down rather than up: it is not an alarm, it is absence. */
.row.bay-closed .bay { opacity: .55; }
.row.bay-closed .bay-dot { background: var(--ink-4); }
.row.bay-closed .lane { background-image: repeating-linear-gradient(
    45deg, transparent, transparent 7px, var(--line-2) 7px, var(--line-2) 14px); }

/* ── the rail's surface ───────────────────────────────────────────────────────
   The sidebar has a lot of dead space below the menu, and it is the one surface on screen all
   day. It carries the garage's colour as diagonal lines.

   It used to be a sheet of wrenches, nuts and sockets. That is gone: at the faint opacity it
   needed to stay out of the way it was invisible, and at the strength that made the colour
   legible it read as clip-art behind the navigation. Lines carry the same colour, say nothing,
   and never compete with a label.

   It lives on a pseudo-element, NOT on .nav itself. A mask applied to .nav would fade the menu
   labels along with the pattern — a mask affects the element's content, not just its background.
   Worth keeping written down: half-transparent navigation text looks like a rendering bug rather
   than a decision. */
.nav { position: relative; }
.nav::before {
  content: '';
  position: absolute;
  inset: 0;
  /* DIAGONAL LINES IN THE GARAGE'S OWN COLOUR. They replace a repeating sheet of wrenches and
     nuts, which at full strength read as clip-art on the one surface that is on screen all day.
     Lines carry the colour just as well, say nothing, and never compete with a menu label.

     Drawn as a gradient rather than an image, so there is no file to fetch, nothing to go stale
     and nothing that can silently fail to parse — the tool sheet did exactly that once, and a
     mask that cannot load leaves no trace at all.

     16px of gap to 3px of line: thin enough to read as texture at a glance, wide enough apart
     that the rail still reads as a dark surface rather than a striped one. 135deg runs them with
     the eye's travel down the rail instead of against it. */
  background: repeating-linear-gradient(
    135deg,
    transparent 0 18px,
    color-mix(in srgb, var(--brand-rail, #fff) 42%, transparent) 18px 21px);
  /* THE FOOT ONLY. Measured from the bottom rather than as a percentage of the rail: a
     percentage puts the lines behind the menu on a tall screen and crowds them into nothing on a
     short one, because it scales with the rail instead of with the empty space under the last
     item. From the bottom, the band is the same size on every screen and always lands in the gap
     the menu does not use. Solid for the last 80px, gone by 240px up. */
  -webkit-mask-image: linear-gradient(to top, #000 0 80px, transparent 240px);
  mask-image: linear-gradient(to top, #000 0 80px, transparent 240px);
  pointer-events: none;
  z-index: 0;
}
/* The menu sits above it and stays fully opaque. */
.nav > * { position: relative; z-index: 1; }

/* Sign out, at the foot of the rail beside whoever is signed in. Quiet until hovered — it is
   needed daily on a shared counter machine but should not compete with the navigation. */
.who-row { display: flex; align-items: center; gap: 10px; }
.rail-out {
  margin-left: auto; flex: 0 0 auto;
  background: transparent; border: 0; border-radius: 7px; padding: 6px;
  color: var(--rail-dim); cursor: pointer; line-height: 0;
}
.rail-out svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
                stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.rail-out:hover { background: var(--rail-hov); color: #fff; }
.who-row .who { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ───────────────────────────────────────────────────────────────────────────
   Entry forms.

   A counter form is not a stack of boxes. The person filling it in is talking to
   somebody across a desk, so it has to be readable at a glance, grouped the way the
   conversation goes — who they are, how to reach them, what they drive — and it has to
   say which fields actually matter. Labels sit ABOVE the field, not beside it: a label
   column forces every field onto its own line, which is what turned the first version of
   this into a ribbon of inputs two screens long.

   Shared by the desk, the tablet and the portal. Same class names in all three, because
   they share token names and a form is a form.
   ─────────────────────────────────────────────────────────────────────────── */
.fform { display: block; }

.fsec { padding: 16px 18px; border-bottom: 1px solid var(--line-2, var(--line)); }
.fsec:last-of-type { border-bottom: 0; }
.fsec > h4 {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3); font-weight: 700; margin: 0 0 3px;
}
.fsec > .fnote { font-size: 12px; color: var(--ink-3); margin: 0 0 12px; line-height: 1.45; }
.fsec > h4 + .fgrid { margin-top: 12px; }

/* Three tracks at desk width, two on a tablet, one on a phone. Fields declare how many
   they want to take rather than being positioned, so reordering a form cannot break it. */
.fgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 14px; }
.fld { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.fld.w2 { grid-column: span 2; }
.fld.w3 { grid-column: span 3; }
/* The span was tied to .fld, so anything else asking for the full width — a chip list, a block
   of day rows — silently took one column of three and wrapped inside it. Width is a property of
   the grid child, not of being a field, so the rule belongs on any direct child that asks. */
.fgrid > .w2 { grid-column: span 2; }
.fgrid > .w3 { grid-column: span 3; }

.fld > label {
  font-size: 11.5px; font-weight: 650; color: var(--ink-3);
  display: flex; align-items: center; gap: 5px;
}
.fld.need > label::after {
  content: '•'; color: var(--brand); font-size: 15px; line-height: 0; margin-top: 2px;
}

.fld input, .fld select, .fld textarea {
  width: 100%; font: inherit; font-size: 13.5px; padding: 9px 11px;
  border-radius: 9px; border: 1px solid var(--line);
  background: var(--panel, #fff); color: var(--ink);
  min-width: 0; /* or a long placeholder blows the grid track out */
}
.fld input:focus, .fld select:focus, .fld textarea:focus {
  outline: 0; border-color: var(--brand-line, var(--brand)); box-shadow: 0 0 0 3px var(--brand-50);
}
.fld textarea { resize: vertical; min-height: 68px; line-height: 1.5; }

/* ── the setup form's two odd shapes ──────────────────────────────────────────
   Sixty-three services and eight vehicle categories are a bad fit for the three-track grid: as
   checkboxes they would be a wall three columns deep, and the eye has no way to scan it. As
   chips they wrap to the text, so the list is as tall as it needs to be and no taller, and the
   ticked ones are visible at a glance from across the row rather than by reading each label. */
/* The colour control: the picker, the hex beside it, and a line saying where it lands. Two
   inputs for one value because they answer different questions — the swatch is for choosing, the
   text box is for pasting the code a sign-writer or an existing logo already fixed. */
/* Logo: the mark as it will actually appear, then the buttons. The preview is the point — a
   filename tells nobody whether the thing is legible at 44px on an invoice. */
.logorow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.logoprev {
  width: 60px; height: 60px; flex: 0 0 60px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 13px; background: var(--panel-2);
  overflow: hidden;
}
.logoprev img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }

/* The palette. Big enough to hit without aiming — these are chosen by somebody looking at their
   own sign, not by somebody aiming a cursor. The ring sits OUTSIDE the swatch (box-shadow, not a
   border) so picking one does not change its size and nudge the row. */
.swatches { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.swatches .sw {
  width: 34px; height: 34px; border-radius: 9px; cursor: pointer; padding: 0;
  background: var(--sw); border: 1px solid rgba(0, 0, 0, .14);
}
.swatches .sw:hover { transform: translateY(-1px); }
.swatches .sw.on {
  box-shadow: 0 0 0 2px var(--panel, #fff), 0 0 0 4px var(--ink);
}

.colpick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.colpick input[type="color"] {
  width: 46px; height: 38px; padding: 2px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel, #fff);
}
.colpick input[type="text"] {
  width: 112px; font: inherit; font-size: 13.5px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--panel, #fff); color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; text-transform: uppercase;
}
.colpick .colnote { font-size: 11.5px; color: var(--ink-3); flex: 1; min-width: 180px; }

.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chips .chip {
  font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  padding: 7px 12px; border-radius: 20px;
  border: 1px solid var(--line); background: var(--panel, #fff); color: var(--ink-2);
}
.chips .chip:hover { border-color: var(--brand-line, var(--brand)); color: var(--ink); }
/* The chosen state is the brand at full strength, because this is the one place on the desk
   where "did I pick that one" has to be answerable without reading. */
.chips .chip.on {
  background: var(--brand); border-color: var(--brand-line, var(--brand));
  color: var(--brand-on, #fff);
}

/* One day, or one closure: a label, a time, a dash, a time. Grid rather than flex so the dashes
   line up down the column — seven rows of times that do not align read as a bug. */
/* Capped, and not stretched across the card. A day and two times need about 480px; given the
   full width of a desk the 1fr day column pushed the times to the far right edge, so the eye had
   to travel the whole card to read one row and seven rows read as seven separate things. */
.hrow {
  display: grid; grid-template-columns: minmax(118px, 190px) auto auto auto;
  align-items: center; gap: 8px; padding: 4px 0; max-width: 520px;
}
.hrow input[type="time"], .hrow input[type="date"], .hrow input[type="text"] {
  font: inherit; font-size: 13.5px; padding: 8px 10px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--panel, #fff); color: var(--ink);
}
.hrow input:focus {
  outline: 0; border-color: var(--brand-line, var(--brand)); box-shadow: 0 0 0 3px var(--brand-50);
}
.hday { display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 600; }
.hday input { width: 16px; height: 16px; accent-color: var(--brand); }
.hto { color: var(--ink-4); font-size: 12px; }
/* A closure is a date and a name, with no dash between them. */
#su_holidays .hrow { grid-template-columns: auto 1fr; }
@media (max-width: 620px) {
  .hrow { grid-template-columns: 1fr auto auto; row-gap: 4px; }
  .hday { grid-column: 1 / -1; }
}
.fld .fhint { font-size: 11px; color: var(--ink-3); line-height: 1.4; }
.fld .fhint.bad { color: var(--urgent, #b42318); font-weight: 600; }
.fld .fhint.good { color: var(--ok, #0f7b4f); font-weight: 600; }

/* A field with a button welded to it — the VIN and its decode. */
.fwith { display: flex; gap: 7px; align-items: stretch; }
.fwith input { flex: 1 1 auto; }
.fwith button {
  flex: 0 0 auto; font: inherit; font-size: 12.5px; font-weight: 650; cursor: pointer;
  padding: 0 13px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--panel-2, var(--panel)); color: var(--ink-2); white-space: nowrap;
}
.fwith button:hover:not(:disabled) { border-color: var(--brand-line, var(--brand)); color: var(--brand); }
.fwith button:disabled { opacity: .5; cursor: progress; }

.ferr {
  margin: 0; padding: 11px 18px; font-size: 13px; font-weight: 600; line-height: 1.45;
  background: var(--urgent-bg, #fef3f2); color: var(--urgent-ink, #b42318);
  border-bottom: 1px solid var(--line);
}

.ffoot {
  display: flex; gap: 9px; justify-content: flex-end; align-items: center;
  padding: 14px 18px; border-top: 1px solid var(--line);
  background: var(--panel-2, var(--panel));
  position: sticky; bottom: 0; /* a long form must not hide its own Save button */
}
.ffoot .fspacer { margin-right: auto; font-size: 12px; color: var(--ink-3); }

/* ── the walk-up queue ─────────────────────────────────────────────────────
   The rule is on the row, not the cells: drawn per cell with the children
   centred, each segment sits at its own height and the line steps across. */
.wq-head { display:flex; gap:8px; padding:12px 16px; border-bottom:1px solid var(--line) }
.wq-grp { display:flex; align-items:baseline; gap:9px; padding:11px 16px 7px;
  background:var(--panel-2); border-top:1px solid var(--line) }
.wq-grp b { font-size:12px; font-weight:680; text-transform:uppercase; letter-spacing:.05em }
.wq-grp span { font-size:12px; color:var(--ink-3) }
.wq-grp i { font-style:normal; font-size:11.5px; color:var(--ink-4); margin-left:auto }

.wq-row { display:flex; align-items:center; gap:13px; padding:11px 16px;
  border-top:1px solid var(--line-2) }
.wq-row:hover { background:var(--panel-2) }

.wq-pos { flex:0 0 38px; height:38px; border-radius:10px; display:grid; place-items:center;
  font-size:15px; font-weight:700; background:var(--line-2); color:var(--ink-2) }
.wq-pos.first { background:var(--brand); color:var(--brand-on,#fff) }
.wq-pos.bay   { background:var(--ok-bg); color:var(--ok-ink); font-size:10.5px; font-weight:650 }
.wq-pos.away  { background:transparent; border:1px dashed var(--warn); color:var(--warn-ink);
  font-size:10.5px; font-weight:650 }

.wq-who { flex:1; min-width:0 }
.wq-name { font-size:13.5px; font-weight:620; display:flex; align-items:center;
  gap:7px; flex-wrap:wrap }
.wq-meta { font-size:12.5px; color:var(--ink-3); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap }
.wq-wait { flex:0 0 92px; text-align:right }
.wq-wait b { display:block; font-size:13.5px; font-variant-numeric:tabular-nums }
.wq-wait span { font-size:11px; color:var(--ink-4); text-transform:uppercase; letter-spacing:.04em }
.wq-acts { flex:0 0 auto; display:flex; gap:6px }
.chip.c-code { font-family:ui-monospace,Menlo,monospace; letter-spacing:.05em }

.wq-add { display:flex; gap:8px; flex-wrap:wrap; padding:12px 16px;
  background:var(--panel-2); border-bottom:1px solid var(--line) }
.wq-add input { font:inherit; font-size:13px; padding:8px 11px; border-radius:8px;
  border:1px solid var(--line); background:var(--panel); color:inherit; flex:1; min-width:130px }

/* ── a phone ───────────────────────────────────────────────────────────────
   The rail is 236px, which is sixty per cent of a 390px screen, so the desk
   arrived with a wall of navigation and a sliver of content — every card
   wrapped to one word a line.

   Collapsed to icons rather than hidden behind a hamburger: no new markup, no
   JavaScript, nothing to get out of step with the desktop, and the whole menu
   stays one tap away. 58px back from 236px gives the content 85% of the screen
   instead of 40%.

   The section headings go. "VUE D'ENSEMBLE" over a column of icons is a label
   for something you can no longer read. */
@media (max-width: 760px) {
  .rail { width: 58px; flex: 0 0 58px; }
  .rail .brand-name, .rail .brand-sub, .nav-label,
  .nav a span, .who-row .who, .who-row .role, .switch-hint { display: none; }
  .rail-top { padding: 14px 0; justify-content: center; }
  .nav { padding: 8px 7px; }
  .nav a { justify-content: center; padding: 10px 0; gap: 0 }
  .nav a svg { width: 19px; height: 19px; opacity: 1 }
  /* The badge cannot sit beside a label that is gone; it rides the icon. */
  .nav .count { position: absolute; margin: 0; transform: translate(13px, -11px);
    font-size: 9.5px; padding: 0 4px; min-width: 15px; text-align: center;
    background: var(--urgent); color: #fff }
  .nav a { position: relative }
  .rail-foot { padding: 9px 0 }
  .who-row { justify-content: center; padding: 4px 0 }
  .rail-out { justify-content: center }
  .rail-out span { display: none }
  /* The header is `flex: 0 0 58px` inside a flex column, which is a HARD 58px — setting
     height:auto did nothing while that basis stood, so the contents wrapped onto four lines
     and spilled out of the bar onto the banner underneath. The basis has to go too.
     Measured, not guessed: the children ended at y=122 inside a box 58px tall. */
  .top { padding: 8px 12px; flex-wrap: wrap; height: auto; min-height: 0;
    flex: 0 0 auto; row-gap: 7px }
  .top > * { min-width: 0 }
  .top h1 { font-size: 16px }
  /* Four groups of controls will not fit 332px however they are arranged. The theme switcher
     and the online light are the two nobody reaches for at a counter; the view switcher and
     the primary action stay. */
  .top #themeSeg, .top .net { display: none }
  /* The view switcher gets its own full-width line and scrolls rather than wrapping. */
  .top #viewAs { order: 9; flex: 1 0 100%; max-width: 100% }
  .top .spacer { display: none }

  /* Four KPI tiles across a phone is four illegible tiles. */
  .stats { grid-template-columns: repeat(2, 1fr) }

  /* The day grid is a label column plus eleven hours — twelve columns will not fit a phone and
     were dragging the whole page with them. The grid keeps its shape and scrolls inside its
     own box, which is the one place a sideways scroll is the right answer. */
  .sched-scroll, .hours, .row { min-width: 760px }
  .sched-wrap, .board { overflow-x: auto; -webkit-overflow-scrolling: touch }

  /* Laid out, not wrapped. Letting a flex row wrap at 330px put the position badge alone on
     its own line, where it stretched to the full width and read as a green bar, and stacked
     every button. A grid with named areas cannot do that. */
  .wq-row { display: grid; align-items: center; gap: 7px 11px;
    grid-template-columns: 38px minmax(0, 1fr) auto;
    grid-template-areas: "pos who wait" "acts acts acts" }
  /* Beside the name, not beside the middle of a three-line block. */
  .wq-pos   { grid-area: pos; width: 38px; justify-self: start; align-self: start }
  /* A card title and its one-line explanation sit side by side on a desk and on top of each
     other on a phone. Stacked, with the hint under the title where it reads as a hint. */
  .card-head { flex-wrap: wrap; row-gap: 3px; align-items: flex-start }
  .card-head > * { flex: 1 1 100%; min-width: 0 }
  .card-head h2 { font-size: 13.5px }
  .wq-who  { grid-area: who }
  .wq-wait { grid-area: wait; flex: none; text-align: right }
  .wq-acts { grid-area: acts; flex-wrap: wrap }
  .wq-acts .btn { flex: 0 0 auto }
  .wq-add input { flex: 1 0 44%; min-width: 0 }

  /* The view tabs are a strip, not a wrap: five of them folding onto two lines pushed the
     banner down over the content. They scroll sideways inside their own box instead. */
  .tabs, .seg { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch;
    scrollbar-width: none }
  .tabs::-webkit-scrollbar, .seg::-webkit-scrollbar { display: none }
  .seg button, .tabs button { white-space: nowrap; flex: 0 0 auto }
  .top .seg { max-width: 100% }
}

/* Nothing should be able to scroll the whole page sideways on a phone — a stray wide table
   drags the header and the rail with it, which is what made the top bar look broken. */
@media (max-width: 760px) {
  html, body { max-width: 100%; overflow-x: hidden }
  .main { min-width: 0; overflow-x: hidden }
}

@media (max-width: 900px) { .fgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .fgrid { grid-template-columns: minmax(0, 1fr); }
  .fld.w2, .fld.w3 { grid-column: span 1; }
  .ffoot { flex-wrap: wrap; }
  .ffoot button { flex: 1 1 auto; }
}

/* ── a client's profile, opened from a search result ───────────────────────── */
.prohead {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; border-bottom: 1px solid var(--line);
}
.prohead .proav {
  flex: 0 0 52px; width: 52px; height: 52px; border-radius: 14px;
  background: var(--brand); color: var(--brand-on, #fff);
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 800; letter-spacing: .02em;
}
.prohead .proname { font-size: 18px; font-weight: 800; line-height: 1.2; }
.prohead .prometa { font-size: 12.5px; color: var(--ink-3); margin-top: 3px; line-height: 1.45; }
.prohead .proback { margin-left: auto; }

.protabs { display: flex; gap: 2px; padding: 0 18px; border-bottom: 1px solid var(--line); }
.protab {
  font: inherit; font-size: 13px; font-weight: 650; cursor: pointer;
  background: none; border: 0; border-bottom: 2px solid transparent;
  padding: 11px 13px; color: var(--ink-3); border-radius: 0;
}
.protab:hover { color: var(--ink-2); }
.protab.on { color: var(--brand); border-bottom-color: var(--brand); }

.propane { padding: 16px 18px; }
.propane .proempty { font-size: 13px; color: var(--ink-3); padding: 18px 0; text-align: center; }

/* One visit. The money is right-aligned because that is the column an eye runs down. */
.visit { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-2, var(--line)); }
.visit:last-child { border-bottom: 0; }
.visit .vbody { flex: 1; min-width: 0; }
.visit .vno { font-size: 13.5px; font-weight: 700; }
.visit .vwhen { font-size: 12px; color: var(--ink-3); font-weight: 500; }
.visit .vcar { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
.visit .vwhat { font-size: 12.5px; color: var(--ink-2); margin-top: 5px; line-height: 1.5; }
.visit .vright { flex: 0 0 auto; text-align: right; }
.visit .vtotal { font-size: 14px; font-weight: 750; margin-top: 7px; font-variant-numeric: tabular-nums; }

/* ───────────────────────────────────────────────────────────────────────────
   Tools in the empty space.

   An empty panel reads as a broken panel. A shop opening this on Saturday will meet a lot of empty
   panels before it fills up, and "Aucun bon de travail sur le plancher" on bare white looks like
   something failed to load. The outlined tools say the screen is working and waiting.

   Painted as a MASK, not a background image: an external SVG referenced from CSS inherits neither
   currentColor nor custom properties, so it cannot know the theme. Masked, only its alpha matters
   and background-color supplies the ink — one asset, correct in light, mid and dark.

   Two mask layers, intersected: the tiled tools, and a radial gradient that fades them out toward
   the edges so it reads as texture under the panel rather than wallpaper stopping at a hard line.

   Very low contrast on purpose. This is texture, not decoration — anything strong enough to notice
   is strong enough to read as content, and that makes an empty state harder to recognise as empty.
   ─────────────────────────────────────────────────────────────────────────── */
.emptybox, .empty3, .proempty, .toolbed { position: relative; }

.emptybox::before, .empty3::before, .proempty::before, .toolbed::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--ink-3);
  opacity: .09;
  pointer-events: none;
  z-index: 0;

  -webkit-mask-image: url("shop-icons.svg"),
                      radial-gradient(ellipse at center, #000 30%, transparent 80%);
  -webkit-mask-repeat: repeat, no-repeat;
  -webkit-mask-size: 300px 200px, 100% 100%;
  -webkit-mask-composite: source-in;

  mask-image: url("shop-icons.svg"),
              radial-gradient(ellipse at center, #000 30%, transparent 80%);
  mask-repeat: repeat, no-repeat;
  mask-size: 300px 200px, 100% 100%;
  mask-composite: intersect;
}

/* The words sit above the tools at full contrast — the message is still the first thing read. */
.emptybox > *, .empty3 > *, .proempty > *, .toolbed > * { position: relative; z-index: 1; }

/* An empty state needs room for the texture to show at all; 34px of padding left one tool
   peeking out of a corner. */
.emptybox, .empty3, .proempty {
  min-height: 190px; display: grid; place-items: center; text-align: center;
}

/* ───────────────────────────────────────────────────────────────────────────
   The tools, behind the whole app.

   Not just empty states — every page, on every surface. The shop should feel like a shop.

   Three details carry this:

   * It hangs off `html`, not `body`. The desk's body is `display:flex`, and a pseudo-element on a
     flex container becomes a flex ITEM unless it is out of flow — which is how an earlier wrapper
     broke that layout. `position: fixed` takes it out of flow, and `html` keeps it clear of the
     flex container entirely.

   * `z-index: -1` with the page colour moved from `body` to `html`. A negative-z child paints
     above the root element's background and below all in-flow content, so nothing on the page
     needs a z-index of its own. Had this been `z-index: 0`, every unpositioned paragraph would
     have painted UNDER the pattern.

   * Fixed rather than scrolling, so it does not slide around under a long list, and masked rather
     than drawn so one asset serves light, mid and dark.

   Fainter than the empty-state version: there, the tools are the only thing in the panel; here
   they sit under real content, and anything you actually notice behind a price is too strong.
   ─────────────────────────────────────────────────────────────────────────── */
html { background: var(--bg); }
body { background: transparent; }

html::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--ink-3);
  opacity: .085;

  -webkit-mask-image: url("shop-icons.svg");
  -webkit-mask-repeat: repeat;
  -webkit-mask-size: 300px 200px;
  mask-image: url("shop-icons.svg");
  mask-repeat: repeat;
  mask-size: 300px 200px;
}

/* A facture goes to a customer and into the shop's books. Tools printed faintly across it would
   be toner spent on decoration, and on a fax or a photocopy they turn into dirt. */
@media print {
  html::before { display: none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   The send sheet. One dialog, three surfaces, so the choice looks and behaves the same whether it
   is opened from the counter, the bay tablet or the portal.

   Big targets: this gets tapped with a thumb on a tablet mounted in a bay, often by somebody
   wearing a glove.
   ─────────────────────────────────────────────────────────────────────────── */
.sendsheet { position: fixed; inset: 0; z-index: 9000; display: grid; place-items: center; }
.sendsheet .ss-scrim { position: absolute; inset: 0; background: rgba(12, 10, 18, .5); }

.sendsheet .ss-card {
  position: relative;
  width: min(440px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  overflow: auto;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
}

.sendsheet .ss-head { padding: 18px 18px 4px; }
.sendsheet .ss-title { font-size: 17px; font-weight: 750; }
.sendsheet .ss-sub { font-size: 13px; color: var(--ink-3); margin-top: 3px; }

.sendsheet .ss-msg {
  margin: 12px 18px 0; padding: 10px 12px; border-radius: 10px;
  font-size: 13px; font-weight: 600; line-height: 1.45;
  background: var(--brand-50); color: var(--brand-ink);
}
.sendsheet .ss-msg.bad { background: var(--urgent-bg, #fef3f2); color: var(--urgent-ink, #b42318); }

.sendsheet .ss-list { padding: 12px; display: flex; flex-direction: column; gap: 8px; }

.sendsheet .ss-opt {
  display: flex; align-items: center; gap: 12px; text-align: left;
  padding: 13px 14px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--panel-2, var(--panel));
  color: inherit; font: inherit; cursor: pointer; width: 100%;
}
.sendsheet .ss-opt:hover:not(:disabled) { border-color: var(--brand-line, var(--brand)); background: var(--brand-50); }
.sendsheet .ss-opt:focus-visible { outline: 2px solid var(--brand-line, var(--brand)); outline-offset: 2px; }

/* Disabled, not hidden. "Why can't I text him?" is answered by the reason sitting in the row;
   a missing button just looks like the feature is gone. */
.sendsheet .ss-opt:disabled { opacity: .5; cursor: not-allowed; }

.sendsheet .ss-opt svg { width: 22px; height: 22px; flex: 0 0 22px; color: var(--brand); }
.sendsheet .ss-opt:disabled svg { color: var(--ink-3); }
.sendsheet .ss-opt-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sendsheet .ss-opt-name { font-size: 15px; font-weight: 700; }
.sendsheet .ss-opt-to {
  font-size: 12.5px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.sendsheet .ss-optin {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 0 12px 4px; padding: 12px 14px;
  border: 1px dashed var(--line); border-radius: 12px; cursor: pointer;
}
.sendsheet .ss-optin input { width: 20px; height: 20px; flex: 0 0 20px; margin-top: 1px; accent-color: var(--brand); }
.sendsheet .ss-optin-label { font-size: 13.5px; font-weight: 650; display: block; }
.sendsheet .ss-optin-hint { font-size: 12px; color: var(--ink-3); display: block; margin-top: 3px; line-height: 1.45; }

.sendsheet .ss-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 18px 16px; border-top: 1px solid var(--line);
}
/* The tablet styles its buttons through .primary/.ghost and the desk through .btn; the sheet is
   shared, so it gives its own cancel button a shape either way. */
.sendsheet .ss-foot .btn {
  padding: 10px 16px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel); color: var(--ink-2); font: inherit; font-weight: 650; cursor: pointer;
}
.sendsheet .ss-foot .btn:hover { border-color: var(--brand-line, var(--brand)); color: var(--brand); }

/* ───────────────────────────────────────────────────────────────────────────
   The garage's mark.

   Separate from .logo, which is the BA lettermark and stays Balenton purple. These rules never
   set a brand colour: a logo arrives with its own, and tinting somebody's logo is worse than
   showing none.
   ─────────────────────────────────────────────────────────────────────────── */
.shopmark, .railmark, .gmark { display: inline-block; vertical-align: middle; }
.shopmark-txt, .railmark-txt, .gmark-txt {
  display: inline-grid; place-items: center;
  border-radius: 7px; font-weight: 800; letter-spacing: .01em;
  /* The shop's own colour, which shop-theme.js has already set from brand_colour — so a shop with
     no logo still gets its own identity rather than a grey box. */
  background: var(--brand-50); color: var(--brand-ink);
  font-size: .62em; line-height: 1;
}

/* In the rail, under "Balenton Automotive": small, and on the rail's dark ground. */
.brand-sub { display: flex; align-items: center; gap: 7px; }
/* A chip, not a bare image. Noukoua's mark is a solid black disc — correct on a white invoice,
   invisible on the rail's dark ground. A light pad behind it means ANY logo reads here, dark or
   light, without the app touching the logo's own colours. */
.railmark {
  flex: 0 0 auto; border-radius: 5px;
  background: #fff; padding: 2px; box-sizing: content-box;
}
.railmark-txt { background: rgba(255, 255, 255, .14); color: inherit; border-radius: 5px; }

/* The tablet header line. */
#shopName { display: flex; align-items: center; gap: 7px; }

/* The portal masthead. This is a letterhead: the mark leads and the name sits beside it. */
.gname { display: flex; align-items: center; gap: 12px; }
.gmark { flex: 0 0 auto; border-radius: 10px; }
.gmark-txt { border-radius: 12px; font-size: .5em; }

/* ── reception, on direction 04 ──────────────────────────────────────────── */
/* The KPI strip. Four real numbers, in the same card as every other surface. */
#statStrip .b-kpis { padding: 16px 20px 0; margin-bottom: 0; }
#statStrip .b-kpi .k svg { width: 14px; height: 14px; stroke-width: 1.9; }
#statStrip .b-kpi .bar { height: 4px; border-radius: 3px; background: var(--line); margin-top: 9px; overflow: hidden; }
#statStrip .b-kpi .bar i { display: block; height: 100%; background: var(--brand); border-radius: 3px; }

/* A job the CUSTOMER booked through the portal, marked in the garage's own colour. At a glance
   the counter can see what arrived without anybody answering the phone — which is the number the
   shop will actually want to watch as the portal takes over from the telephone. */
.job.from-portal { box-shadow: inset 3px 0 0 var(--brand); }
.job.from-portal .t::after {
  content: '•'; color: var(--brand-ink, var(--brand)); font-weight: 900; margin-left: 5px;
}

/* The rail already carries the faint tools; nothing here paints over them. */


/* ── the owner's dashboard, in the garage's colours ───────────────────────── */
/* The four figures were the only part of the desk with no brand colour on them at all, which is
   why the admin looked untouched next to reception and the bay tablet. An accent rule and a
   coloured icon is the right amount: the number still has to be the loudest thing on the card,
   so the colour goes around it rather than through it. */
#viewOwner .b-kpis .b-kpi { border-top: 3px solid var(--brand-line, var(--brand)); }
#viewOwner .b-kpis .b-kpi .k { color: var(--ink-2); }
#viewOwner .b-kpis .b-kpi .k svg { color: var(--brand-ink, var(--ink-2)); }

/* The dashboard's own panels already carry a brand header; this keeps the dark ones consistent
   with the tablet, where the accent is the brand colour on black. */
#viewOwner .b-panel > .b-top.k .k2 { color: var(--brand); }


/* ── the signed-in shop, in the shop's own colour ──────────────────────────── */
/* The disc in the rail foot carries the garage's name and was a fixed slate grey, the same on
   every install. It is the one mark on the desk that stands for THIS shop — the BA lettermark
   above it is Balenton's and stays purple on purpose — so it is the one that should be theirs.
   --brand-on keeps it legible: white on #FFED00 is about 1.1:1. */
.rail-foot .avatar { background: var(--brand); color: var(--brand-on, #000); font-weight: 800; }
.rail-foot .avatar.mech { background: var(--brand); color: var(--brand-on, #000); }


/* ── more than one garage ──────────────────────────────────────────────────── */
/* Hidden unless the account has a second shop. It sits in the rail foot, above the person,
   because which garage you are in decides what every other control on the screen means. */
.shoppick {
  width: calc(100% - 20px); margin: 0 10px 8px; font: inherit; font-size: 12px; font-weight: 650;
  padding: 7px 9px; border-radius: 8px; cursor: pointer;
  background: var(--rail-hov); color: #fff; border: 1px solid var(--rail-line);
}
.shoppick:focus-visible { outline: 2px solid var(--brand-line, var(--brand)); outline-offset: 1px; }


/* ── a new member, between the invitation and their first job ──────────────── */
.signin-card.ob { max-width: 420px; text-align: left; }
.signin-card.ob h1 { font-size: 20px; text-align: center; }
.ob-lede { font-size: 13.5px; color: var(--ink-3); text-align: center; line-height: 1.5;
  margin-top: 6px; }
.ob-mark { display: flex; justify-content: center; margin-bottom: 12px; }
.ob-mark img { max-height: 54px; max-width: 170px; height: auto; }

.ob-lab { font-size: 10.5px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
  color: var(--ink-3); margin: 16px 0 6px; }
.ob-lab .opt { text-transform: none; letter-spacing: 0; font-weight: 600; color: var(--ink-4); }

/* Three ways in. The whole tile is the target — this is read on a phone, standing up. */
.ob-ch { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.ob-ch button {
  font: inherit; border: 1.5px solid var(--line); background: var(--panel); color: var(--ink);
  border-radius: 12px; padding: 11px 6px; cursor: pointer; text-align: center;
}
.ob-ch button.on {
  border-color: var(--brand-line, var(--brand)); background: var(--brand-50);
  box-shadow: inset 0 0 0 1.5px var(--brand-line, var(--brand));
}
.ob-ch .ci { display: block; font-size: 17px; line-height: 1.1; }
.ob-ch .cn { display: block; font-size: 12px; font-weight: 780; margin-top: 4px; }
.ob-ch .cs { display: block; font-size: 10px; color: var(--ink-3); margin-top: 1px; }

/* The photograph. Big, and the whole circle is the button — a pencil in the corner is a miss
   on a tablet with gloves on. */
/* Scoped past `.signin-card button { width: 100% }`, which is (0,1,1) and would otherwise win
   and stretch this into an ellipse — a circle is only a circle while it stays square. */
.signin-card .ob-face {
  display: block; width: 104px; height: 104px; min-width: 0; max-width: 104px;
  margin: 16px auto 0; padding: 0;
  border-radius: 50%; border: 2px dashed var(--line); background: var(--line-2);
  cursor: pointer; overflow: hidden;
}
.signin-card .ob-face:hover,
.signin-card .ob-face:focus-visible { border-color: var(--brand-line, var(--brand)); outline: none; }
.signin-card .ob-face.has { border: 0; }
.signin-card .ob-face .b-face { width: 100%; height: 100%; flex: none; }
.ob-facelab { text-align: center; font-size: 12.5px; font-weight: 730;
  color: var(--brand-ink, var(--ink-2)); margin-top: 9px; }

.ob-note { font-size: 12.5px; line-height: 1.5; margin-top: 12px; text-align: center; }
.ob-note.bad { color: var(--urgent-ink); }
.ob-note.ok { color: var(--ok); }

#obCode { text-align: center; font-size: 26px; letter-spacing: 8px; font-weight: 800; }


/* ── watching somebody else's desk ─────────────────────────────────────────── */
/* Unmissable, because an owner who forgets whose screen this is will try to take a payment on
   it. The refusal is in JavaScript; this is so the attempt is not made. */
.roview {
  /* Its own height, never a share of the column it sits in. Without this it stretches to fill
     whatever flex context it lands in, which is how it became a full-height block. */
  flex: 0 0 auto;
  position: relative; z-index: 90;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--brand); color: var(--brand-on, #000);
  padding: 9px 16px; font-size: 13px; font-weight: 700;
}
.roview a { color: inherit; margin-left: auto; font-weight: 800; }
body.readonly .main button:not(.roview a),
body.readonly .main input,
body.readonly .main select,
body.readonly .main textarea { pointer-events: none; opacity: .55; }
body.readonly .roview, body.readonly .roview * { pointer-events: auto; opacity: 1; }


/* ── the three ways in ─────────────────────────────────────────────────────── */
/* Equal tiles, drawn icons, and air between them and the fields below. They are a choice, not
   a toolbar, so they are allowed to be large. */
.signin-card .ob-ch { margin-top: 6px; gap: 9px; }
.signin-card .ob-ch button {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px; padding: 14px 8px; min-height: 86px;
  background: var(--panel-2); border: 1.5px solid var(--line); color: var(--ink);
}
.signin-card .ob-ch button:hover { border-color: var(--brand-line, var(--brand)); }
.signin-card .ob-ch button.on {
  background: var(--brand-50); border-color: var(--brand-line, var(--brand));
  box-shadow: inset 0 0 0 1.5px var(--brand-line, var(--brand));
}
.signin-card .ob-ch .ci { display: grid; place-items: center; height: 22px; }
.signin-card .ob-ch .ci svg { width: 20px; height: 20px; stroke-width: 1.9; color: var(--ink-2); }
.signin-card .ob-ch button.on .ci svg { color: var(--brand-ink, var(--ink)); }
.signin-card .ob-ch .cn { font-size: 12.5px; font-weight: 740; line-height: 1.15; }
.signin-card .ob-ch .cs { display: none; }

/* The fields, matching the rest of the product rather than the browser's defaults. */
.signin-card input {
  background: var(--panel-2); color: var(--ink);
  border: 1.5px solid var(--line); border-radius: 11px;
  padding: 12px 14px; font-size: 15px; text-align: left;
}
.signin-card input::placeholder { color: var(--ink-4); }
.signin-card input:focus {
  outline: none; border-color: var(--brand-line, var(--brand)); box-shadow: 0 0 0 3px var(--brand-100);
}
.signin-card button[type="submit"] { margin-top: 3px; border-radius: 11px; font-weight: 800; }

/* ── signed out, the product is Balenton ───────────────────────────────────── */
/* No shop is known yet, so the neutral default applies and the Entrer button comes out nearly
   black beneath an indigo BA. These are scoped to the sign-in wrapper: once a shop loads,
   applyShopTheme sets the same names on :root and this card no longer exists. */
.signin-wrap {
  --brand: #6d28d9;
  --brand-on: #ffffff;
  --brand-ink: #a78bfa;
  --brand-50: color-mix(in srgb, #6d28d9 13%, transparent);
  --brand-100: color-mix(in srgb, #6d28d9 28%, transparent);
}

/* ── a record somebody reads, not a list they scan ──────────────────────────── */
/* .req is right for the dashboard's dense rows. A client file is read rather than scanned, so
   its rows get air, and their rules stop short of the card's rounded corners instead of running
   into them — which is what made the panel look like a grid of boxes. */
/* A two-column grid rather than a flex row with a 150px stub: the label column sizes to the
   longest label instead of to a number somebody picked, and the value starts right after it. */
.detail .req {
  display: grid; grid-template-columns: minmax(96px, max-content) 1fr;
  gap: 6px 20px; padding: 13px 22px; align-items: baseline; border-bottom: 0;
}
.detail .req > :first-child {
  color: var(--dim); font-size: 12.5px; font-weight: 600; letter-spacing: .01em;
}
.detail .req .svc {
  color: var(--ink); font-size: 13.5px; margin: 0; text-align: left;
  overflow-wrap: anywhere;
}
.detail .req.owing .svc { color: var(--urgent-ink, var(--ink)); }

/* The rule is drawn on an inset pseudo-element so it never touches the border or the corner. */
.detail .req + .req { border-top: 0; position: relative; }
.detail .req + .req::before {
  content: ''; position: absolute; left: 22px; right: 22px; top: 0;
  border-top: 1px solid var(--line-2);
}

/* An empty section should be a short, quiet sentence, not a wall. Three of these stack up on a
   client with no cars yet, and at 34px of padding each they pushed everything else off screen. */

@media (max-width: 560px) {
  .detail .req { grid-template-columns: 1fr; gap: 2px; padding: 11px 16px; }
}

/* The empty state is a sentence, not a room. Three of these stack on a client with no cars yet. */
.emptybox { padding: 20px; color: var(--dim); font-size: 13px; text-align: center; }

/* ── the month ──────────────────────────────────────────────────────────────── */
/* One pixel of gap over a line-coloured background draws every cell border at once, so the grid
   has no doubled edges and no rule running into the card's rounded corner. */
.cal-head { display:flex; align-items:center; gap:10px; padding:12px 22px;
  border-bottom:1px solid var(--line-2); flex-wrap:wrap }
.cal-head .mon { font-weight:760; font-size:14.5px; text-transform:capitalize; min-width:150px }
.cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:1px; background:var(--line);
  border-bottom:1px solid var(--line) }
.cal-dow { background:var(--panel-2); padding:7px 8px; font-size:11px; font-weight:700;
  color:var(--dim); text-transform:uppercase; letter-spacing:.04em; text-align:center }
.cal-day { background:var(--panel); min-height:94px; padding:6px 6px 8px; position:relative }
.cal-day.out { background:var(--panel-2) }
.cal-day.out .dn { color:var(--ink-4) }
.cal-day .dn { font-size:11.5px; font-weight:700; color:var(--ink-3); display:block;
  margin-bottom:4px; text-align:right }
.cal-day.today { box-shadow:inset 0 0 0 2px var(--brand-line, var(--brand)) }
.cal-day.today .dn { color:var(--brand-ink) }
.cal-ev { display:block; width:100%; text-align:left; border:0; font:inherit; cursor:pointer;
  font-size:11px; line-height:1.3; font-weight:600; border-radius:5px; padding:3px 6px;
  margin-top:3px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.cal-ev .t { opacity:.75; font-weight:700; margin-right:4px }
.cal-ev.booking     { background:var(--off-bg);    color:var(--off) }
.cal-ev.placed      { background:var(--ok-bg);     color:var(--ok-ink) }
.cal-ev.appointment { background:var(--brand-50);  color:var(--brand-ink) }
.cal-key { display:flex; gap:14px; flex-wrap:wrap; padding:10px 22px; font-size:11.5px;
  color:var(--dim); border-bottom:1px solid var(--line-2) }
.cal-key i { display:inline-block; width:9px; height:9px; border-radius:3px; margin-right:5px }

/* ── the queue, with somewhere for the eye to rest ──────────────────────────── */
/* Rules between the columns, and the middle one held to a readable measure. Stretched across a
   wide monitor the three columns read as three unrelated things sharing a line. */
.q-head, .q-row { display:grid; grid-template-columns:120px minmax(0,1fr) 300px;
  gap:0; align-items:center; padding:0 22px }
.q-head > *, .q-row > * { padding:11px 18px; min-width:0 }
.q-head > * + *, .q-row > * + * { border-left:1px solid var(--line-2) }
.q-row > :first-child, .q-head > :first-child { padding-left:0 }
.q-row > :last-child, .q-head > :last-child { padding-right:0 }
.q-asking { background:var(--urgent-bg) }
.q-asking .sub { white-space:normal; color:var(--urgent-ink) }
.q-explain { margin:0; padding:12px 22px 14px; font-size:12.5px; line-height:1.65;
  color:var(--ink-3); border-bottom:1px solid var(--line-2); background:var(--panel-2) }
.q-explain b { color:var(--ink-2); font-weight:700 }
.q-head { font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  color:var(--dim); border-bottom:1px solid var(--line); background:var(--panel-2) }
.q-daybar { padding:9px 22px; font-size:12px; font-weight:740; color:var(--ink-2);
  background:var(--panel-2); border-top:1px solid var(--line-2);
  border-bottom:1px solid var(--line-2); text-transform:capitalize }
.q-row + .q-row { border-top:1px solid var(--line-2) }
.q-row .at { font-size:12.5px; font-weight:700; color:var(--ink) }
.q-row .dur { font-size:11.5px; color:var(--dim) }
.q-row .ttl { font-size:13.5px; font-weight:600; color:var(--ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* The payroll entry's description is a paragraph of names and numbers. One line, or it pushes
   the buttons off the right-hand edge. */
.q-row .sub { font-size:12px; color:var(--ink-3); margin-top:2px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* Two rows, always the same two rows: the usual answer across the top, the other two beneath.
   A flex wrap put the primary button in a different place on every line. */
.q-acts { display:grid; grid-template-columns:1fr 1fr; gap:7px }
.q-acts > :first-child { grid-column:1 / -1 }
.q-tags { display:flex; gap:6px; flex-wrap:wrap; margin-top:7px }

@media (max-width: 820px) {
  .q-head { display:none }
  .q-row { grid-template-columns:1fr; gap:6px; padding:8px 16px }
  .q-row > * { padding:3px 0 }
  .q-row > * + * { border-left:0 }
  .q-acts { grid-template-columns:1fr }
  .cal-day { min-height:72px }
}

/* ── the morning brief ──────────────────────────────────────────────────────── */
.brief { border:1px solid var(--line); border-radius:10px; background:var(--panel);
  box-shadow:var(--shadow); overflow:hidden; margin-bottom:14px }
.brief-h { display:flex; align-items:center; gap:10px; padding:12px 18px;
  border-bottom:1px solid var(--line-2) }
.brief-h h3 { font-size:13.5px; font-weight:760; margin:0 }
.brief-h .when { font-size:12px; color:var(--dim); margin-left:auto }
.brief-b { display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:1px;
  background:var(--line-2) }
.brief-c { background:var(--panel); padding:14px 18px }
.brief-c .k { font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  color:var(--dim); display:block; margin-bottom:6px }
.brief-c .v { font-size:22px; font-weight:800; letter-spacing:-.4px; line-height:1 }
.brief-c .n { font-size:12px; color:var(--ink-3); margin-top:5px; display:block }
.brief-c.hot .v { color:var(--urgent-ink) }
.brief-list { padding:10px 18px 14px; border-top:1px solid var(--line-2) }
.brief-list li { font-size:12.5px; color:var(--ink-2); padding:3px 0; list-style:none }
.brief-list li b { color:var(--ink); font-weight:650 }
.brief-list .late { color:var(--urgent-ink); font-weight:700 }
.brief-f { display:flex; gap:9px; padding:0 18px 14px; flex-wrap:wrap }

/* ── per-member switches on the Équipe row ─────────────────────────────────── */
.mctl { flex:1 0 100%; display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin-top:10px; padding-top:10px; border-top:1px dashed var(--line-2) }
.mctl-k { font-size:11px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  color:var(--dim) }
.mtog { display:inline-flex; align-items:center; gap:6px; font-size:12.5px; color:var(--ink-2);
  cursor:pointer }
.mtog input { accent-color:var(--brand); width:15px; height:15px; cursor:pointer }
.mtog input:disabled { cursor:not-allowed }
.mtog:has(input:disabled) { opacity:.45; cursor:not-allowed }
.msel { font:inherit; font-size:12.5px; padding:4px 8px; border-radius:7px;
  border:1px solid var(--line); background:var(--panel-2); color:var(--ink) }

/* ── a client found from inside a form ─────────────────────────────────────── */
.cres { display:flex; align-items:baseline; gap:10px; width:100%; text-align:left;
  border:0; border-top:1px solid var(--line-2); background:transparent; cursor:pointer;
  font:inherit; padding:8px 2px; color:var(--ink) }
.cres:first-of-type { border-top:0 }
.cres:hover { background:var(--panel-2) }
.cres-n { font-size:13.5px; font-weight:620 }
.cres-m { font-size:12px; color:var(--ink-3); margin-left:auto }

/* Whose car this is, at the top of the vehicle form. In the shop's own colour, because on a
   list of six thousand clients the wrong one costs a month and an invoice to find. */
.fclient { display:flex; align-items:center; gap:10px; padding:13px 16px;
  background:var(--brand-50); border-bottom:1px solid var(--brand-100) }
.fclient-k { font-size:11px; font-weight:650; letter-spacing:.06em; text-transform:uppercase;
  color:var(--brand-ink); opacity:.72 }
.fclient-n { font-size:16px; font-weight:680; color:var(--brand-ink); min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.fclient .btn { margin-left:auto }
@media (max-width:760px) {
  .fclient { flex-wrap:wrap; gap:7px }
  .fclient-n { font-size:15px; flex:1 1 100% }
  .fclient .btn { margin-left:0 }
}

/* ── the vehicle file ─────────────────────────────────────────────────────
   The car first and large. A shop recognises a customer's vehicle before it recognises the
   name on the account, and the picture is the fastest way to know you have opened the right
   one of the three a family keeps here. */
.vhero { display:flex; gap:0; overflow:hidden; align-items:stretch }
.vhero-pic { flex:0 0 42%; min-width:0; display:flex; align-items:center; justify-content:center;
  padding:18px; background:var(--brand-50); border-right:1px solid var(--line) }
.vhero-pic .carbig { width:100%; height:auto; max-height:230px; object-fit:contain;
  /* A generated render on a tinted panel wants a little grounding or it floats. */
  filter:drop-shadow(0 10px 14px rgba(0,0,0,.16)) }
.vhero-side { flex:1 1 auto; min-width:0; padding:18px 20px; display:flex; flex-direction:column; gap:13px }
.vhero-top { display:flex; align-items:flex-start; justify-content:space-between; gap:12px }
.vhero-h { font-size:24px; font-weight:680; letter-spacing:-.01em; margin:0; line-height:1.15 }
.vhero-sub { font-size:13px; color:var(--ink-3); margin-top:3px }
.vchips { display:flex; flex-wrap:wrap; gap:7px }

/* The owner is a button because "whose car is this" is nearly always followed by "open them". */
.vowner { display:flex; align-items:center; gap:10px; width:100%; text-align:left; cursor:pointer;
  padding:11px 13px; border-radius:9px; border:1px solid var(--brand-100);
  background:var(--brand-50); color:inherit; font:inherit }
.vowner:hover { border-color:var(--brand-line, var(--brand)); background:var(--brand-100) }
.vowner-k { font-size:10.5px; font-weight:650; letter-spacing:.06em; text-transform:uppercase;
  color:var(--brand-ink); opacity:.7; flex:0 0 auto }
.vowner-n { font-size:15px; font-weight:670; color:var(--brand-ink); min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.vowner-m { font-size:12px; color:var(--ink-3) }
.vowner-go { margin-left:auto; font-size:12px; font-weight:620; color:var(--brand);
  flex:0 0 auto; white-space:nowrap }

.vstats { display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:1px;
  background:var(--line); border:1px solid var(--line); border-radius:9px; overflow:hidden }
.vstat { background:var(--panel); padding:10px 12px; min-width:0 }
.vstat-k { font-size:10.5px; font-weight:620; letter-spacing:.04em; text-transform:uppercase;
  color:var(--ink-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.vstat-v { font-size:16px; font-weight:670; margin-top:3px }
.vstat.bad .vstat-v { color:var(--urgent-ink) }

@media (max-width: 860px) {
  .vhero { flex-direction:column }
  .vhero-pic { flex:0 0 auto; border-right:0; border-bottom:1px solid var(--line); padding:14px }
  .vhero-pic .carbig { max-height:170px }
  .vhero-side { padding:14px 16px }
  .vhero-h { font-size:20px }
  .vstats { grid-template-columns:repeat(2, minmax(0,1fr)) }
  .vowner { flex-wrap:wrap; gap:7px }
  .vowner-go { margin-left:0; flex:1 1 100% }
}

/* ── the queue, on the owner's dashboard ──────────────────────────────────
   One person standing at the counter was a single cramped line between the money tiles and
   the bays, with nothing around it. It is the only thing on this screen that needs somebody
   to do something in the next minute, so it gets a panel and room to be seen. */
.qp-head-r { display:flex; align-items:center; gap:12px; min-width:0 }
.qp-count { font-size:12px; font-weight:700; white-space:nowrap; opacity:.92 }
.qp-head-r .btn { background:rgba(255,255,255,.16); border-color:rgba(255,255,255,.34);
  color:inherit }
.qp-head-r .btn:hover { background:rgba(255,255,255,.26) }

.qp-none { margin:0; padding:15px; font-size:13px; color:var(--ink-3) }

.qp-row { display:flex; align-items:center; gap:14px; padding:14px 15px;
  border-top:1px solid var(--line-2) }
.qp-row:first-of-type { border-top:0 }
/* The next car in is the decision being made, so it is the one that reads first.

   NOT var(--brand-50) as a background. For a shop with its own colour that token is computed
   as a pale tint, which in dark mode put near-white behind text that stays light: the first
   row -- the one person this panel exists to point at -- went blank. Mixing the brand INTO the
   panel colour instead keeps the row the right side of its own text in both themes, whatever
   colour the shop picked at onboarding. The flat fallback on the line before covers a browser
   without color-mix. */
.qp-row.first { background:var(--panel-2);
  background:color-mix(in srgb, var(--brand) 13%, var(--panel));
  box-shadow:inset 3px 0 0 var(--brand) }
.qp-who { flex:1 1 auto; min-width:0 }
.qp-name { font-size:15px; font-weight:680; display:flex; align-items:center; gap:8px;
  flex-wrap:wrap }
.qp-meta { font-size:12.5px; color:var(--ink-3); margin-top:3px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.qp-wait { flex:0 0 auto; text-align:right }
.qp-wait b { display:block; font-size:14px; font-variant-numeric:tabular-nums }
.qp-wait span { font-size:10.5px; color:var(--ink-4); text-transform:uppercase;
  letter-spacing:.05em }
.qp-row .btn { flex:0 0 auto }
.qp-more { margin:0; padding:10px 15px 13px; font-size:12px; color:var(--ink-3);
  border-top:1px solid var(--line-2) }

@media (max-width: 760px) {
  .qp-head-r { gap:8px }
  .qp-count { white-space:normal; font-size:11.5px }
  .qp-row { flex-wrap:wrap; gap:9px 11px; padding:12px 14px }
  .qp-who { flex:1 1 60%; }
  .qp-wait { text-align:left }
  .qp-row .btn { flex:1 1 100% }
}

/* ── this customer already has one open ───────────────────────────────────── */
/* A warning, not a wall: it has to be impossible to miss and still easy to walk past, so it
   sits inside the form rather than over it and keeps its own "create anyway" beside the list. */
.dupe { margin:10px 16px; padding:11px 13px 9px; border-radius:10px;
  border:1px solid var(--warn); background:var(--warn-bg) }
.dupe-h { font-size:13px; font-weight:680; color:var(--ink) }
.dupe-p { margin:3px 0 8px; font-size:12px; color:var(--ink-3) }
/* The rule belongs to the row. Drawn on the cells it steps up and down, because with the
   children baseline-aligned each cell is only as tall as the text inside it. */
.dupe-r { display:flex; align-items:center; gap:10px; padding:7px 0;
  border-top:1px solid var(--line-2) }
.dupe-w { font-size:13px; font-weight:620; flex:0 0 auto }
.dupe-m { font-size:12px; color:var(--ink-3); flex:1; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.dupe-f { display:flex; justify-content:flex-end; padding-top:8px }

/* ── paie ───────────────────────────────────────────────────────────────────── */
/* The name gets a measure, a spacer takes the slack, and the figures stay together on the
   right. Letting the name column stretch flung the numbers to the far edge of a wide screen. */
.pr-row { display:grid;
  grid-template-columns:minmax(170px,260px) 1fr 70px 90px 100px 120px 120px 110px 125px auto;
  align-items:center; padding:0 20px }
.pr-row > * { padding:11px 10px; min-width:0 }
.pr-row > :first-child { padding-left:0 }
.pr-row > :last-child { padding-right:0 }
/* The rule belongs to the ROW. Drawn on each cell it is one segment per column, and with
   align-items:center every cell is a different height, so the segments sit at different levels
   and the line looks broken — which it is. */
.pr-row + .pr-row { border-top:1px solid var(--line-2) }
.pr-head { font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  color:var(--dim); background:var(--panel-2); border-bottom:1px solid var(--line) }
.pr-row .num { text-align:right; font-variant-numeric:tabular-nums }
.pr-tot { border-top:2px solid var(--line) }
.pr-tot > * { font-weight:800; background:var(--panel-2) }
.pr-ot { color:var(--warn-ink); font-weight:700 }
.pr-rate { width:100%; font:inherit; text-align:right; font-variant-numeric:tabular-nums;
  padding:6px 9px; border-radius:7px; border:1px solid var(--line);
  background:var(--panel-2); color:var(--ink) }
.pr-acts { display:flex; gap:7px; justify-content:flex-end }
.pr-foot { padding:12px 20px; border-top:1px solid var(--line-2) }
.pr-warn { color:var(--warn-ink) }

.sh-row { display:grid; grid-template-columns:150px 1fr 90px auto; align-items:center;
  padding:0 20px }
.sh-row > * { padding:10px 10px }
.sh-row > :first-child { padding-left:0 }
.sh-row + .sh-row { border-top:1px solid var(--line-2) }
.sh-row .num { font-variant-numeric:tabular-nums }
.sh-note { grid-column:1 / -1; padding-top:0; font-size:12px; color:var(--ink-3);
  font-style:italic }
.sh-edit { padding:14px 20px; background:var(--brand-50);
  border-bottom:1px solid var(--line-2); display:grid; gap:10px }
.sh-fields { display:flex; gap:10px; align-items:center; flex-wrap:wrap }
.sh-edit input { font:inherit; padding:8px 11px; border-radius:8px;
  border:1px solid var(--line); background:var(--panel); color:var(--ink) }
.sh-edit input[type=text] { width:100% }

@media (max-width:1000px) {
  .pr-head { display:none }
  .pr-row { grid-template-columns:1fr 1fr; gap:4px }
  .pr-row > :nth-child(2) { display:none }
  .pr-row > * { padding:5px 0 }
  .pr-row + .pr-row { border-top:1px solid var(--line-2); padding-top:10px; margin-top:6px }
  .sh-row { grid-template-columns:1fr }
}
