/* ===========================================================================
   Dee's CRM — application layer.
   Built on the marketing site's brand tokens, retuned for a dense tool that
   gets used on a phone in the bay and an iPad on the bench.
   =========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&family=JetBrains+Mono:wght@400;500;600;700&display=swap");

:root {
  --ink-950:#06141a; --ink-900:#0a1d24; --ink-850:#0e262d;
  --slate-800:#16303a; --slate-700:#22414b; --slate-600:#35434a;
  --slate-500:#4c6069; --slate-400:#7a8e97; --steel-300:#a6b6bd;
  --cream-050:#fdf8ee; --cream-100:#fbf1df; --cream-200:#f4e2c3;
  --brick-500:#c7473b; --brick-600:#ad3a30; --brick-700:#8a2b23;
  --amber-400:#e8a33d; --amber-500:#d18b27;
  --green-400:#5cb98f; --green-600:#2f7d5c;

  --font-display:"Archivo",system-ui,sans-serif;
  --font-text:"Archivo",system-ui,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,monospace;

  --bg:var(--ink-950);
  --panel:var(--ink-850);
  --panel-2:var(--slate-800);
  --fg:var(--cream-100);
  --fg-muted:var(--steel-300);
  --fg-faint:var(--slate-400);
  --line:rgb(244 226 195 / .14);
  --line-soft:rgb(244 226 195 / .07);

  --r-sm:8px; --r-md:12px; --r-lg:18px;
  --ease:cubic-bezier(.2,.7,.2,1);
  --snap:cubic-bezier(.16,1,.3,1);
  --rail:236px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;background:var(--bg);color-scheme:dark;overscroll-behavior-x:none}
body{
  margin:0;background:var(--bg);color:var(--fg);
  font-family:var(--font-text);font-size:.9375rem;line-height:1.5;
  -webkit-font-smoothing:antialiased;overscroll-behavior-x:none;
}
img,svg{display:block;max-width:100%}
button,input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4,p,figure,dl,dd{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
:focus-visible{outline:2px solid var(--amber-400);outline-offset:2px;border-radius:4px}
*{-webkit-tap-highlight-color:rgb(232 163 61 / .18)}

.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* ---- shell ---------------------------------------------------------- */
.app{display:grid;min-height:100dvh}
@media (min-width:900px){ .app{grid-template-columns:var(--rail) minmax(0,1fr)} }

.rail{
  display:none;border-right:1px solid var(--line);background:var(--ink-900);
  padding:1.1rem .8rem;position:sticky;top:0;height:100dvh;
  flex-direction:column;gap:1.25rem;
}
@media (min-width:900px){ .rail{display:flex} }
.rail nav{flex:1 1 auto;min-height:0;overflow-y:auto}
.rail__foot{margin-top:auto;display:grid;gap:.5rem}
.rail__brand{display:flex;align-items:center;gap:.6rem;padding:.15rem .4rem 0}
/* Sized for a tenant's wide logo. Excluded from the platform mark, which is a
   square icon with its own dimensions — without the :not() this rule outranks
   .plat__mark on specificity and stretches it. */
.rail__brand img:not(.plat__mark){height:34px;width:auto}
.rail__brand b{
  font-family:var(--font-display);font-variation-settings:"wdth" 112,"wght" 800;
  text-transform:uppercase;font-size:.8125rem;letter-spacing:-.01em;line-height:1.1;
}
.rail__brand small{display:block;font-family:var(--font-mono);font-size:.5625rem;
  letter-spacing:.14em;color:var(--slate-400);margin-top:2px}
.rail nav{display:grid;gap:2px;align-content:start}
.rail a{
  display:flex;align-items:center;gap:.7rem;padding:.6rem .7rem;border-radius:var(--r-sm);
  color:var(--fg-muted);font-size:.875rem;transition:background .14s var(--ease),color .14s var(--ease);
}
.rail a:hover{background:rgb(244 226 195 / .05);color:var(--fg)}
.rail a[aria-current="page"]{background:var(--brick-600);color:var(--cream-050)}
.rail a svg{flex:none;opacity:.9}
.rail a .count{
  margin-left:auto;font-family:var(--font-mono);font-size:.625rem;
  background:rgb(244 226 195 / .1);padding:.1rem .38rem;border-radius:99px;
}
.rail a[aria-current="page"] .count{background:rgb(0 0 0 / .25)}
.rail__user{display:flex;align-items:center;gap:.6rem;padding:.6rem .7rem;border-top:1px solid var(--line-soft)}
.rail__user div{min-width:0}
.rail__user b{display:block;font-size:.8125rem}
.rail__user span{font-size:.6875rem;color:var(--fg-faint)}
/* .rail__user span outranks .avatar, so the monogram needs its colour restated
   here or it inherits the faint label grey and drops under 4.5:1 */
.rail__user .avatar{color:var(--cream-200)}
.avatar{width:30px;height:30px;border-radius:99px;background:var(--slate-700);
  display:grid;place-items:center;font-size:.625rem;font-weight:700;flex:none;
  color:var(--cream-200);letter-spacing:.02em}

.main{min-width:0;display:flex;flex-direction:column;padding-bottom:72px}
@media (min-width:900px){ .main{padding-bottom:0} }

/* topbar */
.top{
  position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:.8rem;
  /* sticky already establishes a containing block for the centred mark */
  padding:.75rem 1rem;background:rgb(6 20 26 / .92);backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line);min-height:60px;
}
.top__title{min-width:0}
.top__title h1{
  font-family:var(--font-display);font-variation-settings:"wdth" 112,"wght" 800;
  text-transform:uppercase;font-size:1.0625rem;letter-spacing:-.01em;line-height:1.1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.top__title p{font-size:.75rem;color:var(--fg-faint);margin-top:1px}
.top__actions{margin-left:auto;display:flex;gap:.5rem;align-items:center}
/* The mark is a square monogram tile, not an image. `width:auto` collapsed it
   to a sliver against the title on every phone. It gets both dimensions. */
.top .mobile-logo{width:30px;height:30px;flex:none;font-size:.75rem}
@media (min-width:900px){ .top .mobile-logo{display:none} }

.wrap{padding:1rem;display:grid;gap:1rem;align-content:start}
@media (min-width:900px){ .wrap{padding:1.35rem 1.5rem} }

/* ---- controls -------------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
  min-height:40px;padding:.5rem 1rem;border:1px solid transparent;border-radius:var(--r-sm);
  background:var(--brick-600);color:var(--cream-050);cursor:pointer;
  font-family:var(--font-display);font-variation-settings:"wdth" 106,"wght" 700;
  font-size:.8125rem;letter-spacing:.03em;text-transform:uppercase;white-space:nowrap;
  transition:background .14s var(--ease),transform .14s var(--snap),border-color .14s var(--ease);
}
.btn:hover{background:var(--brick-500);transform:translateY(-1px)}
.btn--ghost{background:transparent;border-color:var(--line);color:var(--fg)}
.btn--ghost:hover{background:rgb(244 226 195 / .05);border-color:var(--fg-muted)}
.btn--sm{min-height:34px;padding:.35rem .7rem;font-size:.75rem}
.btn--block{width:100%}
.btn--go{background:var(--green-600)}
.btn--go:hover{background:var(--green-400);color:var(--ink-950)}

.field{display:grid;gap:.3rem}
.field label{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--fg-faint)}
.input,.field input,.field select,.field textarea{
  width:100%;background:var(--ink-900);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:.6rem .7rem;font-size:max(16px,.875rem);color:var(--fg);
  transition:border-color .14s var(--ease),box-shadow .14s var(--ease);
}
@media (min-width:900px){ .input,.field input,.field select,.field textarea{font-size:.875rem} }
.input:focus,.field :is(input,select,textarea):focus{
  outline:none;border-color:var(--amber-400);box-shadow:0 0 0 3px rgb(232 163 61 / .14)}

.search{flex:1;max-width:340px;position:relative;display:none}
@media (min-width:640px){ .search{display:block} }
.search input{padding-left:2.1rem}
.search svg{position:absolute;left:.65rem;top:50%;transform:translateY(-50%);color:var(--fg-faint)}

/* ---- status ---------------------------------------------------------- */
.chip{
  display:inline-flex;align-items:center;gap:.35rem;
  font-family:var(--font-mono);font-size:.625rem;letter-spacing:.1em;text-transform:uppercase;
  padding:.22rem .5rem;border-radius:99px;border:1px solid currentColor;white-space:nowrap;
}
.chip::before{content:"";width:5px;height:5px;border-radius:99px;background:currentColor}
.chip--wait{color:var(--brick-500)}
.chip--est{color:var(--amber-400)}
.chip--go{color:var(--green-400)}
.chip--idle{color:var(--slate-400)}
.chip--done{color:var(--steel-300)}
.chip--flat{border-color:transparent;background:rgb(244 226 195 / .08)}

/* ---- panels ---------------------------------------------------------- */
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
/* Every panel header takes the shop's accent, the same treatment as a board
   column. On a page with six stacked panels the titles were the only thing
   separating them and they were the same grey as the body text; on a phone,
   where the panels stack into one long column, that is the whole navigation.
   --brand is the tenant's own colour, so this follows a re-skin for free. */
.panel__hd{
  display:flex;align-items:center;gap:.6rem;padding:.7rem .9rem;
  border-bottom:1px solid color-mix(in srgb, var(--brand) 30%, var(--line));
  background:color-mix(in srgb, var(--brand) 12%, var(--panel-2));
}
.panel__hd h2,.panel__hd h3{
  font-family:var(--font-mono);font-size:.6875rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--brand-head);
}
.panel__hd .btn{margin-left:auto}
.panel__bd{padding:.9rem}
.panel__bd--flush{padding:0}

.grid-2{display:grid;gap:1rem}
@media (min-width:1000px){ .grid-2{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);align-items:start} }

/* ---- stat tiles ------------------------------------------------------ */
.stats{display:grid;gap:.7rem;grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:760px){ .stats{grid-template-columns:repeat(4,minmax(0,1fr))} }
.stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);padding:.85rem .95rem}
.stat dt{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fg-faint)}
.stat dd{margin:.35rem 0 0;font-family:var(--font-display);
  font-variation-settings:"wdth" 110,"wght" 800;font-size:1.5rem;line-height:1}
.stat .delta{font-family:var(--font-mono);font-size:.6875rem;margin-top:.3rem;display:block}
.delta--up{color:var(--green-400)} .delta--down{color:var(--brick-500)}

/* ---- board ----------------------------------------------------------- */
.board{display:grid;gap:.8rem;grid-template-columns:1fr}
@media (min-width:900px){
  .board{grid-auto-flow:column;grid-auto-columns:minmax(240px,1fr);overflow-x:auto;
    padding-bottom:.5rem;scrollbar-width:thin}
}
.col{background:var(--ink-900);border:1px solid var(--line);border-radius:var(--r-md);
  display:flex;flex-direction:column;min-height:0}
/* Column headers carry the shop's own accent, not ours. On a wide board the
   five columns run together as grey bands; on a phone they are the only thing
   telling you which stage you have scrolled into. --brand is the tenant's
   colour, so this re-skins per shop with nothing else to set. --brand-text is
   its readable-as-text variant, which differs on dark. */
.col__hd{display:flex;align-items:center;gap:.5rem;padding:.65rem .75rem;
  background:color-mix(in srgb, var(--brand) 12%, transparent);
  border-bottom:1px solid color-mix(in srgb, var(--brand) 30%, transparent);
  border-radius:calc(var(--r-md) - 1px) calc(var(--r-md) - 1px) 0 0}
.col__hd b{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--brand-head)}
/* The count stays neutral. Brand at 75% opacity measured 4.47:1 on the tint,
   and dimming the accent to make it secondary is what broke it. Muted text is
   already the right weight for a count and already clears. */
.col__hd .n{margin-left:auto;font-family:var(--font-mono);font-size:.625rem;
  color:var(--fg-muted)}
/* An empty column should not shout as loudly as one with work in it. */
.col--empty .col__hd{background:none;border-bottom-color:var(--line-soft)}
.col--empty .col__hd b,.col--empty .col__hd .n{color:var(--fg-faint)}
.col__bd{padding:.5rem;display:grid;gap:.5rem;align-content:start}

.card{
  display:block;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:.7rem .75rem;transition:border-color .14s var(--ease),transform .14s var(--snap);
}
.card:hover{border-color:rgb(244 226 195 / .32);transform:translateY(-1px)}
.card__top{display:flex;align-items:baseline;gap:.5rem;margin-bottom:.3rem}
.card__no{font-family:var(--font-mono);font-size:.625rem;color:var(--amber-400)}
.card__top .chip{margin-left:auto}
.card h3{font-family:var(--font-display);font-variation-settings:"wdth" 106,"wght" 700;
  font-size:.9375rem;line-height:1.2}
.card .veh{font-size:.75rem;color:var(--fg-muted);margin-top:.15rem}
.card__ft{display:flex;align-items:center;gap:.5rem;margin-top:.55rem;
  padding-top:.5rem;border-top:1px solid var(--line-soft);
  font-family:var(--font-mono);font-size:.625rem;color:var(--fg-faint)}
.card__ft .amt{margin-left:auto;color:var(--cream-100)}
.card--flag{border-left:2px solid var(--brick-500)}

/* ---- tables / rows --------------------------------------------------- */
.rows{display:grid}
.row{display:grid;gap:.15rem .8rem;padding:.7rem .9rem;border-bottom:1px solid var(--line-soft);
  align-items:center;grid-template-columns:1fr auto}
.row:last-child{border-bottom:0}
.row:hover{background:rgb(244 226 195 / .03)}
.row b{display:block;font-weight:650;font-size:.875rem;margin-bottom:1px}
.row span{display:block;font-size:.75rem;color:var(--fg-muted)}
.row .amt{font-family:var(--font-mono);font-size:.8125rem;text-align:right}

/* ---- estimate lines -------------------------------------------------- */
.lines{width:100%;border-collapse:collapse}
.lines th{
  text-align:left;font-family:var(--font-mono);font-size:.625rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fg-faint);font-weight:500;
  padding:.5rem .6rem;border-bottom:1px solid var(--line);
}
.lines td{padding:.55rem .6rem;border-bottom:1px solid var(--line-soft);font-size:.8125rem;vertical-align:top}
.lines tr:last-child td{border-bottom:0}
.lines .num{font-family:var(--font-mono);text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.lines .kind{font-family:var(--font-mono);font-size:.5625rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-faint)}
.lines tr.declined td{opacity:.42;text-decoration:line-through}
.totals{display:grid;gap:.35rem;padding:.75rem .9rem;border-top:1px solid var(--line);background:var(--ink-900)}
.totals div{display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:.8125rem;color:var(--fg-muted)}
.totals .grand{font-size:1rem;color:var(--fg);font-weight:700;padding-top:.4rem;
  border-top:1px solid var(--line-soft);margin-top:.2rem}

/* ---- inspection ------------------------------------------------------ */
.insp{display:grid}
.insp__item{display:grid;grid-template-columns:auto 1fr auto;gap:.7rem;align-items:center;
  padding:.6rem .9rem;border-bottom:1px solid var(--line-soft)}
.insp__item:last-child{border-bottom:0}
.dot{width:9px;height:9px;border-radius:99px;flex:none}
.dot--g{background:var(--green-400)} .dot--y{background:var(--amber-400)} .dot--r{background:var(--brick-500)}
.insp__item b{font-size:.8125rem;font-weight:600;display:block}
.insp__item span{font-size:.6875rem;color:var(--fg-faint);font-family:var(--font-mono)}
.insp__thumb{width:44px;height:44px;border-radius:6px;object-fit:cover;border:1px solid var(--line)}

/* ---- messages -------------------------------------------------------- */
.thread{display:grid;gap:.6rem;padding:.9rem;max-height:420px;overflow-y:auto}
.msg{max-width:78%;padding:.55rem .75rem;border-radius:14px;font-size:.8125rem;line-height:1.45}
.msg--in{background:var(--panel-2);border-bottom-left-radius:4px;justify-self:start}
.msg--out{background:var(--brick-600);color:var(--cream-050);border-bottom-right-radius:4px;justify-self:end}
.msg time{display:block;font-family:var(--font-mono);font-size:.5625rem;opacity:.7;margin-top:.25rem}
.composer{display:flex;gap:.5rem;padding:.75rem;border-top:1px solid var(--line);background:var(--ink-900)}
.composer input{flex:1}

/* ---- bottom tabs (mobile) -------------------------------------------- */
.tabs{
  position:fixed;left:0;right:0;bottom:0;z-index:40;display:grid;grid-auto-flow:column;
  background:rgb(10 29 36 / .96);backdrop-filter:blur(12px);border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
@media (min-width:900px){ .tabs{display:none} }
.tabs a{display:grid;justify-items:center;gap:2px;padding:.55rem .3rem .5rem;
  color:var(--fg-faint);font-family:var(--font-mono);font-size:.5625rem;
  letter-spacing:.06em;text-transform:uppercase;min-height:56px;align-content:center}
.tabs a[aria-current="page"]{color:var(--amber-400)}

/* ---- misc ------------------------------------------------------------ */
.muted{color:var(--fg-muted);font-size:.8125rem}
.kv{display:grid;grid-template-columns:auto 1fr;gap:.35rem .9rem;font-size:.8125rem}
.kv dt{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-faint);align-self:center}
.kv dd{margin:0}
.note{background:rgb(232 163 61 / .1);border:1px solid rgb(232 163 61 / .3);
  border-radius:var(--r-sm);padding:.6rem .75rem;font-size:.75rem;color:var(--amber-400)}
.proto{
  position:fixed;left:50%;bottom:76px;transform:translateX(-50%);z-index:60;
  background:var(--amber-400);color:var(--ink-950);border-radius:99px;
  padding:.3rem .8rem;font-family:var(--font-mono);font-size:.5625rem;
  letter-spacing:.12em;text-transform:uppercase;white-space:nowrap;
}
@media (min-width:900px){ .proto{bottom:1rem;left:auto;right:1rem;transform:none} }

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;transition-duration:.001ms!important}
}

/* ---- approval line picker (customer view) ---------------------------- */
.opt-row{
  display:grid;grid-template-columns:auto 1fr auto;gap:.7rem;align-items:center;
  padding:.7rem .8rem;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--ink-900);cursor:pointer;min-height:56px;
  transition:border-color .14s var(--ease),background .14s var(--ease);
}
.opt-row:hover{border-color:var(--fg-muted)}
.opt-row:has(input:checked){border-color:var(--green-600);background:rgb(47 125 92 / .1)}
.opt-row:has(input:disabled){opacity:.65;cursor:default}
.opt-row input{width:20px;height:20px;accent-color:var(--green-600);flex:none}
.opt-row b{display:block;font-size:.875rem;font-weight:650}
.opt-row small{display:block;font-size:.75rem;color:var(--fg-faint);margin-top:1px}
.opt-row em{font-style:normal;font-size:.875rem}

/* ---- report bars ----------------------------------------------------- */
.bars{display:grid;gap:.7rem}
.bar{display:grid;grid-template-columns:7.5rem 1fr auto;gap:.7rem;align-items:center}
.bar span{font-size:.75rem;color:var(--fg-muted)}
.bar i{height:8px;border-radius:99px;background:rgb(244 226 195 / .1);position:relative;display:block}
.bar i::after{content:"";position:absolute;inset-block:0;left:0;width:var(--w);
  border-radius:99px;background:linear-gradient(90deg,var(--amber-500),var(--amber-400))}
.bar em{font-style:normal;font-size:.75rem;color:var(--fg)}

/* Customer-facing pages render without the shop shell. */
.app--bare{grid-template-columns:1fr}
.app--bare .main{padding-bottom:2rem}
.app--bare .top{justify-content:center;text-align:center}
.app--bare .top__actions{display:none}
.app--bare .top__title h1{white-space:normal}

/* Five board columns fit a laptop without clipping the last one. */
@media (min-width:900px){ .board{grid-auto-columns:minmax(212px,1fr)} }

/* ===========================================================================
   Charts. Palette validated with the dataviz validator against the panel
   surface #0e262d: categorical #BE7A00 / #2B93C4 (adjacent CVD ΔE 21.6, normal
   25.2), ordinal amber ramp for ordered buckets. Status hues stay reserved for
   good/bad and are never used as a series.
   =========================================================================== */
:root{
  --cat-1:#BE7A00;      /* Labor */
  --cat-2:#2B93C4;      /* Parts */
  --seq:#C98A17;        /* single-series magnitude */
  --ord-1:#F0C88A; --ord-2:#DFA855; --ord-3:#C98A17; --ord-4:#8A5E10;
  --grid:rgb(244 226 195 / .09);
  --demote:#5d757f;     /* emphasis: recessive but still readable */
}

.chart{display:grid;gap:.9rem}
.chart figcaption{font-size:.75rem;color:var(--fg-faint);line-height:1.5}

/* legend — always present for >=2 series */
.legend{display:flex;flex-wrap:wrap;gap:.9rem;font-size:.75rem;color:var(--fg-muted)}
.legend span{display:inline-flex;align-items:center;gap:.4rem}
.legend i{width:10px;height:10px;border-radius:3px;flex:none}

/* horizontal bars — magnitude, one hue, direct-labelled */
.hbars{display:grid;gap:.55rem}
.hbar{display:grid;grid-template-columns:8.5rem 1fr auto;gap:.75rem;align-items:center}
.hbar>span{font-size:.75rem;color:var(--fg-muted);text-align:right}
.hbar .track{height:10px;border-radius:3px;background:rgb(244 226 195 / .07);position:relative}
.hbar .track i{position:absolute;inset-block:0;left:0;width:var(--w);border-radius:3px;
  background:var(--c,var(--seq))}
.hbar em{font-style:normal;font-family:var(--font-mono);font-size:.75rem;
  font-variant-numeric:tabular-nums;color:var(--fg);min-width:4.5rem;text-align:right}

/* stacked part-to-whole, 2px surface gap between segments */
.stack{display:flex;height:14px;border-radius:4px;overflow:hidden;gap:2px;background:transparent}
.stack i{display:block;height:100%}
.stack i:first-child{border-radius:4px 0 0 4px}
.stack i:last-child{border-radius:0 4px 4px 0}

/* meter — one ratio against a limit */
.meter{display:grid;gap:.4rem}
.meter .track{height:12px;border-radius:4px;background:rgb(244 226 195 / .07);position:relative}
.meter .track i{position:absolute;inset-block:0;left:0;width:var(--w);border-radius:4px;background:var(--seq)}
.meter .track b{position:absolute;top:-4px;bottom:-4px;width:2px;left:var(--target);
  background:var(--cream-200);opacity:.75}
.meter__row{display:flex;justify-content:space-between;font-family:var(--font-mono);
  font-size:.6875rem;color:var(--fg-faint)}

/* sparkline inside a stat tile */
.stat svg{margin-top:.5rem;display:block;width:100%;height:30px;overflow:visible}

/* table view — identity never by colour alone */
.dv-table{width:100%;border-collapse:collapse;margin-top:.2rem}
.dv-table th,.dv-table td{text-align:left;padding:.4rem .5rem;font-size:.75rem;
  border-bottom:1px solid var(--line-soft)}
.dv-table th{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-faint);font-weight:500}
.dv-table td:not(:first-child),.dv-table th:not(:first-child){
  text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums}
details.dv{margin-top:.7rem}
details.dv summary{font-family:var(--font-mono);font-size:.625rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-faint);cursor:pointer;padding:.3rem 0}
details.dv summary:hover{color:var(--fg-muted)}

.kpi-note{font-size:.75rem;color:var(--fg-muted);line-height:1.55}
.kpi-note b{color:var(--fg)}

/* ===========================================================================
   Light theme — a separate palette designed for a white surface, not the dark
   one inverted. Neutrals are cool-warm grays rather than tinted brand slate;
   accents are deepened so they hold contrast on white. Every pair below was
   measured, not eyeballed:
     ink 15.9:1 · muted 6.0:1 · faint 3.5:1 · white-on-brick 6.8:1
     white-on-green 5.3:1 · chart cat-1 3.5:1 · cat-2 3.5:1
   Chart identity colours are unchanged across themes so a series keeps its hue.
   =========================================================================== */
[data-theme="light"]{
  color-scheme:light;

  --bg:#F7F6F3;          /* page */
  --panel:#FFFFFF;       /* cards, the reading surface */
  --panel-2:#F1EFEA;     /* headers, inset strips */
  --ink-900:#FFFFFF;     /* inputs sit on white, not a darker well */
  --ink-950:#F7F6F3;

  --fg:#16242A;
  --fg-muted:#56666E;
  --fg-faint:#7B8B93;

  --line:rgb(22 36 42 / .13);
  --line-soft:rgb(22 36 42 / .07);

  --brick-600:#A6322A;   /* primary action */
  --brick-500:#8E2A23;   /* hover: darker on light, not lighter */
  --green-600:#1D7A55;
  --green-400:#1A6B4A;
  --amber-400:#8A5A05;   /* label/status text weight, not the fill */
  --cream-050:#FFFFFF;
  --cream-100:#16242A;
  --cream-200:#16242A;
  --steel-300:#56666E;
  --slate-400:#7B8B93;
  --slate-700:#E7E4DD;
  --slate-800:#F1EFEA;

  /* charts: identity constant, ordinal restepped for the white surface */
  --seq:#BE7A00;
  --ord-1:#D9AC5C; --ord-2:#C4922E; --ord-3:#A06F10; --ord-4:#63470C;
  --grid:rgb(22 36 42 / .09);
  --demote:#A9B4B9;
}

html[data-theme="light"]{background:var(--bg)}
[data-theme="light"] body{background:var(--bg);color:var(--fg)}
[data-theme="light"] .rail{background:#FFFFFF;border-right-color:var(--line)}
[data-theme="light"] .rail a:hover{background:rgb(22 36 42 / .05)}
[data-theme="light"] .rail a[aria-current="page"]{background:var(--brick-600);color:#fff}
[data-theme="light"] .rail__brand b{color:var(--fg)}
[data-theme="light"] .top{background:rgb(255 255 255 / .93);border-bottom-color:var(--line)}
[data-theme="light"] .tabs{background:rgb(255 255 255 / .96);border-top-color:var(--line)}
[data-theme="light"] .tabs a[aria-current="page"]{color:var(--brick-600)}
[data-theme="light"] .panel{box-shadow:0 1px 2px rgb(22 36 42 / .05)}
[data-theme="light"] .stat{box-shadow:0 1px 2px rgb(22 36 42 / .05)}
[data-theme="light"] .col{background:#FBFAF8}
[data-theme="light"] .card{background:#FFFFFF;box-shadow:0 1px 2px rgb(22 36 42 / .05)}
[data-theme="light"] .card:hover{border-color:rgb(22 36 42 / .3)}
[data-theme="light"] .totals{background:var(--panel-2)}
[data-theme="light"] .hbar .track,[data-theme="light"] .meter .track{background:rgb(22 36 42 / .08)}
[data-theme="light"] .meter .track b{background:var(--fg);opacity:.55}
[data-theme="light"] .msg--in{background:var(--panel-2);color:var(--fg)}
[data-theme="light"] .msg--out{background:var(--brick-600);color:#fff}
[data-theme="light"] .composer{background:var(--panel-2)}
[data-theme="light"] .note{background:rgb(190 122 0 / .09);border-color:rgb(190 122 0 / .32);color:#7A4F04}
[data-theme="light"] .chip--wait{color:#96291F}
[data-theme="light"] .chip--est{color:#8A5A05}
[data-theme="light"] .chip--go{color:#1A6B4A}
[data-theme="light"] .chip--idle{color:#6C7C84}
[data-theme="light"] .chip--flat{background:rgb(22 36 42 / .06)}
[data-theme="light"] .dot--g{background:#1A6B4A}
[data-theme="light"] .dot--y{background:#B57E0A}
[data-theme="light"] .dot--r{background:#B3392E}
[data-theme="light"] .delta--up{color:#1A6B4A}
[data-theme="light"] .delta--down{color:#96291F}
[data-theme="light"] .avatar{background:var(--panel-2);color:var(--fg)}
[data-theme="light"] .btn--ghost{color:var(--fg);border-color:var(--line)}
[data-theme="light"] .btn--ghost:hover{background:rgb(22 36 42 / .04)}
[data-theme="light"] .proto{background:#16242A;color:#FFFFFF}
[data-theme="light"] .insp__thumb{border-color:var(--line)}
[data-theme="light"] .opt-row{background:#FFFFFF}
[data-theme="light"] .opt-row:has(input:checked){background:rgb(29 122 85 / .07);border-color:var(--green-600)}
[data-theme="light"] .lines tr.declined td{opacity:.45}

/* theme toggle */
.theme-btn{
  display:grid;place-items:center;width:38px;height:38px;flex:none;
  background:transparent;border:1px solid var(--line);border-radius:var(--r-sm);
  cursor:pointer;color:var(--fg-muted);
}
.theme-btn:hover{color:var(--fg);border-color:var(--fg-muted)}
.theme-btn .sun{display:none}
[data-theme="light"] .theme-btn .sun{display:block}
[data-theme="light"] .theme-btn .moon{display:none}

/* ===========================================================================
   Filter bar. Per the dataviz interaction rules: one left-aligned row above the
   content it scopes, date range first, never inside a chart card. Everything
   below re-renders against the same slice so the numbers always agree.
   =========================================================================== */
.filters{
  display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;
  padding:.7rem .8rem;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-md);position:sticky;top:60px;z-index:20;
}
[data-theme="light"] .filters{box-shadow:0 1px 2px rgb(22 36 42 / .05)}
.filters__sep{width:1px;align-self:stretch;background:var(--line);margin-inline:.15rem}
.filters__spacer{margin-left:auto}

.fsel{position:relative;display:inline-flex}
.fsel select{
  -webkit-appearance:none;appearance:none;cursor:pointer;
  background:var(--ink-900);border:1px solid var(--line);border-radius:var(--r-sm);
  color:var(--fg);font-size:max(16px,.8125rem);min-height:38px;
  padding:.4rem 2rem .4rem .7rem;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:right 1.05rem center,right .8rem center;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;
}
@media (min-width:900px){ .fsel select{font-size:.8125rem} }
.fsel select:hover{border-color:var(--fg-muted)}
.fsel--lead select{font-family:var(--font-display);font-variation-settings:"wdth" 104,"wght" 650}

.pill-group{display:inline-flex;border:1px solid var(--line);border-radius:999px;overflow:hidden}
.pill-group button{
  appearance:none;background:transparent;border:0;cursor:pointer;
  padding:.45rem .8rem;min-height:38px;color:var(--fg-muted);
  font-family:var(--font-mono);font-size:.6875rem;letter-spacing:.08em;text-transform:uppercase;
}
.pill-group button+button{border-left:1px solid var(--line)}
.pill-group button[aria-pressed="true"]{background:var(--brick-600);color:#fff}
.pill-group button:hover:not([aria-pressed="true"]){background:rgb(128 128 128 / .08);color:var(--fg)}

.applied{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;font-size:.75rem;color:var(--fg-faint)}
.applied b{
  display:inline-flex;align-items:center;gap:.35rem;color:var(--fg);
  background:rgb(128 128 128 / .1);border-radius:999px;padding:.2rem .55rem;
  font-weight:600;font-size:.6875rem;
}

/* Refetch keeps the frame: hold the previous render, dimmed. */
.is-loading{opacity:.45;transition:opacity .12s var(--ease)}

/* ---- forecast ---------------------------------------------------------- */
.fc{display:grid;gap:.6rem}
.fc__plot{position:relative;height:190px}
.fc__plot svg{width:100%;height:100%;overflow:visible}
.fc__band{fill:var(--seq);opacity:.14}
.fc__actual{fill:none;stroke:var(--seq);stroke-width:2;vector-effect:non-scaling-stroke;
  stroke-linecap:round;stroke-linejoin:round}
.fc__proj{fill:none;stroke:var(--seq);stroke-width:2;stroke-dasharray:5 4;
  vector-effect:non-scaling-stroke;stroke-linecap:round}
.fc__grid{stroke:var(--grid);stroke-width:1;vector-effect:non-scaling-stroke}
.fc__divider{stroke:var(--fg-faint);stroke-width:1;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}
.fc__xlab{display:flex;justify-content:space-between;font-family:var(--font-mono);
  font-size:.625rem;color:var(--fg-faint)}
.fc__method{font-size:.75rem;color:var(--fg-muted);line-height:1.55;
  border-left:2px solid var(--line);padding-left:.7rem}

/* ---- metric builder ---------------------------------------------------- */
.builder{display:grid;gap:.7rem}
@media (min-width:720px){ .builder{grid-template-columns:repeat(3,minmax(0,1fr))} }
.builder .field label{margin-bottom:.1rem}
.mchips{display:flex;flex-wrap:wrap;gap:.4rem}
.mchip{
  appearance:none;cursor:pointer;background:transparent;color:var(--fg-muted);
  border:1px solid var(--line);border-radius:999px;padding:.35rem .7rem;min-height:34px;
  font-family:var(--font-mono);font-size:.625rem;letter-spacing:.08em;text-transform:uppercase;
}
.mchip[aria-pressed="true"]{background:var(--seq);border-color:var(--seq);color:#fff}
[data-theme="light"] .mchip[aria-pressed="true"]{color:#fff}
.mchip:hover:not([aria-pressed="true"]){color:var(--fg);border-color:var(--fg-muted)}

/* ===========================================================================
   Typography correction.
   Mono was doing a job it is bad at: prose labels at 10–11px with .14em
   tracking. Mono earns its place on figures that must align in a column, and
   nowhere else. Labels move to the text face at larger sizes with normal
   tracking, and the whole scale steps up — this is a tool read at arm's length
   on a bench, not a marketing page.
   =========================================================================== */
:root{
  --fs-body:1rem;        /* 16 */
  --fs-sm:.875rem;       /* 14 — secondary prose */
  --fs-xs:.8125rem;      /* 13 — captions, meta */
  --fs-label:.75rem;     /* 12 — uppercase labels, text face */
  --fs-data:.875rem;     /* 14 — mono figures */
  --track-label:.055em;  /* was .12–.16em, which broke word shapes */
}

body{font-size:var(--fs-body);line-height:1.55}

/* Labels: text face, heavier, roomier, far less tracking. */
.panel__hd h2,.panel__hd h3,
.stat dt,.col__hd b,.field label,.rail a .count,
.lines th,.dv-table th,.kv dt,.insp__item span,
.detail__label,.tabs a,.ticket__no,.applied b,
.pill-group button,.mchip,details.dv summary,
.legend span,.hbar>span,.meter__row,.fc__xlab,.bar span{
  font-family:var(--font-text);
  letter-spacing:var(--track-label);
}
.panel__hd h2,.panel__hd h3{font-size:var(--fs-label);font-weight:700}
.stat dt{font-size:var(--fs-label);font-weight:600}
.col__hd b{font-size:var(--fs-label);font-weight:700}
.field label{font-size:var(--fs-label);font-weight:600;color:var(--fg-muted)}
.lines th,.dv-table th{font-size:var(--fs-label);font-weight:600}
.kv dt{font-size:var(--fs-label);font-weight:600}
.tabs a{font-size:.6875rem;font-weight:600;letter-spacing:.02em}
.pill-group button,.mchip{font-size:var(--fs-label);font-weight:600}
details.dv summary{font-size:var(--fs-label);font-weight:600}
.legend span{font-size:var(--fs-xs)}
.hbar>span,.bar span{font-size:var(--fs-xs)}
.applied b{font-size:var(--fs-label)}

/* Figures stay mono — that is what it is for. */
.mono,.lines .num,.hbar em,.bar em,.stat dd,
.dv-table td:not(:first-child),.meter__row span:last-child,
.row .amt,.card__ft .amt,.card__no,.fc__xlab span{
  font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;
  letter-spacing:0;
}
.hbar em,.bar em{font-size:var(--fs-data)}
.lines .num{font-size:var(--fs-data)}
.meter__row{font-size:var(--fs-xs)}
.meter__row span:last-child{font-size:var(--fs-data)}

/* Prose sizes up across the board. */
.muted,.kpi-note,.chart figcaption,.fc__method,
.row span,.card .veh,.insp__item span,.stat .delta{font-size:var(--fs-xs)}
.lines td,.dv-table td,.kv dd,.row b,.card h3,.insp__item b{font-size:var(--fs-sm)}
.card h3{font-size:var(--fs-body)}
.chart figcaption,.kpi-note{line-height:1.6}
.insp__item span{font-family:var(--font-mono);font-size:var(--fs-xs)}

/* Status chips were 10px mono; make them legible without shouting. */
.chip{font-family:var(--font-text);font-size:.6875rem;font-weight:650;
  letter-spacing:.04em;padding:.28rem .6rem}
.svc__cat,.job__code{font-size:.6875rem;letter-spacing:.05em}

/* Light mode: thin gray on white loses too much weight at small sizes. */
[data-theme="light"]{--fg-muted:#48585F;--fg-faint:#6B7B83}
[data-theme="light"] .chart figcaption,
[data-theme="light"] .kpi-note,
[data-theme="light"] .muted{color:#48585F}

/* Headings gain air now that labels are larger. */
.panel__hd{padding:.8rem .95rem}
.stat dt{margin-bottom:.15rem}
.top__title h1{font-size:1.125rem}
.top__title p{font-size:var(--fs-xs)}

/* A headline figure is a display element, not a data column — mono's even
   advance helps a table and hurts a hero number. Tabular figures stay on so
   tiles don't jitter when values change. */
.stat dd{
  font-family:var(--font-display);
  font-variation-settings:"wdth" 108,"wght" 800;
  font-variant-numeric:tabular-nums;
  font-size:1.75rem;letter-spacing:-.015em;
}
.stat .delta{font-family:var(--font-text);font-size:var(--fs-xs);letter-spacing:0}
.top__title h1{letter-spacing:-.005em}

/* Stat tiles double as filters on the board. */
.stat[type="button"],button.stat{
  text-align:left;cursor:pointer;font:inherit;color:inherit;
  transition:border-color .14s var(--ease),transform .14s var(--snap),background .14s var(--ease);
}
button.stat:hover{border-color:var(--fg-muted);transform:translateY(-1px)}
button.stat[aria-pressed="true"]{border-color:var(--brick-600);box-shadow:inset 0 0 0 1px var(--brick-600)}
button.stat[aria-pressed="true"] dt{color:var(--brick-600)}

.col[hidden],.card[hidden],tr[hidden]{display:none}
.col--empty .col__bd::after{
  content:"Nothing here";display:block;padding:.9rem .25rem;
  font-size:var(--fs-xs);color:var(--fg-faint);
}
.empty-board{
  grid-column:1/-1;padding:2.5rem 1rem;text-align:center;
  color:var(--fg-faint);font-size:var(--fs-sm);
}
.empty-board[hidden]{display:none}
[data-view][hidden]{display:none}

/* Collapsible filters. The toggle keeps a count badge when collapsed, so a
   hidden filter set can never silently explain missing rows. */
.filters{flex-wrap:wrap}
.filters__toggle{
  display:inline-flex;align-items:center;gap:.45rem;cursor:pointer;
  background:transparent;border:1px solid var(--line);border-radius:var(--r-sm);
  color:var(--fg-muted);min-height:38px;padding:.4rem .7rem;
  font-family:var(--font-text);font-size:var(--fs-label);font-weight:600;
  letter-spacing:var(--track-label);
}
.filters__toggle:hover{color:var(--fg);border-color:var(--fg-muted)}
.filters__toggle .chev{transition:transform .18s var(--ease)}
.filters__toggle[aria-expanded="false"] .chev{transform:rotate(-90deg)}
.filters__toggle[aria-expanded="false"]{border-color:var(--fg-muted);color:var(--fg)}
.filters__count{
  display:inline-grid;place-items:center;min-width:17px;height:17px;padding:0 4px;
  border-radius:99px;background:var(--brick-600);color:#fff;
  font-family:var(--font-mono);font-size:.625rem;font-weight:700;
}
.filters__count[hidden]{display:none}

.filters__body{
  display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;
  flex:1 1 100%;
}
@media (min-width:1200px){ .filters__body{flex:1 1 auto} }
.filters__body[hidden]{display:none}

/* ---------------------------------------------------------------------------
   Top bar: the action buttons are nowrap and non-shrinking, so on a narrow
   screen they took the whole row and collapsed the title to its longest word
   (the h1 disappeared entirely and the subtitle broke one word per line).
   The bar now wraps: title keeps a floor width and ellipsises, actions drop to
   their own row and share it.
   --------------------------------------------------------------------------- */
.top{flex-wrap:wrap;row-gap:.55rem}
.top__title{flex:1 1 10rem;min-width:0}
.top__title p{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

@media (max-width:767px){
  .top{padding:.6rem .85rem}
  .top__actions{
    flex:1 1 100%;margin-left:0;order:9;
    justify-content:flex-start;gap:.45rem;
    /* without this the row cannot shrink below its nowrap children */
    min-width:0;
  }
  /* buttons share the row and truncate rather than push the layout wider */
  .top__actions .btn{flex:0 1 auto;min-width:0;padding-inline:.7rem;font-size:.75rem}
  .top__actions .theme-btn,.top__actions .bell__btn{flex:0 0 auto;width:30px;height:30px}
  .search{display:none}          /* search lives in the filter bar on mobile */
  .top__title h1{font-size:1.0625rem}
}
@media (max-width:400px){
  .top__actions{gap:.35rem}
  .top__actions .btn{padding-inline:.5rem;font-size:.6875rem;letter-spacing:0}
  .bell__dot{top:-4px;right:-4px;min-width:14px;height:14px;font-size:.5rem}
}

/* ===========================================================================
   Platform layer. The rail brand, accent and logo are driven by tenant config
   at runtime, so one build serves every shop.
   =========================================================================== */
:root{ --brand:#AD3A30; --brand-hi:#C7473B; --brand-text:var(--brand); }
/* The red used as TEXT is not the red used as a FILL. On a dark ground the fill
   red measures 3.76:1, under the 4.5 needed for text — the same split the brand
   kit documents for buttons. Anything tinting a letterform uses this token. */
[data-theme="dark"]{ --brand-text:var(--brand-hi); }
.btn,[data-theme="light"] .btn{background:var(--brand)}
.btn:hover{background:var(--brand-hi)}
.btn--ghost,[data-theme="light"] .btn--ghost{background:transparent}
.rail a[aria-current="page"],[data-theme="light"] .rail a[aria-current="page"]{background:var(--brand)}
.tabs a[aria-current="page"]{color:var(--brand)}
button.stat[aria-pressed="true"]{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
button.stat[aria-pressed="true"] dt{color:var(--brand)}
.pill-group button[aria-pressed="true"]{background:var(--brand)}
.filters__count{background:var(--brand)}
.msg--out{background:var(--brand)}
.card--flag{border-left-color:var(--brand)}

/* tenant switcher (prototype only) */
.tenant{
  display:flex;align-items:center;gap:.5rem;padding:.5rem .6rem;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel);
}
.tenant select{
  -webkit-appearance:none;appearance:none;background:transparent;border:0;
  color:var(--fg);font-family:var(--font-text);font-size:var(--fs-label);
  font-weight:600;cursor:pointer;padding-right:1rem;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:right .28rem center,right .05rem center;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;
}
.tenant__mark{
  width:24px;height:24px;border-radius:6px;flex:none;display:grid;place-items:center;
  background:var(--brand);color:#fff;font-weight:800;font-size:.6875rem;
}

/* ---- admin console ---------------------------------------------------- */
.tenants{display:grid;gap:.7rem}
.tenant-row{
  display:grid;gap:.5rem .9rem;align-items:center;
  grid-template-columns:auto 1fr auto;
  padding:.85rem .95rem;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-md);
}
/* On a phone the three-column row squeezed the name to two words a line and
   the meta to one word. Stack it: mark and name on one row, meta under. */
@media (max-width:640px){
  .tenant-row{grid-template-columns:auto minmax(0,1fr)}
  .tenant-row > :nth-child(3){grid-column:1 / -1}
}
@media (min-width:900px){
  .tenant-row{grid-template-columns:auto minmax(0,1.4fr) minmax(0,1fr) auto auto}
}
[data-theme="light"] .tenant-row{box-shadow:0 1px 2px rgb(22 36 42 / .05)}
.tenant-row__mark{width:34px;height:34px;border-radius:8px;display:grid;place-items:center;
  color:#fff;font-weight:800;font-size:.8125rem;flex:none}
.tenant-row b{display:block;font-size:var(--fs-sm);font-weight:650}
.tenant-row span{display:block;font-size:var(--fs-xs);color:var(--fg-faint)}
/* Third time this bit: a BEM element that happens to be a <span> loses to the
   block's own `... span` rule defined below it. Any coloured monogram tile needs
   its colour restated after that rule. darkcheck.mjs is what catches it. */
.tenant-row .tenant-row__mark{color:#fff;display:grid}
.tenant-row .feat{font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--fg-muted)}

/* brand picker */
.brand-grid{display:grid;gap:1rem}
.brand-grid > div:first-child{display:grid;gap:.6rem;align-content:start}
@media (min-width:820px){ .brand-grid{grid-template-columns:minmax(0,1fr) 260px} }
.swatch-row{display:flex;flex-wrap:wrap;gap:.5rem}
.swatch{
  width:38px;height:38px;border-radius:8px;cursor:pointer;border:2px solid transparent;
  position:relative;
}
.swatch[aria-pressed="true"]{border-color:var(--fg);box-shadow:0 0 0 2px var(--panel) inset}
.hexline{display:flex;gap:.5rem;align-items:center}
.hexline input[type="color"]{width:44px;height:38px;padding:0;border:1px solid var(--line);
  border-radius:var(--r-sm);background:none;cursor:pointer}
.hexline input[type="text"]{max-width:9rem;font-family:var(--font-mono)}

.contrast{display:grid;gap:.4rem;margin-top:.7rem}
.contrast div{display:flex;justify-content:space-between;gap:1rem;
  font-family:var(--font-mono);font-size:var(--fs-xs)}
.contrast .ok{color:var(--green-400)}
.contrast .bad{color:var(--brick-500)}
[data-theme="light"] .contrast .ok{color:#1A6B4A}
[data-theme="light"] .contrast .bad{color:#96291F}

.preview-card{
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--panel);
}
.preview-card__bar{display:flex;align-items:center;gap:.5rem;padding:.6rem .7rem;
  border-bottom:1px solid var(--line)}
.preview-card__bd{padding:.8rem .7rem;display:grid;gap:.55rem}

/* feature toggles */
.flags{display:grid;gap:1.1rem}
.flag-group h3{font-family:var(--font-text);font-size:var(--fs-label);font-weight:700;
  letter-spacing:var(--track-label);text-transform:uppercase;color:var(--fg-faint);
  padding-bottom:.4rem;border-bottom:1px solid var(--line);margin-bottom:.2rem}
.flag{
  display:grid;grid-template-columns:auto 1fr;gap:.75rem;align-items:start;
  padding:.6rem 0;border-bottom:1px solid var(--line-soft);
}
.flag:last-child{border-bottom:0}
.flag b{display:block;font-size:var(--fs-sm);font-weight:650}
.flag span{display:block;font-size:var(--fs-xs);color:var(--fg-muted);margin-top:1px}
.flag .gate{
  display:inline-block;margin-top:.35rem;font-size:var(--fs-xs);
  color:var(--amber-400);border-left:2px solid currentColor;padding-left:.5rem;
}
[data-theme="light"] .flag .gate{color:#8A5A05}
.flag .needs{font-family:var(--font-mono);font-size:.6875rem;color:var(--fg-faint)}
.flag[data-locked="true"] b,.flag[data-locked="true"] span{opacity:.55}

.sw{
  position:relative;width:40px;height:23px;flex:none;border-radius:99px;cursor:pointer;
  background:rgb(128 128 128 / .3);border:0;transition:background .16s var(--ease);
}
.sw::after{
  content:"";position:absolute;top:2px;left:2px;width:19px;height:19px;border-radius:99px;
  background:#fff;transition:transform .16s var(--snap);
}
.sw[aria-checked="true"]{background:var(--green-600)}
.sw[aria-checked="true"]::after{transform:translateX(17px)}
.sw:disabled{cursor:not-allowed;opacity:.45}
.sw:focus-visible{outline:2px solid var(--amber-400);outline-offset:2px}

/* The `hidden` attribute only wins when nothing sets `display`. These nav items
   set display:flex/grid, so hiding a switched-off feature silently did nothing. */
.rail a[hidden],.tabs a[hidden]{display:none !important}

/* ===========================================================================
   Platform themes. These brand the console itself — Kopperss's product — and
   are deliberately separate from tenant branding, which only ever tints the
   accent inside a tenant's own screens. Every pair below was contrast-checked
   before it shipped (see themecheck.mjs); the weakest in any theme is 3.5:1
   on a non-text element against its panel.
   =========================================================================== */
[data-plat]:not([data-theme="dark"]){--brand-hi:var(--brand)}

/* 1 — Bavarian: deep navy, motorsport blue, white. Cold and engineered. */
[data-plat="bavarian"]{
  color-scheme:dark;
  --bg:#0A1626; --panel:#0F1E33; --panel-2:#16294A; --ink-900:#0A1626; --ink-950:#0A1626;
  --fg:#EAF0F7; --fg-muted:#9DB2C9; --fg-faint:#6E86A1;
  --line:rgb(234 240 247 / .14); --line-soft:rgb(234 240 247 / .07);
  --brand:#0F76C4; --brand-hi:#1789DE;
  --green-600:#2F8A63; --green-400:#3FA679; --amber-400:#D89B2B;
  --brick-600:#E0343F; --brick-500:#EC4D57; --cream-050:#FFFFFF;
  --cream-100:#EAF0F7; --cream-200:#EAF0F7; --steel-300:#9DB2C9; --slate-400:#6E86A1;
  --slate-700:#16294A; --slate-800:#0F1E33;
  --seq:#2E8FD6; --cat-1:#2E8FD6; --cat-2:#C9A227; --grid:rgb(234 240 247 / .09); --demote:#4A6480;
}

/* 2 — Guards: near-white, one hard red, almost no chrome. Restrained. */
[data-plat="guards"]{
  color-scheme:light;
  --bg:#F5F5F4; --panel:#FFFFFF; --panel-2:#EDEDEB; --ink-900:#FFFFFF; --ink-950:#F5F5F4;
  --fg:#111111; --fg-muted:#4B4B4B; --fg-faint:#767674;
  --line:rgb(17 17 17 / .14); --line-soft:rgb(17 17 17 / .07);
  --brand:#C4001A; --brand-hi:#9E0015;
  --green-600:#1C7A4E; --green-400:#166642; --amber-400:#8A6A16;
  --brick-600:#A8121F; --brick-500:#8A0E19; --cream-050:#FFFFFF;
  --cream-100:#111111; --cream-200:#111111; --steel-300:#4B4B4B; --slate-400:#767674;
  --slate-700:#E4E4E1; --slate-800:#EDEDEB;
  --seq:#8A8A87; --cat-1:#C4001A; --cat-2:#5A5A57; --grid:rgb(17 17 17 / .1); --demote:#B8B8B4;
}

/* 2b — Guards after dark. Same restraint, same one red, inverted ground.
   The red has to move: #C4001A on a near-black panel measures 2.90:1, which is
   unreadable. It cannot simply be lightened either — no single red clears 4.5:1
   as text on dark AND holds 4.5:1 behind white button labels. Those pull in
   opposite directions, so this ships two reds: a fill red that white sits on,
   and a lighter one for accent text and charts. */
[data-plat="guards"][data-theme="dark"]{
  color-scheme:dark;
  --bg:#0C0C0B; --panel:#161615; --panel-2:#1F1F1D; --ink-900:#0C0C0B; --ink-950:#0C0C0B;
  --fg:#F5F5F4; --fg-muted:#A5A5A1; --fg-faint:#8A8A86;
  --line:rgb(245 245 244 / .14); --line-soft:rgb(245 245 244 / .07);
  --brand:#E01B2E;                     /* fill: white label 4.81, shape on panel 3.76 */
  --brand-hi:#F0374A;                  /* hover, and readable as text at 4.61 */
  --green-600:#4ECB8D; --green-400:#3FBF80; --amber-400:#E0A93B;
  --brick-600:#F4707A; --brick-500:#E8646E; --cream-050:#FFFFFF;
  --cream-100:#F5F5F4; --cream-200:#F5F5F4; --steel-300:#A5A5A1; --slate-400:#8A8A86;
  --slate-700:#262624; --slate-800:#1F1F1D;
  --seq:#9A9A96; --cat-1:#F0374A; --cat-2:#A8A8A4;
  /* Ramp direction flips with the ground. On white, "more" is darker; on black,
     "more" is more luminous, so ord-4 becomes the lightest step rather than the
     darkest. Keeping the light-theme order here made the busiest cohort cells
     the dimmest ones on screen. */
  --ord-1:#4A3208; --ord-2:#7A5510; --ord-3:#B0851F; --ord-4:#E0B463;
  --grid:rgb(245 245 244 / .1); --demote:#3A3A37;
}
/* The ordinal ramp runs light-to-dark on a dark ground, so the deepest steps
   need light labels rather than the white the light theme uses. */
[data-plat="guards"][data-theme="dark"] .cohort td[data-v="5"],
[data-plat="guards"][data-theme="dark"] .cohort td[data-v="4"]{color:#0C0C0B}
/* --seq is a light grey on the dark ground, so a filled chip needs a dark label
   rather than the white the light theme uses. */
[data-plat="guards"][data-theme="dark"] .mchip[aria-pressed="true"]{color:#0C0C0B}

/* 3 — Racing green: deep green ground, champagne accent. Warm and expensive. */
[data-plat="racing"]{
  color-scheme:dark;
  --bg:#04231E; --panel:#0A312A; --panel-2:#113C34; --ink-900:#04231E; --ink-950:#04231E;
  --fg:#EFEAE0; --fg-muted:#AFC0B7; --fg-faint:#7F948A;
  --line:rgb(239 234 224 / .14); --line-soft:rgb(239 234 224 / .07);
  --brand:#C8A85A; --brand-hi:#DABF7C;
  --green-600:#3F8F6B; --green-400:#5DB98D; --amber-400:#D8B25A;
  --brick-600:#B8544B; --brick-500:#D9645A; --cream-050:#0A2620;
  --cream-100:#EFEAE0; --cream-200:#EFEAE0; --steel-300:#AFC0B7; --slate-400:#7F948A;
  --slate-700:#113C34; --slate-800:#0A312A;
  --seq:#C8A85A; --cat-1:#C8A85A; --cat-2:#5DB98D; --grid:rgb(239 234 224 / .09); --demote:#4E6A61;
}
/* the champagne accent is light, so labels on it must go dark */
[data-plat="racing"] .btn,[data-plat="racing"] .rail a[aria-current="page"],
[data-plat="racing"] .msg--out,[data-plat="racing"] .pill-group button[aria-pressed="true"],
[data-plat="racing"] .filters__count,[data-plat="racing"] .mchip[aria-pressed="true"]{color:#0A2620}

/* 4 — Graphite: cool neutrals, one indigo accent. Technical, tight. */
[data-plat="graphite"]{
  color-scheme:light;
  --bg:#F7F8FA; --panel:#FFFFFF; --panel-2:#EEF1F5; --ink-900:#FFFFFF; --ink-950:#F7F8FA;
  --fg:#14181F; --fg-muted:#515A67; --fg-faint:#7A838F;
  --line:rgb(20 24 31 / .13); --line-soft:rgb(20 24 31 / .06);
  --brand:#4A4FD6; --brand-hi:#3A3EB4;
  --green-600:#177B55; --green-400:#126847; --amber-400:#8A5D08;
  --brick-600:#B4232F; --brick-500:#961A25; --cream-050:#FFFFFF;
  --cream-100:#14181F; --cream-200:#14181F; --steel-300:#515A67; --slate-400:#7A838F;
  --slate-700:#E4E8EE; --slate-800:#EEF1F5;
  --seq:#4A4FD6; --cat-1:#4A4FD6; --cat-2:#0E7C86; --grid:rgb(20 24 31 / .1); --demote:#AEB6C0;
}

/* 5 — Signal: monochrome near-black with a single high-chroma signal. */
[data-plat="signal"]{
  color-scheme:dark;
  --bg:#0B0C0E; --panel:#15171A; --panel-2:#1E2126; --ink-900:#0B0C0E; --ink-950:#0B0C0E;
  --fg:#F2F3F4; --fg-muted:#A7ADB5; --fg-faint:#767D86;
  --line:rgb(242 243 244 / .13); --line-soft:rgb(242 243 244 / .06);
  --brand:#00A67E; --brand-hi:#00C295;
  --green-600:#00A67E; --green-400:#00C295; --amber-400:#C9922B;
  --brick-600:#DC4B4B; --brick-500:#EA6060; --cream-050:#04120E;
  --cream-100:#F2F3F4; --cream-200:#F2F3F4; --steel-300:#A7ADB5; --slate-400:#767D86;
  --slate-700:#1E2126; --slate-800:#15171A;
  --seq:#00A67E; --cat-1:#00A67E; --cat-2:#C9922B; --grid:rgb(242 243 244 / .09); --demote:#4A5058;
}
[data-plat="signal"] .btn,[data-plat="signal"] .rail a[aria-current="page"],
[data-plat="signal"] .msg--out,[data-plat="signal"] .pill-group button[aria-pressed="true"],
[data-plat="signal"] .filters__count{color:#04120E}

/* shared: a platform theme owns the surfaces, so the light-mode overrides that
   assume the Dee's palette must not also apply */
[data-plat] .rail{background:var(--panel);border-right-color:var(--line)}
[data-plat] .top{background:var(--bg);border-bottom-color:var(--line)}
[data-plat] .tabs{background:var(--panel);border-top-color:var(--line)}
[data-plat] .panel,[data-plat] .stat,[data-plat] .card{box-shadow:none}
[data-plat] .col{background:var(--panel-2)}
[data-plat] .totals{background:var(--panel-2)}
[data-plat] .hbar .track,[data-plat] .meter .track{background:rgb(128 128 128 / .18)}

/* ---- theme gallery ----------------------------------------------------- */
.themes{display:grid;gap:1rem}
@media (min-width:820px){ .themes{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1300px){ .themes{grid-template-columns:repeat(3,minmax(0,1fr))} }

.theme-card{
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;
  display:grid;grid-template-rows:auto 1fr auto;
}
.theme-card__hd{
  display:flex;align-items:baseline;gap:.5rem;padding:.75rem .85rem;
  border-bottom:1px solid var(--line);
}
.theme-card__hd b{font-size:var(--fs-sm);font-weight:700}
.theme-card__hd span{font-size:var(--fs-xs);color:var(--fg-faint);margin-left:auto}

/* the miniature paints itself with its own theme tokens */
.mini{padding:.85rem;display:grid;gap:.65rem;background:var(--bg);color:var(--fg)}
.mini__row{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap}
.mini__rail{
  display:flex;align-items:center;gap:.5rem;padding:.45rem .55rem;border-radius:6px;
  background:var(--panel);border:1px solid var(--line);
}
.mini__mark{width:20px;height:20px;border-radius:5px;background:var(--brand);flex:none}
.mini__nav{
  padding:.22rem .5rem;border-radius:5px;font-size:.6875rem;font-weight:600;
  background:var(--brand);color:var(--cream-050);
}
.mini__navi{padding:.22rem .5rem;font-size:.6875rem;color:var(--fg-muted)}
.mini__panel{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:.6rem}
.mini__stat b{display:block;font-family:var(--font-display);
  font-variation-settings:"wdth" 108,"wght" 800;font-size:1.15rem;line-height:1}
.mini__stat span{font-size:.625rem;color:var(--fg-faint)}
.mini__btn{
  padding:.32rem .7rem;border-radius:6px;background:var(--brand);color:var(--cream-050);
  font-size:.6875rem;font-weight:700;border:0;
}
.mini__btn--g{background:transparent;color:var(--fg);border:1px solid var(--line)}
.mini__bar{height:7px;border-radius:99px;background:rgb(128 128 128 / .2);position:relative}
.mini__bar i{position:absolute;inset-block:0;left:0;width:var(--w);border-radius:99px;background:var(--seq)}
.mini__chips{display:flex;gap:.3rem;flex-wrap:wrap}
.mini__chip{font-size:.5625rem;font-weight:700;padding:.14rem .4rem;border-radius:99px;border:1px solid currentColor}
.theme-card__ft{padding:.7rem .85rem;border-top:1px solid var(--line);display:flex;gap:.5rem;align-items:center}
.theme-card__ft p{font-size:var(--fs-xs);color:var(--fg-muted);margin-right:auto;line-height:1.45}

/* ---- platform console identity ---------------------------------------- */
.plat__mark{
  /* 30px, not the 26 a letterform needed: below about 32 the piston's crown
     grooves start to close, so the mark buys back what it can. */
  width:30px;height:30px;border-radius:7px;flex:none;display:grid;place-items:center;
  background:var(--fg);color:var(--panel);font-weight:800;font-size:.8125rem;
}
/* the mark is now artwork on its own ground, not a letter on a filled tile */
.plat__mark{
  background:url("../img/mark-light.png") center/contain no-repeat;
  border-radius:0; color:transparent; font-size:0;
}
[data-theme="dark"] .plat__mark{background-image:url("../img/mark-dark.png")}
.rail__brand--plat b small{color:var(--fg-faint)}
/* TORQUEOS in caps reads as one unbroken run. The OS is set in the brand red,
   the same separation the wordmark makes, so the name stays a single word while
   the two halves still read apart. Not italic — <i> is only the hook. */
.rail__brand b i,.auth__brand h1 i{font-style:normal;color:var(--brand-text)}
/* Same device on the domain. Lowercase torqueos.app reads as one run; tinting
   the OS separates it without a hyphen, and the URL itself is unchanged. */
i.os{font-style:normal;color:var(--brand-text)}

/* TorqueOS's own red, fixed. Distinct from --brand, which is the *tenant's*
   colour: a blue-branded shop must not render the platform's name with a blue
   OS. Dark uses the lighter red because this is always text, never a fill. */
/* The accent as a *heading on a tinted band*. Not the same job as --brand-text,
   which sits on the plain panel, so it is not the same value. Measured on the
   12% tint: raw brand came to 3.01:1 on dark and 4.31:1 on the console's light
   theme, both under AA. Pushing the accent toward the foreground keeps the hue
   and buys the luminance. Do not swap this back to --brand: it will look right
   and fail. */
:root{--brand-head:color-mix(in srgb, var(--brand) 85%, var(--fg))}
[data-theme="dark"]{--brand-head:color-mix(in srgb, var(--brand-hi) 60%, var(--fg))}

:root{--torqueos-red:#C4001A}
/* Measured, not picked. The dark value has to clear 4.5:1 on the three surfaces
   the red actually lands on — page background 6,20,26; rail 10,29,36; panel
   14,38,45 — and the rail sets it at 9px. #F0374A came to 4.41 there, so it is
   lifted to the nearest tone of the same hue that clears all three (4.72 worst
   case). Lighter than the logo's red on purpose: this is 9px type, not a fill. */
[data-theme="dark"]{--torqueos-red:#F25666}
.rail__brand small i{font-style:normal;color:var(--torqueos-red)}
/* The subdomain belongs to us, not to the shop, so its OS takes the platform
   red rather than the tenant's --brand-text. Fixes the tint and the contrast in
   one move: a blue-branded shop was rendering our domain in its own blue. */
.locked i.os{color:var(--torqueos-red)}

/* "Powered by" mark in the header of every tenant screen, sitting between the
   page title and the actions.

   It used to be absolutely centred on the viewport. That cannot work: absolute
   positioning takes the mark out of flow, so it has no idea how wide the actions
   block is, and the actions block varies by page. Measured at 1360px, the mark
   overlapped the actions on job.html by 74px and on settings.html by 2px — a
   collision, not a tight fit, and raising the breakpoint far enough to clear the
   worst page would have hidden the mark on every laptop.

   So the header becomes three real tracks and the mark occupies the middle one.
   It is centred in the space that is genuinely free rather than on the viewport,
   which on most pages reads as centred and on none of them overlaps. Still
   dropped below 1220px, where there is no free space to centre in. */
.powered{
  display:none;align-items:center;gap:.5rem;pointer-events:none;
  font-family:var(--font-display);font-variation-settings:"wdth" 112,"wght" 700;
  text-transform:uppercase;font-size:1.125rem;letter-spacing:.01em;
  color:var(--fg-muted);white-space:nowrap;
}
.powered i{font-style:normal;color:var(--torqueos-red)}
/* The gap belongs between the mark and the word. Without this wrapper every
   inline child becomes a flex item and the gap opens up inside "TorqueOS" —
   the same trap the sign-in host chip fell into. */
.powered__word{display:inline}
.powered__mark{
  width:42px;height:42px;flex:none;
  background:url("../img/mark-light.png") center/contain no-repeat;
}
[data-theme="dark"] .powered__mark{background-image:url("../img/mark-dark.png")}
@media (min-width:1220px){
  .top:has(.powered){
    display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;
  }
  /* The mark comes first in source so it is announced before the page title is
     read out, but it belongs in the middle track. Rows are stated explicitly:
     without them, auto-placement sees column 2 filled before column 1 and drops
     the title onto a second row. */
  .top:has(.powered) .top__title{grid-column:1;grid-row:1}
  .powered{grid-column:2;grid-row:1;display:flex;justify-self:center;margin-inline:1.25rem}
  .top:has(.powered) .top__actions{grid-column:3;grid-row:1;margin-left:0}
  /* The customer-facing estimate page hides the actions, which would leave the
     mark hard against the right edge. With nothing in the third track, mirroring
     the first puts it back on the true centre. */
  .app--bare .top:has(.powered){grid-template-columns:minmax(0,1fr) auto minmax(0,1fr)}
}
.top{position:sticky}
.rail__exit{
  display:flex;align-items:center;gap:.45rem;
  padding:.5rem .7rem;border-radius:var(--r-sm);
  border:1px dashed var(--line);color:var(--fg-faint);
  font-size:var(--fs-xs);text-decoration:none;
}
.rail__exit:hover{color:var(--fg);border-color:var(--fg-muted);background:rgb(128 128 128 / .06)}
.rail__exit svg{flex:none}

/* ---- sign-in ----------------------------------------------------------- */
.authbody{
  min-height:100dvh;display:grid;place-items:center;
  padding:2rem 1.25rem;background:var(--bg);color:var(--fg);
}
.auth{width:min(100%,410px);display:grid;gap:1.25rem}
.auth__brand{display:grid;justify-items:center;gap:.7rem;text-align:center}
.auth__brand img{height:78px;width:auto}
.auth__brand .tenant__mark,.auth__brand .plat__mark{width:44px;height:44px;border-radius:11px;font-size:1.0625rem}
.auth__brand h1{
  font-family:var(--font-display);font-variation-settings:"wdth" 112,"wght" 800;
  text-transform:uppercase;font-size:1.25rem;letter-spacing:-.01em;line-height:1.1;
}
.auth__brand p{font-size:var(--fs-xs);color:var(--fg-faint)}
.auth__card{
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);
  padding:1.4rem 1.3rem;display:grid;gap:.9rem;
}
[data-theme="light"] .auth__card{box-shadow:0 1px 3px rgb(22 36 42 / .07)}
.auth__card h2{
  font-family:var(--font-text);font-size:var(--fs-label);font-weight:700;
  letter-spacing:var(--track-label);text-transform:uppercase;color:var(--fg-muted);
}
.auth__alt{display:grid;gap:.5rem}
.auth__sep{
  display:flex;align-items:center;gap:.7rem;
  font-family:var(--font-text);font-size:var(--fs-xs);color:var(--fg-faint);
}
.auth__sep::before,.auth__sep::after{content:"";height:1px;flex:1;background:var(--line)}
.auth__meta{text-align:center;font-size:var(--fs-xs);color:var(--fg-faint);line-height:1.6}
.auth__meta a{color:var(--fg-muted);text-decoration:underline;text-underline-offset:2px}
.auth__host{
  display:inline-flex;align-items:center;gap:.4rem;justify-self:center;
  font-family:var(--font-mono);font-size:.6875rem;color:var(--fg-faint);
  border:1px solid var(--line);border-radius:99px;padding:.25rem .65rem;
}
.auth__host b{color:var(--fg-muted);font-weight:600}
/* The chip is inline-flex with a gap, so every inline child became a flex item
   and the gap opened up inside the hostname. Wrapping the host in one span puts
   the gap where it belongs — between the shield and the address. */
.auth__host .host{display:inline}
.auth__note{
  border-left:2px solid var(--amber-400);padding-left:.7rem;
  font-size:var(--fs-xs);color:var(--fg-muted);line-height:1.55;
}
.auth__2fa{display:flex;gap:.4rem}
.auth__2fa input{
  width:100%;text-align:center;font-family:var(--font-mono);font-size:1.05rem;
  letter-spacing:.1em;
}

/* ---- platform reporting ------------------------------------------------ */
/* MRR movement. Up/down genuinely is good/bad here, so the status pair is the
   honest encoding — always with a sign and a direct label, never colour alone. */
.waterfall{display:grid;gap:.5rem}
.wf{display:grid;grid-template-columns:8.5rem 1fr auto;gap:.75rem;align-items:center}
.wf > span{font-size:var(--fs-xs);color:var(--fg-muted);text-align:right}
.wf .track{height:12px;border-radius:3px;background:rgb(128 128 128 / .12);position:relative;overflow:hidden}
.wf .track i{position:absolute;inset-block:0;border-radius:3px}
.wf .track i.pos{left:50%;width:var(--w);background:var(--green-600)}
.wf .track i.neg{right:50%;width:var(--w);background:var(--brick-600)}
.wf .track::after{content:"";position:absolute;left:50%;top:-2px;bottom:-2px;width:1px;background:var(--line)}
.wf em{font-style:normal;font-family:var(--font-mono);font-size:var(--fs-data);
  font-variant-numeric:tabular-nums;min-width:5.5rem;text-align:right}
.wf em.pos{color:var(--green-600)} .wf em.neg{color:var(--brick-600)}
.wf--total{border-top:1px solid var(--line);padding-top:.55rem;margin-top:.2rem}
.wf--total > span,.wf--total em{font-weight:700;color:var(--fg)}

/* cohort grid */
.cohort{overflow-x:auto}
.cohort table{border-collapse:separate;border-spacing:2px;min-width:520px}
.cohort th{font-family:var(--font-text);font-size:var(--fs-label);font-weight:600;
  letter-spacing:var(--track-label);color:var(--fg-faint);text-align:center;padding:.2rem .3rem}
.cohort th:first-child{text-align:left}
.cohort td{font-family:var(--font-mono);font-size:.6875rem;text-align:center;
  padding:.42rem .3rem;border-radius:4px;color:var(--fg);min-width:44px;
  font-variant-numeric:tabular-nums}
.cohort td:first-child{text-align:left;font-family:var(--font-text);font-size:var(--fs-xs);
  background:none;color:var(--fg-muted);white-space:nowrap}
.cohort td[data-v="5"]{background:var(--ord-4);color:#fff}
.cohort td[data-v="4"]{background:var(--ord-3);color:#fff}
.cohort td[data-v="3"]{background:var(--ord-2)}
.cohort td[data-v="2"]{background:var(--ord-1)}
.cohort td[data-v="1"]{background:rgb(128 128 128 / .12);color:var(--fg-faint)}
.cohort td.empty{background:none}

/* health scoring */
.health{display:grid;gap:.55rem}
.hrow{display:grid;grid-template-columns:auto 1fr auto auto;gap:.7rem;align-items:center;
  padding:.6rem .7rem;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel)}
.hrow__mark{width:28px;height:28px;border-radius:7px;display:grid;place-items:center;
  color:#fff;font-weight:800;font-size:.6875rem;flex:none}
.hrow b{display:block;font-size:var(--fs-sm);font-weight:650}
.hrow span{display:block;font-size:var(--fs-xs);color:var(--fg-faint)}
/* .hrow span outranks .hrow__mark and was stealing the monogram's colour, so the
   initials rendered as faint grey on a saturated tile. Restated after it. */
.hrow .hrow__mark{color:#fff;display:grid}
.hscore{font-family:var(--font-mono);font-size:1.05rem;font-weight:700;min-width:2.4rem;text-align:right}
.hscore[data-band="good"]{color:var(--green-600)}
.hscore[data-band="watch"]{color:var(--amber-400)}
.hscore[data-band="risk"]{color:var(--brick-600)}
.hbadge{font-family:var(--font-text);font-size:.6875rem;font-weight:650;letter-spacing:.03em;
  padding:.2rem .5rem;border-radius:99px;border:1px solid currentColor;white-space:nowrap}

/* funnel */
.funnel{display:grid;gap:.4rem}
.fstep{display:grid;grid-template-columns:11rem 1fr auto;gap:.75rem;align-items:center}
.fstep > span{font-size:var(--fs-xs);color:var(--fg-muted);text-align:right}
.fstep .track{height:26px;border-radius:4px;background:rgb(128 128 128 / .1);position:relative}
.fstep .track i{position:absolute;inset-block:0;left:0;width:var(--w);border-radius:4px;background:var(--seq)}
.fstep .track b{position:absolute;left:.5rem;top:50%;transform:translateY(-50%);
  font-family:var(--font-mono);font-size:.6875rem;color:#fff;font-weight:700}
.fstep em{font-style:normal;font-family:var(--font-mono);font-size:var(--fs-xs);
  color:var(--fg-faint);min-width:4.5rem;text-align:right}
.fstep--drop em{color:var(--brick-600)}
/* Counts alongside a rate ramp: plain, so the ramp keeps meaning one thing. */
.cohort td.num{background:none;color:var(--fg-muted)}

/* ---- tenant users, permissions, lifecycle ------------------------------ */
.urow{display:grid;grid-template-columns:34px minmax(0,1fr) auto auto;gap:.75rem;
  align-items:center;padding:.7rem .9rem;border-bottom:1px solid var(--line-soft)}
.urow:last-child{border-bottom:0}
.urow b{display:block;font-size:var(--fs-sm);font-weight:650}
.urow > div span{display:block;font-size:var(--fs-xs);color:var(--fg-faint)}
.urow .avatar{width:34px;height:34px}
.urow .avatar{color:var(--cream-200)}
.urow select{font-size:var(--fs-xs);padding:.3rem .5rem}
.urow--invited{background:rgb(128 128 128 / .05)}
.urow--invited b{color:var(--fg-muted)}
/* The tint lifts the row's background, which drops --fg-faint under 4.5:1 on
   dark. The expiry line is the one thing you actually need to read on an invite
   row, so it steps up rather than the tint coming off. */
.urow--invited > div span{color:var(--fg-muted)}
.urow--off b,.urow--off > div span{opacity:.55}
.umenu{position:relative}
.umenu > button{appearance:none;background:none;border:1px solid var(--line);
  border-radius:var(--r-sm);color:var(--fg-muted);cursor:pointer;
  width:30px;height:30px;display:grid;place-items:center;font-size:1rem;line-height:1}
.umenu > button:hover{color:var(--fg);border-color:var(--fg-muted)}
.umenu ul{position:absolute;right:0;top:calc(100% + 4px);z-index:20;min-width:190px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);
  box-shadow:0 8px 24px rgb(0 0 0 / .18);padding:.3rem;display:none}
.umenu[data-open="true"] ul{display:grid}
.umenu li button{appearance:none;background:none;border:0;width:100%;text-align:left;
  padding:.45rem .55rem;border-radius:4px;font-size:var(--fs-xs);color:var(--fg);cursor:pointer}
.umenu li button:hover{background:var(--panel-2)}
.umenu li button[data-destructive]{color:var(--brick-600)}
.umenu li[role="separator"]{height:1px;background:var(--line-soft);margin:.25rem .3rem}

/* permission matrix: ticks are never the only channel, every cell has a label
   in its title and the header repeats on scroll */
.perms{overflow-x:auto}
.perms table{width:100%;border-collapse:collapse;min-width:560px}
.perms th{font-family:var(--font-text);font-size:var(--fs-label);font-weight:600;
  letter-spacing:var(--track-label);color:var(--fg-faint);text-align:center;
  padding:.5rem .4rem;border-bottom:1px solid var(--line);white-space:nowrap}
.perms th:first-child{text-align:left;width:40%}
.perms td{padding:.45rem .4rem;border-bottom:1px solid var(--line-soft);
  font-size:var(--fs-xs);text-align:center}
.perms td:first-child{text-align:left;color:var(--fg)}
.perms tr:last-child td{border-bottom:0}
.perms i{font-style:normal;font-weight:800}
.perms i[data-p="yes"]{color:var(--green-600)}
/* green-600 is tuned to be a fill on dark, not text: 3.16:1 on the panel. The
   permission grid is nothing but text, so it takes the lighter green. */
[data-theme="dark"] .perms i[data-p="yes"]{color:var(--green-400)}
.perms i[data-p="own"]{color:var(--amber-400);font-size:.6875rem;font-weight:650}
.perms i[data-p="no"]{color:var(--fg-faint);opacity:.6}

/* lifecycle. Reversible things look ordinary; the irreversible one is caged. */
.zone{display:grid;gap:.7rem}
.zone__act{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.9rem;align-items:center;
  padding:.75rem .9rem;border:1px solid var(--line);border-radius:var(--r-sm)}
/* The heading only. A bare `.zone__act b` also caught <b> used for emphasis
   inside the paragraph and broke it onto three block-level lines — the same
   descendant-vs-child trap as .hrow span and .rail__user span. */
.zone__act > div > b{display:block;font-size:var(--fs-sm);font-weight:650}
.zone__act p{margin:.2rem 0 0;font-size:var(--fs-xs);color:var(--fg-muted);max-width:52ch}
.zone__act--danger{border-color:var(--brick-600)}
.zone__act--danger > div > b{color:var(--brick-600)}
.btn--danger{background:var(--brick-600);color:var(--cream-050);border-color:transparent}
.btn--danger[disabled]{opacity:.45;cursor:not-allowed}

/* provisioning */
.prov{display:grid;gap:1rem;align-content:start}
.prov__grid{display:grid;gap:.9rem}
@media (min-width:640px){ .prov__grid--2{grid-template-columns:1fr 1fr} }
.prov__sub{display:flex;align-items:center;gap:.35rem}
.prov__sub input{flex:1 1 auto;min-width:0}
.prov__sub span{font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--fg-faint);white-space:nowrap}
.tpl{display:grid;gap:.5rem;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.tpl button{appearance:none;cursor:pointer;text-align:left;padding:.6rem .7rem;
  background:none;border:1px solid var(--line);border-radius:var(--r-sm);color:var(--fg)}
.tpl button b{display:block;font-size:var(--fs-sm);font-weight:650}
.tpl button span{display:block;font-size:var(--fs-xs);color:var(--fg-faint);margin-top:.15rem}
.tpl button[aria-pressed="true"]{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
.quote{position:sticky;top:1rem}
.quote dl{display:grid;grid-template-columns:1fr auto;gap:.45rem .8rem;margin:0}
.quote dt{font-size:var(--fs-xs);color:var(--fg-muted)}
.quote dd{margin:0;font-family:var(--font-mono);font-size:var(--fs-xs);
  text-align:right;font-variant-numeric:tabular-nums}
.quote .tot{border-top:1px solid var(--line);padding-top:.5rem;margin-top:.2rem;font-weight:700}
.quote .tot dd{font-size:1rem}
.muted.warn{color:var(--amber-400)}

/* ---- tenant settings --------------------------------------------------- */
/* A sub-navigation, not a second rail: the shop is still inside one section,
   so the four panes read as tabs rather than as separate destinations. */
.subnav{
  display:flex;gap:.15rem;flex-wrap:wrap;margin:0 0 1.15rem;
  border-bottom:1px solid var(--line);
}
.subnav a{
  padding:.55rem .85rem;font-size:var(--fs-sm);color:var(--fg-muted);
  border-bottom:2px solid transparent;margin-bottom:-1px;white-space:nowrap;
}
.subnav a:hover{color:var(--fg)}
.subnav a[aria-current="page"]{color:var(--fg);border-bottom-color:var(--brand);font-weight:650}
.subnav a:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}

/* a setting is a label, a control and the sentence explaining the consequence */
.setrow{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,320px);gap:.9rem 1.4rem;
  align-items:start;padding:1rem 0;border-bottom:1px solid var(--line-soft);
}
.setrow:last-child{border-bottom:0}
.setrow__k b{display:block;font-size:var(--fs-sm);font-weight:650}
.setrow__k p{margin:.25rem 0 0;font-size:var(--fs-xs);color:var(--fg-muted);max-width:56ch}
.setrow__v{display:grid;gap:.45rem}
.setrow__v input,.setrow__v select,.setrow__v textarea{width:100%}
@media (max-width:720px){ .setrow{grid-template-columns:1fr} }

/* operator-owned settings the shop can see but not change */
.locked{
  display:flex;align-items:center;gap:.5rem;font-size:var(--fs-xs);
  color:var(--fg-faint);padding:.45rem .6rem;border:1px dashed var(--line);
  border-radius:var(--r-sm);
}
.locked svg{flex:none;opacity:.7}

/* billing summary */
.plan{
  display:grid;gap:.9rem;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
}
.plan__box{border:1px solid var(--line);border-radius:var(--r-sm);padding:.9rem 1rem}
.plan__box dt{font-size:var(--fs-label);letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint);font-weight:600}
.plan__box dd{margin:.3rem 0 0;font-family:var(--font-mono);font-size:1.25rem;
  font-variant-numeric:tabular-nums}
.plan__box span{display:block;margin-top:.2rem;font-size:var(--fs-xs);color:var(--fg-muted)}
.plan__box--live{border-color:var(--brand)}

.calc{
  display:grid;grid-template-columns:1fr auto;gap:.5rem 1rem;
  font-family:var(--font-mono);font-size:var(--fs-xs);font-variant-numeric:tabular-nums;
}
.formcol{display:grid;gap:.9rem;align-content:start}
/* Column heading inside a panel body. Same voice as a .field label, but it
   heads a group rather than labelling one control, so it is not a <label>. */
.colhd{
  font-family:var(--font-mono);font-size:var(--fs-label);letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-muted);font-weight:600;margin:0 0 .6rem;
}
.calc dt{color:var(--fg-muted)} .calc dd{margin:0;text-align:right}
.calc .tot{border-top:1px solid var(--line);padding-top:.55rem;margin-top:.2rem;font-weight:700}
.calc .tot dd{font-size:1rem}

/* =========================================================================
   ASSIGNMENT
   Who is on a job. The board, the schedule and the job page all use the same
   control, because a shop that can assign in one place and not another will
   stop trusting any of them.
   ========================================================================= */

/* A card is no longer a wrapping <a>. It cannot be: the assign control is a
   button, and interactive content inside an anchor is invalid and unusable by
   keyboard. The title carries the link and stretches over the card, and the
   assign row sits above it on the z axis. */
.card{position:relative}
.card__link{color:inherit;text-decoration:none}
.card__link::after{content:"";position:absolute;inset:0;border-radius:inherit}
.card__link:focus-visible{outline:none}
.card__link:focus-visible::after{outline:2px solid var(--amber-400);outline-offset:2px}
.card__who{position:relative;z-index:1;margin-top:.55rem;padding-top:.5rem;
  border-top:1px solid var(--line-soft)}

.avatar--xs{width:20px;height:20px;font-size:.5rem}

.who{
  appearance:none;background:none;border:1px solid transparent;border-radius:99px;
  display:flex;align-items:center;gap:.4rem;padding:.2rem .5rem .2rem .2rem;
  font-family:var(--font-text);font-size:var(--fs-xs);color:var(--fg-muted);
  cursor:pointer;max-width:100%;
  /* A 20px avatar with tight padding measured 29px tall. This is the control a
     technician taps with a glove on, so it gets a real target. */
  min-height:32px;
}
.who:hover:not(:disabled){border-color:var(--line);color:var(--fg)}
.who:disabled{cursor:default}
.who b{font-weight:600;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.who .chev{opacity:.5;margin-left:auto;flex:none}
/* Unassigned is a state to fix, not a neutral one. It reads as a gap. */
.who--none{color:var(--amber-400)}
.who--none .avatar{background:transparent;border:1px dashed var(--amber-400);color:var(--amber-400)}
.who--none b{color:var(--amber-400);font-weight:650}

.card__who--lg .who{padding:.35rem .6rem .35rem .35rem;font-size:var(--fs-sm)}
.card__who--lg .avatar{width:30px;height:30px;font-size:.625rem}

/* An open menu has to out-rank its neighbours. .card__who carries z-index so it
   clears the stretched link, which makes it a stacking context, which traps the
   dropdown inside the card. Later cards are positioned siblings and paint on
   top of it: the menu was visible but the card below was taking the click.
   Lifting the whole card while its menu is open is the fix. */
.card:has(.pop[data-open="true"]),
.row:has(.pop[data-open="true"]){position:relative;z-index:40}

.pop{position:relative}
.pop ul{
  position:absolute;left:0;top:calc(100% + 4px);z-index:30;min-width:210px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);
  box-shadow:0 8px 24px rgb(0 0 0 / .22);padding:.3rem;display:none;
}
.pop[data-open="true"] ul{display:grid}
/* The board is a horizontal scroller, so it clips. A menu near the bottom of a
   column opens upward rather than into the clip. */
.pop--up ul{top:auto;bottom:calc(100% + 4px)}
.pop li button{
  appearance:none;background:none;border:0;width:100%;cursor:pointer;
  display:flex;align-items:center;gap:.5rem;text-align:left;
  padding:.4rem .5rem;border-radius:4px;font-size:var(--fs-xs);color:var(--fg);
}
.pop li button:hover{background:var(--panel-2)}
.pop li button small{margin-left:auto;color:var(--fg-faint);font-size:.625rem}
.pop li button[aria-checked="true"]{background:var(--panel-2);font-weight:650}
.pop li[role="separator"]{height:1px;background:var(--line-soft);margin:.25rem .3rem}
.pop__cap{font-family:var(--font-mono);font-size:.5625rem;letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint);padding:.35rem .5rem .25rem}

/* the rail switcher opens upward, because it sits at the bottom of the rail */
.umenu--up ul{top:auto;bottom:calc(100% + 4px)}
.umenu__cap{font-family:var(--font-mono);font-size:.5625rem;letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint);padding:.35rem .5rem .25rem}
.umenu li button small{display:block;color:var(--fg-faint);font-size:.5625rem;margin-top:1px}
.rail__user{position:relative}
.rail__user .umenu{margin-left:auto}

/* the banner shown when the board is scoped to one person */
.scoped{
  display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin:0;
  background:rgb(232 163 61 / .1);border:1px solid rgb(232 163 61 / .3);
  border-radius:var(--r-sm);padding:.55rem .75rem;font-size:var(--fs-xs);
}
.scoped .btn{margin-left:auto}
.scoped[hidden]{display:none}

.stats--5{grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:760px){ .stats--5{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (min-width:1180px){ .stats--5{grid-template-columns:repeat(5,minmax(0,1fr))} }
.stat dd small{font-size:.6875rem;font-weight:500;color:var(--fg-faint);margin-left:2px}

/* =========================================================================
   TECHNICIAN TIMELINE
   One lane per person across the working day. Bay utilisation is a number you
   cannot act on; a lane with a hole in it tells you who to give the next job
   to. Half-hour columns, so --s and --e are in half hours from 8:00.
   ========================================================================= */
.tl{display:grid;font-size:var(--fs-xs);overflow-x:auto}
.tl__head,.tl__lane{display:grid;grid-template-columns:200px minmax(560px,1fr);
  border-bottom:1px solid var(--line-soft)}
.tl__lane:last-child{border-bottom:0}
.tl__who{display:flex;align-items:center;gap:.55rem;flex-wrap:wrap;
  padding:.7rem .8rem;border-right:1px solid var(--line-soft)}
.tl__who b{display:block;font-size:var(--fs-xs);font-weight:650}
.tl__who > div span{display:block;font-size:.625rem;color:var(--fg-faint);margin-top:1px}
.tl__who > div span b.load{color:var(--fg-muted);font-weight:700}
.tl__col{font-family:var(--font-mono);font-size:.5625rem;letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint)}
.tl__who--head{padding-block:.45rem}

/* On a phone a 200px name column left about three hours of the day visible in
   the scroller. The lane still scrolls, but it should start with something
   worth reading. */
@media (max-width:640px){
  .tl__head,.tl__lane{grid-template-columns:124px minmax(520px,1fr)}
  .tl__who{padding:.6rem .55rem;gap:.4rem}
  .tl__who .avatar{width:24px;height:24px;font-size:.5rem}
  .tl__who > div span{font-size:.5625rem}
}

.load-bar{flex:1 1 100%;height:3px;border-radius:2px;background:var(--line-soft);overflow:hidden}
.load-bar i{display:block;height:100%;width:var(--pct);background:var(--green-600)}
.load-bar--low i{background:var(--amber-400)}

.tl__track{position:relative;display:grid;grid-template-columns:repeat(20,minmax(28px,1fr));
  grid-auto-rows:minmax(34px,auto);gap:3px;padding:.5rem .6rem;align-content:center}
.tl__track--head{padding-block:.35rem;grid-auto-rows:auto}
.tl__track--head i{grid-column:calc(var(--s) + 1) / span 2;font-style:normal;
  font-family:var(--font-mono);font-size:.5625rem;color:var(--fg-faint)}

.blk{
  grid-column:calc(var(--s) + 1) / calc(var(--e) + 1);grid-row:1;
  display:grid;align-content:center;gap:1px;min-width:0;
  padding:.3rem .5rem;border-radius:5px;text-decoration:none;
  border:1px solid var(--line);border-left:2px solid var(--fg-faint);
  background:var(--panel-2);color:var(--fg);
}
.blk:hover{border-color:var(--fg-muted)}
.blk b{font-size:.625rem;font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.blk span{font-size:.5625rem;color:var(--fg-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.blk--go{border-left-color:var(--green-600)}
.blk--est{border-left-color:var(--amber-400)}
/* Held work is booked but cannot start. It is drawn as an outline so a lane
   that looks busy is not mistaken for a lane that is earning. */
.blk--held{border-left-color:var(--brick-500);background:transparent;border-style:dashed}

.tl__lunch{grid-column:calc(var(--s) + 1) / calc(var(--e) + 1);grid-row:1;
  border-radius:5px;background:rgb(128 128 128 / .07)}
.tl__gap{
  grid-column:calc(var(--s) + 1) / calc(var(--e) + 1);grid-row:1;
  display:grid;place-items:center;border:1px dashed var(--line);border-radius:5px;
  font-size:.5625rem;color:var(--fg-faint);text-align:center;padding-inline:.4rem;
}

.panel--flag{border-color:rgb(232 163 61 / .45)}
.row--assign{grid-template-columns:minmax(0,1fr) auto}

/* =========================================================================
   NOTIFICATIONS
   Four things get pulled out of the noise, because they are the four that cost
   money if they sit: a customer replied, an estimate came back approved, work
   was added to the queue, a booked car never arrived. The count is unread, not
   total, so an untouched bell is not a permanent red dot.
   ========================================================================= */
.bell{position:relative;flex:none}
.bell__btn{
  appearance:none;position:relative;background:none;border:1px solid var(--line);
  border-radius:var(--r-sm);color:var(--fg-muted);cursor:pointer;
  width:34px;height:34px;display:grid;place-items:center;
}
.bell__btn:hover{color:var(--fg);border-color:var(--fg-muted)}
.bell__dot{
  position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;padding:0 4px;
  border-radius:99px;background:var(--brick-500);color:#fff;
  font-family:var(--font-mono);font-size:.5625rem;font-weight:700;
  display:grid;place-items:center;border:2px solid var(--bg);
}
.bell__dot[hidden]{display:none}

.notif{
  position:absolute;right:0;top:calc(100% + 6px);z-index:60;width:340px;max-width:88vw;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:0 12px 32px rgb(0 0 0 / .28);display:none;overflow:hidden;
}
.bell[data-open="true"] .notif{display:block}
.notif__hd{display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;
  border-bottom:1px solid var(--line-soft)}
.notif__hd b{font-family:var(--font-mono);font-size:.5625rem;letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint)}
.notif__all{appearance:none;background:none;border:0;margin-left:auto;cursor:pointer;
  font-family:var(--font-text);font-size:var(--fs-xs);color:var(--fg-muted);padding:0}
.notif__all:hover{color:var(--fg)}
.notif ul{max-height:min(58vh,420px);overflow-y:auto}
.notif__i{border-bottom:1px solid var(--line-soft)}
.notif__i:last-child{border-bottom:0}
.notif__i a{display:grid;gap:2px;padding:.6rem .75rem;text-decoration:none;color:inherit}
.notif__i a:hover{background:var(--panel-2)}
.notif__i b{font-size:var(--fs-xs);font-weight:650;color:var(--fg-muted)}
.notif__t{font-size:.625rem;color:var(--fg-faint)}
.notif__k{font-family:var(--font-mono);font-size:.5rem;letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--fg-faint)}
.notif__k--go{color:var(--green-400)}
.notif__k--bad{color:var(--brick-500)}
/* Unread is carried by a bar and by weight, never by colour alone. */
.notif__i--new{box-shadow:inset 2px 0 0 var(--brand-text)}
.notif__i--new b{color:var(--fg);font-weight:700}
.notif__ft{display:block;padding:.55rem .75rem;text-align:center;
  border-top:1px solid var(--line-soft);font-size:var(--fs-xs);color:var(--fg-muted);
  text-decoration:none}
.notif__ft:hover{background:var(--panel-2);color:var(--fg)}
/* On a phone the bell sits at the left of the actions row, so anchoring a
   340px panel to it ran the panel off the left edge. Centring on the bell was
   the same bug with a different sign. Below 640px it stops being anchored to
   the bell at all and spans the viewport with a margin; JS supplies the top,
   because the bell's y position moves with the header. */
@media (max-width:639px){
  .notif{
    position:fixed;top:var(--notif-top,64px);left:8px;right:8px;
    width:auto;max-width:none;
  }
  .notif ul{max-height:min(60vh,440px)}
}

/* =========================================================================
   MOBILE OPERATOR STRIP
   The rail disappears below 900px and the bottom tab bar has room for
   navigation only. Everything else the rail carried, the tenant switcher and
   the link between the tenant app and the console, had nowhere to go, so on a
   phone an operator could not change shop or reach the console at all. This
   strip exists only where the rail does not.
   ========================================================================= */
.mbar{display:none}
@media (max-width:899px){
  .mbar{
    display:flex;align-items:center;gap:.6rem;
    padding:.45rem .85rem;background:var(--panel-2);
    border-bottom:1px solid var(--line);
  }
  /* Not sticky. The header is, and two stacked sticky bars on a phone costs
     more screen than a tenant switcher is worth. It scrolls away; the switcher
     is a once-a-session control, not a constant one. */
  .app--bare .mbar{display:none}
  .mbar__sel{flex:1 1 auto;min-width:0}
  .mbar__sel select{width:100%;font-size:var(--fs-xs);padding:.35rem .5rem}
  .mbar__k{flex:1 1 auto;font-family:var(--font-mono);font-size:.5625rem;
    letter-spacing:var(--track-label);text-transform:uppercase;color:var(--fg-faint)}
  .mbar__go{
    flex:none;font-size:var(--fs-xs);color:var(--brand-text);text-decoration:none;
    border:1px dashed var(--line);border-radius:var(--r-sm);padding:.35rem .55rem;
    white-space:nowrap;
  }
  .mbar__go:hover{border-color:var(--brand)}
}

/* The back link is a header item, not a second row. Once .top became a grid it
   was auto-placing itself under the title and stretching the full column. */
@media (min-width:1220px){
  .top:has(.powered) .top__back{grid-column:1;grid-row:1;justify-self:start;margin-right:.7rem}
  .top:has(.powered) .top__title{grid-column:1;grid-row:1;margin-left:2.6rem}
}
