/* TradeBuilders NewSystem — the look.

   Navy holds it up, orange makes it go.

   The chrome is the management area's -- navy on a soft grey ground,
   white section cards with a hairline shadow -- so a rep switching
   between the staff screens and a member's view is not switching worlds.
   What is new is one rule for the logo orange (#F87800): it is the
   colour of ACTION and of THE MEMBER'S OWN POSITION -- primary buttons,
   the active tab, the tier marker, the brand mark -- and it is never a
   status colour. Red, amber and green are status and never actions, so a
   red button and an orange warning cannot happen.

   White text on the logo orange fails contrast (2.9:1). It is always a
   fill under dark ink; --tb-orange-ink is orange as text (4.6:1).

   One layout, 1K to 4K: the root font size follows the viewport with a
   clamp, everything is in rem, and wide screens get larger type rather
   than wider tables. Phones get a bottom tab bar instead of a menu. */

:root {
  --tb-orange:      #f87800;
  --tb-orange-ink:  #c25a00;
  --tb-orange-soft: #fff1e4;
  --tb-navy:        #0d47a1;
  --tb-navy-2:      #0a3a85;
  --tb-ground:      #f5f6fa;
  --tb-surface:     #ffffff;
  --tb-sunk:        #eef1f6;
  --tb-ink:         #171b22;
  --tb-muted:       #5f6873;
  --tb-faint:       #8b94a1;
  --tb-line:        #e3e7ee;
  --tb-line-2:      #cfd6e1;
  --tb-hover:       #eef3fb;
  --tb-crit:        #b3261e;
  --tb-warn:        #9a6700;
  --tb-good:        #1b6e46;
  --tb-shadow:      0 1px 4px rgba(16, 24, 40, .08);
  --tb-radius:      .5rem;

  /* Bootstrap hooks, so its own components follow the tokens. */
  --bs-body-bg: var(--tb-ground);
  --bs-body-color: var(--tb-ink);
  --bs-body-font-family: Archivo, "Helvetica Neue", Arial, system-ui, sans-serif;
  --bs-link-color: var(--tb-navy);
  --bs-link-hover-color: var(--tb-navy-2);
  --bs-border-color: var(--tb-line);
  --bs-primary: var(--tb-orange);
  --bs-primary-rgb: 248, 120, 0;
  --bs-danger: var(--tb-crit);
  --bs-warning: var(--tb-warn);
  --bs-success: var(--tb-good);
}

/* 15px at 1024, ~16 at 1366, ~17.7 at 1920, 20 from 2560 up. */
html { font-size: clamp(15px, 0.35vw + 11px, 20px); }
body { background: var(--tb-ground); color: var(--tb-ink); -webkit-font-smoothing: antialiased; }
h1, h2, h3, h4, h5 { letter-spacing: -.01em; }
.num, .tabular { font-variant-numeric: tabular-nums; }

/* --- actions --------------------------------------------------------- */
.btn-primary,
.btn-primary:focus {
  --bs-btn-bg: var(--tb-orange); --bs-btn-border-color: var(--tb-orange); --bs-btn-color: #1b1b1b;
  --bs-btn-hover-bg: #e56f00; --bs-btn-hover-border-color: #e56f00; --bs-btn-hover-color: #1b1b1b;
  --bs-btn-active-bg: #d36600; --bs-btn-active-border-color: #d36600; --bs-btn-active-color: #1b1b1b;
  --bs-btn-disabled-bg: #f9b77a; --bs-btn-disabled-border-color: #f9b77a; --bs-btn-disabled-color: #5a3a1a;
  font-weight: 600;
}
.btn-outline-primary {
  --bs-btn-color: var(--tb-orange-ink); --bs-btn-border-color: var(--tb-line-2);
  --bs-btn-hover-bg: var(--tb-orange-soft); --bs-btn-hover-border-color: var(--tb-orange);
  --bs-btn-hover-color: var(--tb-orange-ink); --bs-btn-active-bg: var(--tb-orange-soft);
  --bs-btn-active-border-color: var(--tb-orange); --bs-btn-active-color: var(--tb-orange-ink);
  font-weight: 600;
}
.btn-outline-secondary { --bs-btn-color: var(--tb-muted); --bs-btn-border-color: var(--tb-line-2); --bs-btn-hover-bg: var(--tb-hover); --bs-btn-hover-color: var(--tb-ink); --bs-btn-hover-border-color: var(--tb-line-2); }
.btn:focus-visible, .form-control:focus, .form-check-input:focus, a:focus-visible, button:focus-visible {
  outline: 2px solid var(--tb-orange); outline-offset: 2px; box-shadow: none;
}
.form-control:focus { border-color: var(--tb-orange); }
.form-check-input:checked { background-color: var(--tb-orange); border-color: var(--tb-orange); }
.form-control { border-color: var(--tb-line-2); }
.form-label { font-size: .8rem; font-weight: 600; margin-bottom: .2rem; }
.form-text  { font-size: .75rem; }

/* --- app chrome ------------------------------------------------------ */
.app-navbar { background: var(--tb-navy); padding-top: .5rem; padding-bottom: .5rem; }
.app-navbar .navbar-brand { color: #fff; font-weight: 600; font-size: 1.02rem; letter-spacing: -.01em; }
.app-navbar .nav-link {
  color: rgba(255,255,255,.82); font-size: .9rem; border-radius: .3rem; padding: .3rem .7rem;
  position: relative;
}
.app-navbar .nav-link:hover { color: #fff; background: rgba(255,255,255,.1); }
.app-navbar .nav-link.is-active { color: #fff; font-weight: 600; background: rgba(255,255,255,.12); }
.app-navbar .nav-link.is-active::after {
  content: ""; position: absolute; left: .7rem; right: .7rem; bottom: .1rem; height: 2px;
  background: var(--tb-orange); border-radius: 1px;
}
.app-navbar .nav-link.is-disabled { color: rgba(255,255,255,.4); pointer-events: none; }
.app-navbar .navbar-toggler { border-color: rgba(255,255,255,.3); }
.app-navbar .btn-outline-light { --bs-btn-border-color: rgba(255,255,255,.35); font-size: .82rem; }

/* The brand mark: an orange square with the logo's own weight, so the
   wordmark can be plain white type. */
.tb-mark {
  width: 1.25rem; height: 1.25rem; border-radius: .3rem; background: var(--tb-orange);
  display: inline-block; flex: 0 0 auto; position: relative;
}
.tb-mark::after {
  content: ""; position: absolute; left: 28%; top: 28%; width: 44%; height: 44%;
  background: var(--tb-navy); border-radius: .1rem;
}
.brand-tag {
  font-size: .62rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  padding: .12rem .4rem; border-radius: .25rem;
  background: rgba(255,255,255,.18); color: #fff; align-self: center;
}

/* Widths: one container that grows with the screen, not three layouts. */
.app-container { max-width: 120rem; margin-inline: auto; padding-inline: 1rem; }
@media (min-width: 1200px) { .app-container { padding-inline: 1.5rem; } }
.app-main { padding-block: 1.25rem 2rem; }

/* --- the breach banner ------------------------------------------------ */
.pwned-banner {
  background: #fff8e6; border: 1px solid #f3e2b8; border-left: 3px solid var(--tb-warn);
  border-radius: .4rem; padding: .6rem .9rem; font-size: .86rem; margin-bottom: 1rem;
  display: flex; gap: .6rem; align-items: flex-start;
}
.pwned-banner i { color: var(--tb-warn); margin-top: .1rem; }

/* --- cards ----------------------------------------------------------- */
.section-card { background: var(--tb-surface); border-radius: var(--tb-radius); box-shadow: var(--tb-shadow); }
.card-heading {
  font-size: .76rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--tb-muted); margin-bottom: .75rem;
}

/* --- entity bar (mirrors the staff screens' record bar) --------------- */
.entity-bar {
  background: var(--tb-navy); color: #fff; padding: .7rem 1rem; border-radius: .375rem;
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
}
.entity-bar a { color: #cfe0ff; }
.entity-name { font-weight: 600; font-size: 1.05rem; line-height: 1.25; }
.entity-sub  { font-size: .82rem; opacity: .85; }
.entity-bar .chip-orange {
  background: var(--tb-orange); color: #1b1b1b; font-weight: 600; font-size: .74rem;
  padding: .15rem .55rem; border-radius: .25rem;
}

/* --- Day Sheet: tiles ------------------------------------------------- */
.tile-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: .75rem; }
.tile {
  background: var(--tb-surface); border-radius: var(--tb-radius); padding: .8rem 1rem;
  box-shadow: var(--tb-shadow); border-left: 3px solid transparent;
  text-decoration: none; color: inherit; display: block;
}
a.tile:hover { background: var(--tb-hover); }
/* Only a tile with something in it earns colour. A row of coloured
   zeroes reads as alarm when nothing is wrong. */
.tile.is-live.tile-danger  { border-left-color: var(--tb-crit); }
.tile.is-live.tile-warning { border-left-color: var(--tb-warn); }
.tile.is-live.tile-info    { border-left-color: var(--tb-navy); }
.tile.is-live.tile-success { border-left-color: var(--tb-good); }
.tile-n {
  display: block; font-size: 1.6rem; font-weight: 700; line-height: 1.05;
  font-variant-numeric: tabular-nums; color: var(--tb-ink);
}
.tile:not(.is-live) .tile-n { color: var(--tb-faint); }
.tile-k {
  display: block; margin-top: .2rem; font-size: .68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--tb-muted);
}

/* --- the tier bar ------------------------------------------------------ */
/* The member's own position: the one place orange is used as a mark
   rather than a button. Thresholds come from ref_list_items. */
.tier-card { padding: .9rem 1rem 1.9rem; }
.tier-head { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; }
.tier-head .card-heading { margin-bottom: 0; }
.tier-now { font-weight: 700; color: var(--tb-orange-ink); font-size: .9rem; }
.tier-hint { margin-left: auto; font-size: .78rem; color: var(--tb-muted); }
.tierbar { position: relative; height: .6rem; border-radius: .3rem; background: var(--tb-sunk); margin: 1.5rem .4rem 0; }
.tierbar .fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: .3rem; background: var(--tb-orange); }
.tierbar .mark { position: absolute; top: -.35rem; width: 2px; height: 1.3rem; background: var(--tb-line-2); }
.tierbar .mark.hit { background: var(--tb-orange-ink); }
.tierbar .mark em {
  position: absolute; top: 1.45rem; left: 50%; transform: translateX(-50%);
  font-style: normal; font-size: .68rem; color: var(--tb-muted); white-space: nowrap; text-align: center; line-height: 1.25;
}
.tierbar .mark em b { display: block; color: var(--tb-ink); font-size: .74rem; }
.tierbar .mark:first-of-type em { transform: none; left: 0; text-align: left; }
.tierbar .mark:last-of-type  em { transform: none; left: auto; right: 0; text-align: right; }
.tierbar .you { position: absolute; top: -.15rem; width: 0; }
.tierbar .you i {
  display: block; width: .9rem; height: .9rem; border-radius: 50%;
  background: #fff; border: 3px solid var(--tb-orange); margin-left: -.45rem;
}
.tierbar .you em {
  position: absolute; bottom: 1.15rem; left: 50%; transform: translateX(-50%);
  font-style: normal; font-variant-numeric: tabular-nums; font-size: .72rem; font-weight: 700;
  color: var(--tb-orange-ink); white-space: nowrap; background: var(--tb-surface);
  padding: .05rem .4rem; border-radius: .2rem; border: 1px solid var(--tb-line-2);
}

/* --- Day Sheet: the queue ----------------------------------------------- */
.queue { display: flex; flex-direction: column; }
.queue-row {
  display: flex; align-items: center; gap: .75rem;
  padding: .7rem 1rem .7rem 0; border-top: 1px solid var(--tb-line);
  text-decoration: none; color: inherit; position: relative;
}
.queue-row:first-child { border-top: none; }
.queue-row:hover { background: var(--tb-hover); }
.queue-stripe { width: 3px; align-self: stretch; border-radius: 0 2px 2px 0; flex: 0 0 3px; }
.queue-main { flex: 1; min-width: 0; }
.queue-title { display: block; font-weight: 600; font-size: .92rem; }
.queue-sub {
  display: block; font-size: .8rem; color: var(--tb-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.queue-meta { display: flex; flex-direction: column; align-items: flex-end; gap: .2rem; flex: 0 0 auto; }
.queue-when { font-size: .72rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; }
.badge.text-bg-light { background: var(--tb-sunk) !important; color: var(--tb-muted) !important; font-weight: 600; }

/* --- Job Desk shell --------------------------------------------------- */
.desk { display: grid; grid-template-columns: 17rem minmax(0, 1fr); gap: 1rem; align-items: start; }
.desk-main { min-width: 0; }
@media (min-width: 1600px) { .desk { grid-template-columns: 18rem minmax(0, 1fr); } }

.rail { background: var(--tb-surface); border-radius: var(--tb-radius); box-shadow: var(--tb-shadow); overflow: hidden; }
.rail-search { padding: .6rem; border-bottom: 1px solid var(--tb-line); }
.rail-list { max-height: 62vh; overflow-y: auto; }
.rail-item {
  display: flex; align-items: center; gap: .55rem; padding: .55rem .75rem;
  border-bottom: 1px solid var(--tb-line); text-decoration: none; color: inherit;
}
.rail-item:last-child { border-bottom: none; }
.rail-item:hover { background: var(--tb-hover); }
.rail-item.is-active { background: var(--tb-hover); box-shadow: inset 3px 0 0 var(--tb-orange); }
.rail-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 7px; background: var(--tb-line-2); }
.rail-dot.is-crit { background: var(--tb-crit); }
.rail-dot.is-live { background: var(--tb-navy); }
.rail-body { flex: 1; min-width: 0; }
.rail-name { display: block; font-size: .88rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-sub  { display: block; font-size: .75rem; color: var(--tb-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-badge { flex: 0 0 auto; font-size: .68rem; }
.rail-new {
  display: block; padding: .6rem .75rem; border-top: 1px solid var(--tb-line);
  font-size: .82rem; font-weight: 600; text-decoration: none; color: var(--tb-orange-ink);
}
.rail-new:hover { background: var(--tb-orange-soft); }

.desk-tabs { border-bottom: 1px solid var(--tb-line-2); }
.desk-tabs .nav-link { font-size: .88rem; font-weight: 600; color: var(--tb-muted); border: none; padding: .5rem .9rem; }
.desk-tabs .nav-link:hover { color: var(--tb-ink); }
.desk-tabs .nav-link.active { color: var(--tb-ink); background: transparent; box-shadow: inset 0 -2px 0 var(--tb-orange); }
.tab-body { margin-top: 1rem; }

/* --- tables ------------------------------------------------------------ */
.tb-table th {
  white-space: nowrap; font-size: .7rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--tb-muted); font-weight: 700; border-bottom-color: var(--tb-line-2);
}
.tb-table td { font-size: .875rem; vertical-align: middle; }
.tb-table tbody tr:hover { background: var(--tb-hover); }
.tb-table a.row-link { text-decoration: none; font-weight: 600; color: var(--tb-ink); }
.tb-table a.row-link:hover { color: var(--tb-navy); }
.tb-table .sub { display: block; font-size: .76rem; color: var(--tb-muted); }

/* --- lead thread -------------------------------------------------------- */
.msg { border-radius: .4rem; padding: .6rem .8rem; margin-bottom: .6rem; border: 1px solid var(--tb-line); }
.msg-head { font-size: .75rem; font-weight: 700; color: var(--tb-muted); display: flex; gap: .5rem; }
.msg-when { margin-left: auto; font-weight: 400; }
.msg-body { font-size: .9rem; margin-top: .25rem; }
.msg-me   { background: var(--tb-hover); border-color: #cfe0ff; }
.msg-them { background: var(--tb-surface); }
.msg-tb   { background: #fff8e6; border-color: #f3e2b8; }
.msg-sys  { background: var(--tb-sunk); }
.msg-note { font-size: .78rem; color: var(--tb-muted); padding: .35rem 0 .35rem .8rem; border-left: 2px solid var(--tb-line); margin-bottom: .6rem; }

/* --- misc --------------------------------------------------------------- */
.kv { display: grid; grid-template-columns: auto 1fr; gap: .3rem .8rem; font-size: .85rem; }
.kv dt { color: var(--tb-muted); font-weight: 600; white-space: nowrap; }
.kv dd { margin: 0; }

.chip {
  display: inline-flex; align-items: center; gap: .4rem; padding: .25rem .6rem;
  background: var(--tb-surface); border: 1px solid var(--tb-line); border-radius: 1rem;
  font-size: .8rem; text-decoration: none; color: inherit;
}
.chip:hover { background: var(--tb-hover); border-color: #cfe0ff; }
.chip-n { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--tb-navy); }
.chip.is-on { background: var(--tb-navy); border-color: var(--tb-navy); color: #fff; font-weight: 600; }
.chip.is-on:hover { background: var(--tb-navy-2); border-color: var(--tb-navy-2); }
.chip.is-on .chip-n { color: #fff; }

.stage-preview { display: flex; flex-wrap: wrap; gap: .4rem; }
.stage-chip { padding: .3rem .7rem; background: var(--tb-ground); border: 1px dashed var(--tb-line-2); border-radius: .3rem; font-size: .8rem; color: var(--tb-muted); }
.back-link { display: inline-block; font-size: .82rem; text-decoration: none; margin-bottom: .6rem; }

/* --- sign in ------------------------------------------------------------- */
/* A split page: navy on the left carrying the mark and three lines of
   what the network is for; the form on the right. Stacks on a phone. */
.plain-body { background: var(--tb-ground); min-height: 100vh; margin: 0; }
.plain-main { min-height: 100vh; display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
.login-side {
  background: var(--tb-navy); color: #fff; padding: clamp(2rem, 6vw, 5rem) clamp(1.25rem, 5vw, 4.5rem);
  display: flex; flex-direction: column; justify-content: space-between; gap: 3rem;
  position: relative; overflow: hidden;
}
.login-side::before {
  /* the orange band from the membership card, at the edge */
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: .45rem; background: var(--tb-orange);
}
.login-logo { display: flex; align-items: center; gap: .7rem; }
.login-logo img { height: 2.2rem; width: auto; display: block; }
/* The new mark sits on the white sign-in card (Carl, 6 Oct): its grey
   hammer is lost on navy, so it is not on the navy panel at all. */
.login-card-logo { margin: 0 0 1.1rem; }
.login-card-logo img { height: 3.6rem; width: auto; display: block; }
@media (max-width: 575.98px) { .login-card-logo img { height: 3rem; } }
.login-logo-badge { display: inline-block; background: #fff; border-radius: .6rem; padding: .55rem .85rem; box-shadow: 0 2px 10px rgba(0,0,0,.18); }
.login-logo-badge img { height: 3.4rem; }
@media (max-width: 575.98px) { .login-logo-badge img { height: 2.6rem; } }
.login-logo .tb-word { font-weight: 700; font-size: 1.25rem; letter-spacing: -.01em; }
.login-side h1 { font-size: clamp(1.7rem, 3.2vw, 2.6rem); font-weight: 700; line-height: 1.12; letter-spacing: -.02em; margin: 0 0 1rem; text-wrap: balance; }
.login-side p.lead-line { font-size: 1.02rem; opacity: .88; max-width: 34ch; margin: 0; }
.login-points { list-style: none; padding: 0; margin: 1.6rem 0 0; display: grid; gap: .8rem; max-width: 36ch; }
.login-points li { display: flex; gap: .7rem; align-items: flex-start; font-size: .92rem; opacity: .92; }
.login-points li::before { content: ""; flex: 0 0 .55rem; height: .55rem; margin-top: .45rem; border-radius: 50%; background: var(--tb-orange); }
.login-foot { font-size: .78rem; opacity: .7; }
.login-form-wrap { display: flex; align-items: center; justify-content: center; padding: clamp(1.5rem, 5vw, 4rem) 1rem; }
.login-card { width: 100%; max-width: 26rem; padding: 1.75rem 1.75rem 1.5rem; }
.login-card h2 { font-size: 1.25rem; font-weight: 700; margin: 0 0 .25rem; }
.login-card .login-sub { color: var(--tb-muted); font-size: .88rem; margin-bottom: 1.25rem; }
.login-card .form-control { padding: .6rem .75rem; }
.login-card .btn-primary { padding: .65rem; font-size: .98rem; }
.login-remember { display: flex; align-items: center; gap: .5rem; font-size: .86rem; margin: .75rem 0 1rem; }
.login-remember .form-check-input { margin: 0; }
.login-remember small { display: block; color: var(--tb-muted); font-size: .76rem; }
.login-other { font-size: .8rem; color: var(--tb-muted); text-align: center; margin: 1.1rem 0 0; }
.login-other a { color: var(--tb-orange-ink); }
.brand-mark {
  width: 3.2rem; height: 3.2rem; margin: 0 auto; border-radius: .7rem;
  background: var(--tb-navy); color: #fff; display: flex; align-items: center;
  justify-content: center; font-size: 1.5rem; position: relative;
}
.brand-mark::after { content: ""; position: absolute; left: -.25rem; top: 20%; bottom: 20%; width: .3rem; border-radius: 2px; background: var(--tb-orange); }

/* --- role chooser ----------------------------------------------------------- */
.role-picker { display: flex; flex-direction: column; gap: .6rem; }
.role-option {
  display: flex; align-items: center; gap: .9rem; width: 100%; text-align: left;
  padding: .85rem 1rem; border: 1px solid var(--tb-line); border-radius: .5rem;
  background: var(--tb-surface); color: inherit; cursor: pointer;
}
.role-option:hover  { background: var(--tb-orange-soft); border-color: var(--tb-orange); }
.role-icon { font-size: 1.4rem; color: var(--tb-navy); flex: 0 0 auto; }
.role-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.role-what { font-weight: 600; font-size: .95rem; }
.role-who  { font-size: .82rem; color: var(--tb-muted); }
.role-no   { font-size: .72rem; color: var(--tb-faint); }
.role-go   { color: var(--tb-faint); flex: 0 0 auto; }

/* --- the supplier side ------------------------------------------------------ */
/* Same product, same chrome; the bar colour is the one visual difference,
   and it earns its place -- a rep with both areas open in two tabs needs
   to know which one they are typing a reply into. Teal, not a second
   navy: two navies are the same navy across a room. */
:root { --tb-supplier: #00695c; --tb-supplier-2: #00564b; }
.app-navbar--supplier { background: var(--tb-supplier); }
body:has(.app-navbar--supplier) .entity-bar { background: var(--tb-supplier); }
body:has(.app-navbar--supplier) .chip-n { color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .chip.is-on { background: var(--tb-supplier); border-color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .chip.is-on:hover { background: var(--tb-supplier-2); border-color: var(--tb-supplier-2); }
body:has(.app-navbar--supplier) .chip.is-on .chip-n { color: #fff; }
body:has(.app-navbar--supplier) .login-side { background: var(--tb-supplier); }

/* --- phone: bottom tab bar ----------------------------------------------------- */
.tabbar {
  display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 1030;
  background: var(--tb-surface); border-top: 1px solid var(--tb-line-2);
  padding: .35rem .25rem calc(.5rem + env(safe-area-inset-bottom, 0px));
  grid-template-columns: repeat(5, 1fr);
}
.tabbar a {
  display: flex; flex-direction: column; align-items: center; gap: .15rem;
  font-size: .66rem; font-weight: 600; color: var(--tb-muted); text-decoration: none; padding: .2rem 0;
}
.tabbar a i { font-size: 1.25rem; line-height: 1; }
.tabbar a.is-active { color: var(--tb-orange-ink); }
.tabbar a.is-disabled { opacity: .4; pointer-events: none; }

@media (max-width: 767.98px) {
  .tabbar { display: grid; }
  body:has(.tabbar) .app-main { padding-bottom: 5rem; }
  .app-navbar .navbar-collapse-links { display: none; }
  .plain-main { grid-template-columns: 1fr; min-height: auto; }
  .login-side { padding: 1.5rem 1.25rem; gap: 1.25rem; }
  .login-side h1 { font-size: 1.5rem; }
  .login-points { display: none; }
  .login-card { box-shadow: none; padding: 1.25rem .25rem; }
}
@media (max-width: 991.98px) {
  .desk { grid-template-columns: 1fr; }
  .rail-list { max-height: 240px; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* --- P9: card ------------------------------------------------------------------
   The digital membership card, and the public page a scan of it lands on.

   Built at 400px and allowed to grow, not shrunk down from a desktop
   layout: this is opened one-handed at a trade counter and turned round
   to be shown to somebody. So the card is a fixed aspect panel that caps
   at 26rem and centres, and the two things read across a counter -- the
   member number and the verdict -- are the biggest type on either page.

   Navy is the card because navy is structure here, and a membership card
   IS the structure. The orange band is the member's own mark, a fill
   under nothing, which is the only way #F87800 is allowed to be used at
   this size. The QR sits on white with its full four-module quiet zone;
   a QR tinted to match the card, or bled into the navy, does not scan. */

.mcard-wrap { max-width: 26rem; margin-inline: auto; }

.mcard {
  background: linear-gradient(160deg, var(--tb-navy) 0%, var(--tb-navy-2) 100%);
  color: #fff;
  border-radius: .9rem;
  overflow: hidden;
  box-shadow: 0 6px 20px rgba(13, 71, 161, .28);
}

.mcard-top {
  display: flex; align-items: center; gap: .5rem;
  padding: .9rem 1.1rem .75rem;
}
.mcard-mark {
  width: 1.1rem; height: 1.1rem; border-radius: .28rem;
  background: var(--tb-orange); flex: none;
}
.mcard-brand { font-weight: 700; letter-spacing: -.01em; }
.mcard-tier {
  margin-left: auto;
  background: var(--tb-orange); color: #1b1b1b;   /* dark ink on orange, never white */
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: .2rem .55rem; border-radius: 999px;
}

/* The band. Four pixels of the member's own colour, edge to edge. */
.mcard-band { height: .3rem; background: var(--tb-orange); }

.mcard-body { padding: 1.1rem 1.1rem 1.25rem; }

.mcard-label {
  font-size: .68rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: rgba(255, 255, 255, .62);
}
.mcard-business {
  font-size: 1.25rem; font-weight: 700; line-height: 1.2;
  margin: .15rem 0 1rem; overflow-wrap: anywhere;
}

.mcard-no-row {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.1rem;
}
.mcard-no {
  font-size: 2.1rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.mcard-valid { text-align: right; }
.mcard-date { font-weight: 600; font-size: .95rem; margin-top: .25rem; }

/* Lapsed: amber, because a lapsed card is a renewal conversation and not
   an error. The card stays navy -- recolouring the whole thing red would
   make a member think their account had been cancelled. */
.mcard.is-lapsed .mcard-date { color: #ffcf6b; }
.mcard.is-lapsed .mcard-band { background: #e9a23b; }

.mcard-qr {
  background: #fff; border-radius: .6rem;
  padding: .55rem; width: 12.5rem; max-width: 100%;
  margin: 0 auto;
}
.mcard-qr-svg { display: block; width: 100%; height: auto; }
.mcard-qr-missing {
  color: var(--tb-ink); font-size: .8rem; text-align: center; padding: 2rem .5rem;
}
.mcard-scan {
  text-align: center; margin-top: .6rem;
  font-size: .78rem; color: rgba(255, 255, 255, .72);
}

.mcard-notes { margin-top: 1.1rem; }
.mcard-notes p { margin-bottom: .6rem; }
.mcard-warn {
  background: #fff8e6; border: 1px solid #f3e2b8; border-left: 3px solid var(--tb-warn);
  border-radius: var(--tb-radius); padding: .6rem .8rem; font-size: .9rem;
}
.mcard-warn i { color: var(--tb-warn); margin-right: .35rem; }

/* --- the public verify page ---------------------------------------------------
   One card on the bare layout. The verdict is a WORD and an icon as well
   as a colour: red/green colour blindness is one man in twelve, and this
   is read by a stranger in a hurry. Green, amber and grey are status
   colours here and that is exactly what they are for. */

.vcheck-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 1.5rem 1rem;
}
.vcheck {
  background: var(--tb-surface); border-radius: var(--tb-radius);
  box-shadow: var(--tb-shadow); border-top: .35rem solid var(--tb-faint);
  width: 100%; max-width: 24rem; padding: 1.5rem 1.35rem 1.1rem; text-align: center;
}
.vcheck-verdict {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  font-size: 2rem; font-weight: 700; line-height: 1.1; letter-spacing: -.02em;
}
.vcheck-verdict i { font-size: 1.7rem; }
.vcheck-line { color: var(--tb-muted); font-size: .92rem; margin: .5rem 0 1.1rem; }

.vcheck--valid   { border-top-color: var(--tb-good); }
.vcheck--valid   .vcheck-verdict { color: var(--tb-good); }
.vcheck--lapsed  { border-top-color: var(--tb-warn); }
.vcheck--lapsed  .vcheck-verdict { color: var(--tb-warn); }
.vcheck--unknown { border-top-color: var(--tb-line-2); }
.vcheck--unknown .vcheck-verdict { color: var(--tb-muted); }

.vcheck-facts {
  margin: 0; padding-top: 1rem; border-top: 1px solid var(--tb-line);
  text-align: left;
}
.vcheck-facts dt {
  font-size: .68rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--tb-faint);
}
.vcheck-facts dd { margin: .1rem 0 .8rem; font-weight: 600; }
.vcheck-business { font-size: 1.15rem; line-height: 1.25; overflow-wrap: anywhere; }
.vcheck-no { font-size: 1.35rem; font-variant-numeric: tabular-nums; }

.vcheck-notice {
  background: var(--tb-sunk); border-radius: var(--tb-radius);
  padding: .55rem .7rem; font-size: .85rem; color: var(--tb-muted);
}
.vcheck-foot {
  margin: .6rem 0 0; font-size: .72rem; color: var(--tb-faint);
}

/* Anything narrower than a 2016 phone: give the number the whole row
   rather than letting it and the expiry squeeze each other. */
@media (max-width: 359.98px) {
  .mcard-no-row { flex-direction: column; align-items: flex-start; gap: .75rem; }
  .mcard-valid { text-align: left; }
  .mcard-no { font-size: 1.9rem; }
}
/* --- P6-P8 --- */
/* The Ledger and the tier benefit columns. Tiles, tables, chips and the
   tier bar itself are the classes that were already here; these are the
   two things that had none -- the three benefit columns under the bar,
   and the money figure in a tile, which is wider than a count. */
.tier-benefits { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem; margin-top: .6rem; }
.tier-col { border: 1px solid var(--tb-line); border-radius: var(--tb-radius); padding: .7rem .8rem; }
.tier-col.is-mine { border-color: var(--tb-navy); box-shadow: inset 0 0 0 1px var(--tb-navy); }
.tier-col-name { font-weight: 700; font-size: .92rem; display: flex; align-items: center; gap: .4rem; }
.tier-you { background: var(--tb-orange); color: #1b1b1b; font-size: .68rem; font-weight: 700; padding: .05rem .35rem; border-radius: .2rem; }
.tier-col-when { font-size: .74rem; color: var(--tb-muted); margin: .1rem 0 .45rem; font-variant-numeric: tabular-nums; }
.tier-col ul { margin: 0; padding-left: .9rem; }
.tier-col li { font-size: .78rem; line-height: 1.35; margin-bottom: .25rem; }
.ledger-head { border-bottom: 1px solid var(--tb-line); }
.tile .tile-n { font-size: clamp(1.1rem, 2.4vw, 1.5rem); }

/* --- P14 --- */
/* The one line the notifications control says back. It lives inside the
   account dropdown, so it must wrap rather than widen the menu, and it is
   muted: nothing here is a status the member has to act on. */
.push-note { padding: .1rem 1rem .5rem; font-size: .76rem; line-height: 1.3;
             color: var(--tb-muted); max-width: 16rem; white-space: normal; }
/* --- P10-P11: bill --------------------------------------------------------------
   The bill of work on the Job Desk: the template picker on an empty job,
   then the lines with their four money columns.

   BUILT AT 400px FIRST. A bill has six columns of which four are money,
   and four money columns will not fit beside a description on a phone.
   So under 576px the table stops being a table: each row becomes a block
   and each cell a labelled line under the description, with the label
   coming out of data-label rather than a second copy of the markup. The
   header row is hidden there, not duplicated.

   Colour: nothing here is orange except the Add line button and the save
   tick on a budget cell, which are the two actions. The money columns
   are ink, never red or green -- a budget that is smaller than the
   actual is a fact for the variance column (P13) to have an opinion
   about, not something a bill of work should shout at somebody while
   they are typing. */

.tpl-list { display: flex; flex-direction: column; gap: .5rem; }
.tpl {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius); padding: .7rem .85rem;
}
.tpl:hover { border-color: var(--tb-line-2); background: var(--tb-hover); }
.tpl-main { flex: 1; min-width: 12rem; }
.tpl-name { font-weight: 600; font-size: .95rem; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.tpl-sub  { font-size: .82rem; color: var(--tb-muted); }
.tpl-meta { font-size: .74rem; color: var(--tb-faint); font-variant-numeric: tabular-nums; margin-top: .15rem; }
.tpl-go   { flex: 0 0 auto; }

/* The toolbar. The three actions, then the two numbers that matter. */
.bill-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.bill-bar-tot { display: flex; align-items: baseline; gap: .4rem; font-variant-numeric: tabular-nums; }
.bill-tot-k { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); }
.bill-tot-v { font-weight: 700; font-size: .95rem; margin-right: .4rem; }

/* <details> used as a button that opens a small form. The marker is
   dropped so the summary can be a .btn without a stray triangle. */
.bill-details > summary { list-style: none; cursor: pointer; }
.bill-details > summary::-webkit-details-marker { display: none; }
.bill-details-inline { position: relative; }
.bill-name-form {
  position: absolute; z-index: 20; top: calc(100% + .35rem); left: 0; width: min(22rem, 90vw);
  background: var(--tb-surface); border: 1px solid var(--tb-line-2);
  border-radius: var(--tb-radius); box-shadow: var(--tb-shadow); padding: .75rem;
}

.bill-stage-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap; padding: .75rem 1rem .25rem;
}
.bill-stage-tot { display: flex; gap: .9rem; font-size: .82rem; font-variant-numeric: tabular-nums; }
.bill-stage-tot .k { color: var(--tb-faint); font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; }

.bill-table th { white-space: nowrap; }
.bill-table td { vertical-align: middle; }
.bill-table tfoot th { background: var(--tb-sunk); font-size: .8rem; }
.bill-desc { font-weight: 600; }
.bill-group .bill-desc { font-weight: 700; }
.bill-group > td { background: var(--tb-sunk); }
.bill-child > td:first-child { padding-left: 1.6rem; }

/* The editable cell. The tick is quiet until the row is being used. */
.bill-budget { display: flex; align-items: center; gap: .25rem; justify-content: flex-end; }
.bill-budget .form-control { width: 6.5rem; }
.bill-save {
  border: none; background: none; padding: .1rem .2rem; line-height: 1;
  color: var(--tb-faint); opacity: .35; cursor: pointer;
}
.bill-table tr:hover .bill-save, .bill-budget:focus-within .bill-save { opacity: 1; color: var(--tb-orange-ink); }

.bill-acts { white-space: nowrap; text-align: right; }
.bill-acts-h { width: 7rem; }
.bill-act {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.6rem; height: 1.6rem; border: none; background: none; padding: 0;
  color: var(--tb-muted); text-decoration: none; border-radius: .25rem;
}
.bill-act:hover { background: var(--tb-hover); color: var(--tb-ink); }
.bill-act-del:hover { color: var(--tb-crit); }

/* The edit row. Rendered only for ?edit={id}, so it is simply there. */
.bill-edit > td { background: var(--tb-hover); }
.bill-act.is-on { background: var(--tb-orange-soft); color: var(--tb-orange-ink); }
.bill-cancel { display: inline-block; font-size: .78rem; margin-top: .4rem; }

.bill-form-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: .5rem .6rem; align-items: end; }
.bill-f { display: block; margin: 0; }
.bill-f-stage  { grid-column: span 3; }
.bill-f-desc   { grid-column: span 9; }
.bill-f-qty    { grid-column: span 2; }
.bill-f-unit   { grid-column: span 2; }
.bill-f-cat    { grid-column: span 4; }
.bill-f-budget { grid-column: span 2; }
.bill-f-go     { grid-column: span 2; }

.bill-job-total {
  display: flex; align-items: baseline; gap: 1.25rem; flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}
.bill-tot-name { font-weight: 700; margin-right: auto; }
.bill-tot-cell { display: flex; flex-direction: column; }
.bill-tot-cell .k { font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; color: var(--tb-faint); }
.bill-tot-cell b { font-size: 1.05rem; }

@media (max-width: 767.98px) {
  .bill-form-grid { grid-template-columns: repeat(6, 1fr); }
  .bill-f-stage  { grid-column: span 6; }
  .bill-f-desc   { grid-column: span 6; }
  .bill-f-qty    { grid-column: span 2; }
  .bill-f-unit   { grid-column: span 2; }
  .bill-f-budget { grid-column: span 2; }
  .bill-f-cat    { grid-column: span 4; }
  .bill-f-go     { grid-column: span 2; }
}

/* 400px: the table becomes a list of rows, each money column a labelled
   line under the description. */
@media (max-width: 575.98px) {
  .bill-table thead { display: none; }
  .bill-table, .bill-table tbody, .bill-table tfoot, .bill-table tr, .bill-table td, .bill-table th { display: block; width: auto; }
  .bill-table tr { border-bottom: 1px solid var(--tb-line); padding: .5rem .25rem; }
  .bill-table td { border: none; padding: .15rem .5rem; text-align: left !important; }
  .bill-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    display: inline-block; min-width: 5.5rem;
    font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .bill-table td[data-label="Line"]::before { display: none; }
  .bill-budget { justify-content: flex-start; }
  .bill-save { opacity: 1; }
  .bill-acts { text-align: left; padding-top: .35rem !important; }
  .bill-child > td:first-child { padding-left: 1.25rem; }
  .bill-table tfoot tr { background: var(--tb-sunk); }
  .bill-table tfoot th { padding: .15rem .5rem; }
  .bill-stage-tot { gap: .75rem; }
}

/* --- P15: contacts and the People tab -------------------------------------
   The address book, and who is on a job.

   ONE NEW THING ONLY: the kind chip. Everything else here is
   .section-card, .tb-table, .chip and .btn-primary as they already are,
   because a contact list that looked different from the Ledger would be
   a second application.

   The kind chip is a LABEL, not a status and not an action, so it is
   never orange and never red/amber/green: it is navy, grey or the
   muted ink already in the tokens. Orange on this page is the
   "Add a contact" button and nothing else.

   BUILT AT 400px. Both tables lose their header under 576px and each
   row becomes a block with its cells labelled from data-label -- the
   same mechanism the bill uses, and for the same reason: five columns
   of contact details do not fit beside a name on a phone, and a phone
   is where somebody looks up the certifier's number. */

.chip-kind {
  display: inline-block; padding: .1rem .45rem; border-radius: .25rem;
  font-size: .68rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  border: 1px solid var(--tb-line-2); color: var(--tb-muted); background: var(--tb-ground);
  white-space: nowrap;
}
.chip-kind.is-client  { border-color: #b9cdf0; color: var(--tb-navy);   background: var(--tb-hover); }
.chip-kind.is-trade   { border-color: var(--tb-line-2); color: var(--tb-ink); background: var(--tb-sunk); }
.chip-kind.is-council { border-color: #d5cdbe; color: #6b5a3a;          background: #faf6ee; }

.contacts-head { border-bottom: 1px solid var(--tb-line); }
.contacts-search { min-width: 14rem; max-width: 22rem; flex: 1 1 14rem; }
.contacts-table td { vertical-align: top; }
.contacts-table .sub { margin-top: .1rem; }

/* The kind picker on the form: four radios that read as buttons. The
   underlying <input> stays in the DOM and keeps the keyboard and the
   screen reader working -- it is moved out of sight, not display:none. */
.kind-picker { display: flex; gap: .4rem; flex-wrap: wrap; }
.kind-opt { margin: 0; }
.kind-opt input {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.kind-opt span {
  display: inline-block; padding: .35rem .8rem; border-radius: .3rem; cursor: pointer;
  border: 1px solid var(--tb-line-2); background: var(--tb-surface);
  font-size: .85rem; font-weight: 600; color: var(--tb-muted);
}
.kind-opt span:hover { background: var(--tb-hover); }
.kind-opt input:checked + span { background: var(--tb-navy); border-color: var(--tb-navy); color: #fff; }
.kind-opt input:focus-visible + span { outline: 2px solid var(--tb-orange); outline-offset: 2px; }

/* The contact's details, as a two-column list that collapses to one. */
.contact-dl { display: grid; grid-template-columns: 7rem 1fr; gap: .25rem .75rem; margin: 0; }
.contact-dl dt { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
                 color: var(--tb-faint); font-weight: 700; align-self: center; }
.contact-dl dd { margin: 0; font-size: .9rem; }

/* --- the People tab --- */
.people-head { border-bottom: 1px solid var(--tb-line); }
.people-table td { vertical-align: top; }
.people-table .sub { margin-top: .15rem; }
.people-acts   { white-space: nowrap; text-align: right; }
.people-acts-h { width: 3rem; }

.people-form-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: .5rem .6rem; align-items: end; }
.people-f { display: block; margin: 0; }
.people-f-who  { grid-column: span 4; }
.people-f-role { grid-column: span 3; }
.people-f-note { grid-column: span 3; }
.people-f-go   { grid-column: span 2; }

/* <details> as a quiet second option, not a second form. */
.people-new > summary { cursor: pointer; font-size: .82rem; color: var(--tb-navy); }
.people-new > summary:hover { color: var(--tb-navy-2); }

@media (max-width: 991.98px) {
  .people-f-who  { grid-column: span 6; }
  .people-f-role { grid-column: span 6; }
  .people-f-note { grid-column: span 8; }
  .people-f-go   { grid-column: span 4; }
}

@media (max-width: 575.98px) {
  .people-form-grid { grid-template-columns: repeat(4, 1fr); }
  .people-f-who, .people-f-role, .people-f-note, .people-f-go { grid-column: span 4; }

  .contacts-search { max-width: none; }
  .contact-dl { grid-template-columns: 5.5rem 1fr; }

  .contacts-table thead, .people-table thead { display: none; }
  .contacts-table, .contacts-table tbody, .contacts-table tr, .contacts-table td,
  .people-table, .people-table tbody, .people-table tr, .people-table td { display: block; width: auto; }
  .contacts-table tr, .people-table tr { border-bottom: 1px solid var(--tb-line); padding: .5rem .25rem; }
  .contacts-table td, .people-table td { border: none; padding: .15rem .5rem; text-align: left !important; }
  .contacts-table td[data-label]:not([data-label=""])::before,
  .people-table   td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    display: inline-block; min-width: 4.5rem;
    font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .contacts-table td[data-label="Name"]::before,
  .contacts-table td[data-label="Job"]::before,
  .people-table   td[data-label="Who"]::before { display: none; }
  .people-acts { text-align: left; padding-top: .35rem !important; }
}

/* --- P18: scorecard ---------------------------------------------------------
   The supplier's own report card. The tiles, the table and the badges are
   the classes that were already here; what had none is the two lines that
   turn a figure into something a rep can act on -- the comparison and the
   sentence under it.

   So .tile grows a body. A scorecard tile is not a Day Sheet tile: the
   Day Sheet's job is to be counted at a glance, this one's is to be
   understood once. The wider minimum column keeps the sentence from
   setting two words to a line, and the tiles stack on a phone rather than
   shrinking.

   Colour: green and red only where a figure is plainly good or plainly
   not, from the same --tb-good / --tb-crit the buckets use. Nothing on
   this page is orange -- orange is the action colour and there is no
   action here. */

.sc-tiles { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); align-items: stretch; }
.sc-tiles .tile { display: flex; flex-direction: column; }
.sc-tiles .tile-n { font-size: 1.9rem; }

/* The comparison. Tabular figures so "22h" under one tile lines up with
   "3.5 days" under the next, and never wider than the sentence below it. */
.sc-cmp {
  display: block; margin-top: .35rem;
  font-size: .78rem; font-weight: 600; color: var(--tb-ink);
  font-variant-numeric: tabular-nums;
}

/* What moves it. Quiet, and it wraps -- it is prose, not a label. */
.sc-why {
  display: block; margin-top: .45rem;
  font-size: .76rem; line-height: 1.4; color: var(--tb-muted);
}

/* An aside about the figures rather than a status about the business, so
   it is a rule and a sentence, not a coloured banner. */
.sc-note {
  font-size: .8rem; color: var(--tb-muted);
  border-left: 2px solid var(--tb-line); padding: .1rem 0 .1rem .7rem;
}

.sc-table td { vertical-align: top; }
.sc-table .badge { font-weight: 600; }

/* 576px: the same treatment the bill gets. Five columns of which two are
   dates and one is money will not sit beside a member's name on a phone,
   so each row becomes a block and each cell a labelled line. The labels
   come out of data-label rather than a second copy of the markup. */
@media (max-width: 575.98px) {
  .sc-table thead { display: none; }
  .sc-table, .sc-table tbody, .sc-table tr, .sc-table td { display: block; width: auto; }
  .sc-table tr { border-bottom: 1px solid var(--tb-line); padding: .5rem .25rem; }
  .sc-table td { border: none; padding: .15rem .5rem; text-align: left !important; }
  .sc-table td[data-label]:not([data-label="Member"])::before {
    content: attr(data-label);
    display: inline-block; min-width: 6rem;
    font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .sc-table .sub { display: inline; margin-left: .35rem; }
  .sc-table td[data-label="Member"] .sub { display: block; margin-left: 0; }
}

/* --- P12: request prices ---------------------------------------------------
   The tick column on the bill, the "N asked" chip on a line that is out
   with somebody, and the picker page.

   The picker is a form, not a table of suppliers: each row is a large
   label wrapping its own checkbox, so the whole row is the hit target on
   a phone and the reasons -- categories matched, service committed --
   sit under the name where they are read rather than in columns that
   would wrap into nonsense at 400px.

   Colour: the chip is navy-on-tint, not orange and not amber. It is a
   fact about the line ("this is out with two suppliers"), not an action
   and not a warning. Orange stays on Send. */

.bill-tick-h { width: 2.2rem; }
.bill-tick   { width: 2.2rem; text-align: center; }
.bill-tick .form-check-input { margin: 0; cursor: pointer; }

.chip-asked {
  display: inline-block; margin-top: .15rem;
  font-size: .7rem; line-height: 1.5; text-decoration: none;
  padding: 0 .4rem; border-radius: 1rem;
  background: var(--tb-hover); color: var(--tb-navy);
  border: 1px solid var(--tb-line-2);
}
.chip-asked:hover { background: var(--tb-sunk); color: var(--tb-navy-2); }

/* The lines table, shared by the picker and both lead pages. */
.req-lines td, .req-lines th { vertical-align: middle; }
.req-lines .sub { display: block; }
.req-price { color: var(--tb-faint); }

.req-why { font-size: .85rem; color: var(--tb-muted); max-width: 70ch; }
.req-sub-head {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--tb-faint); margin: .9rem 0 .35rem;
}

.req-sup {
  display: flex; align-items: flex-start; gap: .7rem;
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  padding: .65rem .8rem; margin-bottom: .5rem; cursor: pointer;
}
.req-sup:hover { border-color: var(--tb-line-2); background: var(--tb-hover); }
.req-sup:focus-within { border-color: var(--tb-orange); }
.req-sup .form-check-input { margin-top: .15rem; flex: 0 0 auto; }
.req-sup-main { display: block; min-width: 0; }
.req-sup-name { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-weight: 600; }
.req-sup-cats { display: block; font-size: .82rem; color: var(--tb-muted); }
.req-sup-sla  { display: block; font-size: .74rem; color: var(--tb-faint); font-variant-numeric: tabular-nums; }

.req-go { display: flex; gap: .5rem; flex-wrap: wrap; }

/* 400px: the lines table becomes a list, the same way the bill does. */
@media (max-width: 575.98px) {
  .req-lines thead { display: none; }
  .req-lines, .req-lines tbody, .req-lines tr, .req-lines td { display: block; width: auto; }
  .req-lines tr { border-bottom: 1px solid var(--tb-line); padding: .5rem .25rem; }
  .req-lines td { border: none; padding: .15rem .5rem; text-align: left !important; }
  .req-lines td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    display: inline-block; min-width: 5.5rem;
    font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .req-lines td[data-label="Line"]::before { display: none; }
  .bill-tick { text-align: left; }
}

/* --- P13: compare quotes ---------------------------------------------------
   The comparison panel, the two chips on a bill line, and the
   selected/not-selected badge on the member's lead page.

   ONE CARD PER SUPPLIER, not one column per supplier: a table with a
   column each reads well at two quotes and falls apart at four, and at
   400px it falls apart at two. The card is the unit of the decision, so
   the price, the promise, the scorecard and the button live in it
   together.

   Colour: orange is on "Select this quote" and nowhere else on the page.
   The cheapest price is deliberately NOT green -- cheapest is not best,
   and a screen that colours it is a screen with an opinion about which
   business a member should use. The only green here is the flag on the
   quote the member themselves chose, which is a statement of fact about
   the bill and not a recommendation. */

.cmp-card {
  border: 1px solid var(--tb-line);
  border-radius: var(--tb-radius);
  padding: .8rem .9rem;
  margin-bottom: .75rem;
  background: var(--tb-surface);
}
.cmp-card:last-child { margin-bottom: 0; }
.cmp-card.is-chosen { border-color: var(--tb-good); background: var(--tb-surface); }

.cmp-head { display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap; }
.cmp-who  { min-width: 0; margin-right: auto; }
.cmp-name { font-weight: 600; text-decoration: none; }
.cmp-name:hover { text-decoration: underline; }
.cmp-status { display: block; font-size: .78rem; color: var(--tb-muted); }

.cmp-price   { text-align: right; white-space: nowrap; }
.cmp-figure  { display: block; font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.cmp-when    { display: block; font-size: .72rem; color: var(--tb-faint); }
.cmp-nofigure { font-size: .85rem; color: var(--tb-faint); }

.cmp-chosen-flag {
  margin-top: .5rem; font-size: .78rem; font-weight: 600; color: var(--tb-good);
}

.cmp-facts {
  display: flex; flex-wrap: wrap; gap: .15rem .9rem;
  margin: .6rem 0 0; font-size: .8rem;
}
.cmp-facts dt { color: var(--tb-faint); font-weight: 400; }
.cmp-facts dt::after { content: ":"; }
.cmp-facts dd { margin: 0 .6rem 0 0; color: var(--tb-ink); }

.cmp-score {
  margin-top: .55rem; padding-top: .55rem; border-top: 1px dashed var(--tb-line);
  display: flex; flex-wrap: wrap; gap: .2rem 1.1rem; font-size: .8rem;
}
.cmp-score-bit { display: inline-flex; gap: .35rem; align-items: baseline; }
.cmp-score-k { color: var(--tb-faint); }
.cmp-score-v { font-weight: 600; font-variant-numeric: tabular-nums; }
.cmp-score-note { flex-basis: 100%; font-size: .72rem; color: var(--tb-faint); }

.cmp-lines { margin-top: .6rem; }
.cmp-lines > summary { font-size: .8rem; color: var(--tb-navy); cursor: pointer; }
.cmp-lines > summary:hover { color: var(--tb-navy-2); }
.cmp-lines .table { margin-top: .5rem; }

.cmp-act {
  margin-top: .7rem; padding-top: .7rem; border-top: 1px solid var(--tb-line);
  display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap;
}
.cmp-act-field { min-width: 15rem; }
.cmp-act .form-control, .cmp-act .form-select { min-width: 12rem; }
.cmp-act .btn { white-space: nowrap; }

.cmp-note { margin-top: .6rem; font-size: .78rem; color: var(--tb-muted); }

/* The bill chips. "N quotes" is the same navy chip as "N asked" with a
   heavier border -- a line with answers waiting is a louder fact than a
   line that is merely out, but it is still a fact and not an action. */
.chip-quoted { border-color: var(--tb-navy); font-weight: 600; }
.chip-chosen { color: var(--tb-good); border-color: var(--tb-good); background: var(--tb-surface); }
.chip-chosen:hover { color: var(--tb-good); background: var(--tb-hover); }

/* The lead page's badge. Green only for the one that was chosen. */
.cmp-badge-on  { background: var(--tb-good); color: #fff; font-weight: 600; }
.cmp-badge-off { background: var(--tb-sunk); color: var(--tb-muted); font-weight: 500; }

@media (max-width: 575.98px) {
  .cmp-head { flex-direction: column; gap: .25rem; }
  .cmp-price { text-align: left; }
  .cmp-act { flex-direction: column; align-items: stretch; }
  .cmp-act .btn, .cmp-act .form-control, .cmp-act .form-select { width: 100%; min-width: 0; }
}

/* --- P17: off-network suppliers --- */
/* The "ask somebody else" block sits under the network picker and is
   deliberately quieter than it: a sunk panel, not a second offer of
   equal weight. The network is what a member pays for; this is the door
   beside it. Existing tokens only. */
.offnet { background: var(--tb-sunk); }
.offnet .form-check-label { font-weight: 500; }

/* "Outside the network" beside a lead's name. It reuses .chip-kind --
   navy/grey/sand, never orange and never red-amber-green -- because it
   is a fact about who was asked, not an action and not a status. */
.chip-kind.chip-offnet {
  border-color: var(--tb-line-2); color: var(--tb-muted); background: var(--tb-surface);
  vertical-align: middle; margin-left: .35rem;
}
/* --- P18b --- */
/* Two windows on the scorecard: recent form beside track record.
   A P18 tile was one figure with a comparison under it. A P18b tile is a
   MEASURE with two of those inside it, because the comparison that
   matters most is the measure against itself a year and a half ago -- a
   supplier at 40% this quarter and 85% over two years has a problem this
   quarter, and two separate cards would have made a reader hold one
   number in their head while they found the other.

   So the measure's name moves to the top of the tile, the two windows
   sit side by side under it, and the sentence that says what moves the
   figure stays at the bottom where it belongs to both.

   Colour moves INWARD with them: the border is on the window now, not
   the tile, for exactly the same reason -- one border could only have
   told half the story. Same --tb-good / --tb-crit as everywhere else,
   same rule that the middle stays grey, and still nothing orange.

   The months are never written here. They come from the row, so this
   file does not need editing when scorecard.windows moves. */

.sc-measures { grid-template-columns: repeat(auto-fit, minmax(19.5rem, 1fr)); }
.sc-measure > .tile-k { margin-top: 0; margin-bottom: .5rem; }

/* The two windows. A grid rather than a flex row so both columns are the
   same width whatever is in them -- "100%" beside "—" must not make the
   empty window a third of the size of the one with a figure in it. */
.sc-windows {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: .35rem 1rem; align-items: start;
}
.sc-win {
  min-width: 0;
  border-left: 3px solid var(--tb-line);
  padding-left: .6rem;
}
.sc-win.is-live.tile-success { border-left-color: var(--tb-good); }
.sc-win.is-live.tile-danger  { border-left-color: var(--tb-crit); }

/* "Last 3 months". Quieter than the measure's own name above it, and it
   never wraps mid-number. */
.sc-win-k {
  display: block; margin-bottom: .1rem;
  font-size: .66rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--tb-faint); white-space: nowrap;
}

/* The figure. Smaller than P18's single tile number -- there are two of
   them now and they are read against each other, not across the page --
   and always ink, because the status is the border. The rule it is
   overriding (.tile:not(.is-live) .tile-n) greys the number on a tile
   with no colour, which was right when the tile WAS the figure. */
.sc-measure .tile-n { font-size: 1.5rem; }
.sc-measure .sc-win .tile-n { color: var(--tb-ink); }
.sc-measure .sc-win .tile-n.sc-none { color: var(--tb-faint); font-weight: 600; }
.sc-none-k { color: var(--tb-faint); font-weight: 500; }

.sc-win .sc-cmp { margin-top: .2rem; font-size: .74rem; }

/* Won / lost / decided under the win rate, and the date under a figure
   when the two windows were cut on different nights (which means a run
   failed, so it is worth saying). */
.sc-win-sub, .sc-win-date {
  display: block; margin-top: .25rem;
  font-size: .72rem; color: var(--tb-muted);
  font-variant-numeric: tabular-nums;
}
.sc-win-date { color: var(--tb-faint); }

/* The "only N decided" caution, per window. It was one note under the
   whole card in P18; with two windows it has to belong to the figure it
   is about, because the three-month one will carry it for months after
   the two-year one has stopped needing it. Still a sentence and a rule,
   not a coloured banner. */
.sc-win-note {
  display: block; margin-top: .35rem;
  font-size: .72rem; line-height: 1.35; color: var(--tb-muted);
}

.sc-measure .sc-why { margin-top: .6rem; }

/* 576px: stack the two windows. Two columns inside one tile inside a
   400px phone leaves about 170px a column, which sets "category median"
   three words to a line and makes the pair harder to compare than one
   under the other. Stacked, each window keeps its own coloured rule down
   the left and the labels carry the months, so nothing is ambiguous. */
@media (max-width: 575.98px) {
  .sc-measures { grid-template-columns: 1fr; }
  .sc-windows { grid-template-columns: 1fr; gap: .7rem; }
  .sc-win-k { white-space: normal; }
}

/* --- P21: the counter -------------------------------------------------------
   Scan a card, check it, record the sale. A phone held one-handed with a
   customer waiting, so the camera is big, the answer is one word in one
   colour, and the only orange on the page is the Record button.

   The three states are the public /verify page's three states and wear
   the same colours for the same reason: green is current, AMBER (never
   red) is lapsed because a lapsed membership is a renewal conversation
   and not a fraud alert, and grey is "we do not know who this is".
   Status colours, never actions.

   "Scanned" / "Not scanned" reuse .chip-kind -- a fact about the
   evidence, not a status and not a warning. Typing a member number is
   allowed and normal; colouring it like a failure would teach counter
   hands to ignore it. */

.scan-stage {
  position: relative; width: 100%; max-width: 28rem; margin: 0 auto;
  aspect-ratio: 1 / 1; background: var(--tb-ink); border-radius: var(--tb-radius); overflow: hidden;
}
.scan-stage video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-frame {
  position: absolute; inset: 15%; border: 2px solid rgba(255, 255, 255, .85);
  border-radius: var(--tb-radius); box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .25);
}

.scan-result {
  display: flex; align-items: center; gap: .75rem; margin-top: .75rem;
  padding: .75rem; border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  background: var(--tb-sunk);
}
.scan-state {
  flex: 0 0 auto; padding: .35rem .6rem; border-radius: .3rem;
  font-weight: 700; font-size: .78rem; letter-spacing: .06em; text-transform: uppercase;
  border: 1px solid var(--tb-line-2); color: var(--tb-muted); background: var(--tb-surface);
}
.scan-state.is-valid   { border-color: var(--tb-good); color: var(--tb-good); background: #eaf5ef; }
.scan-state.is-lapsed  { border-color: var(--tb-warn); color: var(--tb-warn); background: #fdf5e3; }
.scan-state.is-unknown { border-color: var(--tb-line-2); color: var(--tb-muted); background: var(--tb-surface); }
.scan-business { font-weight: 600; }

/* The day's list. A voided row is still readable -- nothing is deleted,
   and a row struck through in grey says "this did not count" without
   hiding what was recorded or who took it back. */
tr.is-voided td { color: var(--tb-faint); text-decoration: line-through; }
tr.is-voided td:last-child { text-decoration: none; }

.chip-kind.chip-scanned { border-color: var(--tb-line-2); color: var(--tb-ink); background: var(--tb-surface); }

/* "Counter" beside a supplier's name on the member's Ledger. A fact
   about how the row came to exist -- scanned at a trade counter rather
   than reported at the end of the month -- so it is .chip-kind and not a
   colour anybody could read as good or bad news. */
.chip-kind.chip-counter {
  border-color: var(--tb-line-2); color: var(--tb-muted); background: var(--tb-surface);
  vertical-align: middle; margin-left: .35rem;
}

/* --- P19: purchase orders --------------------------------------------------
   Two chips and one card, all on existing tokens.

   "PO 4154-2656-1" is navy like every other bill chip: it is a FACT about
   the line -- something has been ordered against it -- and the door to the
   document that says so. It is not green, because a committed figure is
   not an achievement, and it is not orange, because orange is an action.

   "Raise PO" IS an action, and it is the only chip in the bill that is:
   it appears only on a line that carries a selected quote and no order
   yet, which is exactly the moment the member has decided and not yet
   acted. Orange text on the surface, never a fill -- the fill belongs to
   the real buttons. */
.chip-po    { border-color: var(--tb-navy); font-weight: 600; }
.chip-raise { color: var(--tb-orange-ink); border-color: var(--tb-orange-ink); }
.chip-raise:hover { color: var(--tb-orange-ink); background: var(--tb-hover); }

/* The order card on the supplier's lead page. A left edge in navy, which
   is what the rest of the app uses for structure, so an order reads as
   part of the lead rather than as an alert about it. */
.po-card { border-left: 3px solid var(--tb-navy); }

/* The timeline on the member's order page: four facts, one per line, the
   ones that have not happened yet said in words rather than left out. */
.po-timeline li { padding: .3rem 0; border-bottom: 1px dashed var(--tb-line); }
.po-timeline li:last-child { border-bottom: 0; }
.po-timeline strong { display: inline-block; min-width: 8.5rem; color: var(--tb-faint); font-weight: 500; }

/* The order form's line table. The figure column is the one that matters,
   so it is the one that is tabular. */
.po-lines input.text-end { font-variant-numeric: tabular-nums; }

/* --- P23: variations -------------------------------------------------
   The register on the Job Desk, and the form that writes it.

   IT BORROWS THE BILL'S FURNITURE ON PURPOSE. The toolbar, the row
   actions and the phone layout are .bill-bar, .bill-act and the same
   data-label collapse the bill uses, because a variation is the same
   kind of object on the same screen and a second visual language for it
   would be two things to learn. What is new here is only what is new in
   the data: a number column, a status badge, and one total.

   COLOUR. The status badge is the only colour on the tab, and it is
   status colour -- amber out for sign-off, green approved, red declined,
   grey draft and withdrawn -- never orange. Orange stays on the two
   actions (New variation, Send for approval), which is the rule the
   whole app follows. */

.var-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.var-bar-tot { display: flex; align-items: baseline; gap: .4rem; font-variant-numeric: tabular-nums; }
.var-tot-k { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); }
.var-tot-v { font-weight: 700; font-size: .95rem; margin-right: .4rem; }

.var-table td { vertical-align: middle; }
.var-table tfoot th { background: var(--tb-sunk); font-size: .8rem; }
.var-no-h { width: 3.5rem; }
.var-no { font-weight: 700; color: var(--tb-muted); }
.var-title { font-weight: 600; }
.var-price { font-weight: 600; white-space: nowrap; }
.var-dates .sub { display: block; }
.var-reason { font-style: italic; }
.var-acts-h { width: 7rem; }
.var-acts { white-space: nowrap; text-align: right; }
.var-is-editing > td { background: var(--tb-hover); }

/* The note under the register: where the money actually went. Quiet, and
   a sentence rather than a legend, because it is explaining a decision
   (cost on the bill, price on the variation) and not labelling a
   control. */
.var-foot-note {
  padding: .75rem 1rem; border-top: 1px solid var(--tb-line);
  font-size: .82rem; color: var(--tb-muted);
}

/* The line grid. Four narrow columns that have to survive a phone, so
   they collapse the same way the bill's do -- see the media query at the
   end of the bill block for the mechanism. */
.var-lines-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap; margin-bottom: .35rem;
  font-weight: 600;
}
.var-line-table th { white-space: nowrap; font-size: .78rem; }
.var-line-table td { vertical-align: middle; padding: .3rem .4rem; }
.var-line-table td:first-child, .var-line-table th:first-child { padding-left: 0; }
.var-l-qty  { width: 6rem; }
.var-l-unit { width: 8rem; }
.var-l-rate { width: 8rem; }

/* The send block is a second card under the form, sunk, because it is
   the step AFTER saving and should not read as a second equal offer --
   the same argument .offnet makes on the request picker. */
.var-send { background: var(--tb-sunk); }
.var-newclient > summary { cursor: pointer; font-size: .88rem; color: var(--tb-navy); }

@media (max-width: 575.98px) {
  .var-table thead, .var-line-table thead { display: none; }
  .var-table tr, .var-line-table tr { display: block; border-bottom: 1px solid var(--tb-line); padding: .5rem 0; }
  .var-table td, .var-line-table td { display: flex; gap: .75rem; border: 0; padding: .2rem 0; }
  .var-table td::before, .var-line-table td::before {
    content: attr(data-label); flex: 0 0 6.5rem;
    font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint);
  }
  .var-table td.var-acts, .var-acts { text-align: left; }
  .var-l-qty, .var-l-unit, .var-l-rate { width: auto; }
}
/* --- NS-20 ---------------------------------------------------------------
   The upload form on the Files tab, and the Attach picker on a lead.

   The grid is P15's people-form-grid, cloned rather than shared: the two
   forms want different column widths (a file input needs room, a role
   dropdown does not) and one grid bent to serve both is how a layout
   stops being editable. Everything else here is existing tokens --
   .bill-act-del does the remove button, .form-text does the caps line,
   and the Attach button is the ordinary .btn-primary.

   No new colour. An upload is an action, so its button is orange like
   every other action, and the remove is P10's red-on-hover cross. */
.ns20-form-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: .5rem .6rem; align-items: end; }
.ns20-f      { display: block; margin: 0; }
.ns20-f-file { grid-column: span 5; }
.ns20-f-kind { grid-column: span 3; }
.ns20-f-desc { grid-column: span 2; }
.ns20-f-go   { grid-column: span 2; }

/* The actions column on the Files table: as narrow as its one button. */
.ns20-acts-h { width: 2.5rem; }
.ns20-acts   { text-align: right; white-space: nowrap; }

/* The Attach picker sits inside the attachments card, under the list, so
   it needs a rule above it rather than a heading of its own. */
.ns20-attach { border-top: 1px dashed var(--tb-line); padding-top: .6rem; }

@media (max-width: 991.98px) {
  .ns20-f-file { grid-column: span 12; }
  .ns20-f-kind { grid-column: span 5; }
  .ns20-f-desc { grid-column: span 4; }
  .ns20-f-go   { grid-column: span 3; }
}

@media (max-width: 575.98px) {
  .ns20-form-grid { grid-template-columns: repeat(4, 1fr); }
  .ns20-f-file, .ns20-f-kind, .ns20-f-desc, .ns20-f-go { grid-column: span 4; }
}
/* --- end NS-20 --- */
/* --- P20: budget -------------------------------------------------------------
   The Budget tab: the bill of work read as money. Four tiles, the stage
   roll-up with a bar per stage, and what moved this week.

   ALMOST NOTHING NEW. The tiles are the Day Sheet's .tile-row / .tile,
   the week is the Day Sheet's .queue / .queue-row, the stage table is
   the bill's .tb-table and its 400px block layout, and every colour is
   an existing token. What is added here is a third line on a tile, the
   bar, and the two variance colours.

   RED IS OVER BUDGET AND NOTHING ELSE. --tb-crit on the figure, never as
   a background and never on a control: a variance is a status, and the
   only orange on this page is Save. --tb-good marks a line that came in
   UNDER, which is the same fact with the other sign and deserves to be
   as easy to see.

   Every block below is closed where it is opened, including the media
   query at the end -- three earlier packets left one open and took the
   desktop layout with it. */

/* The tile grows a third line: the tile itself says the figure, the key
   says what it is, and this says what it is OF. Same idea as the
   scorecard's tile body (.sc-tiles), which is why the row shares its
   minimum column width -- four money figures do not fit in 8.5rem. */
.budget-tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); align-items: stretch; }
.budget-tiles .tile { display: flex; flex-direction: column; }
.budget-tiles .tile-n { font-size: clamp(1.15rem, 2.2vw, 1.55rem); }
.budget-tile-sub {
  margin-top: .3rem; font-size: .73rem; line-height: 1.3; color: var(--tb-muted);
}
.budget-tile-sub a { color: var(--tb-navy); }

/* Over its budget by more than this job's tolerance, and under it. */
.budget-over  { color: var(--tb-crit); font-weight: 700; }
.budget-under { color: var(--tb-good); }

/* The three numbers the member tells the tab. A form, laid out as a row
   that wraps, because it is three short fields and a button rather than
   a page of settings. */
.budget-set { display: flex; align-items: flex-end; gap: .6rem; flex-wrap: wrap; }
.budget-f { display: block; margin: 0; flex: 0 0 9rem; }
.budget-f .form-control { font-variant-numeric: tabular-nums; }
.budget-f-go { flex: 0 0 7rem; }
.budget-set-note {
  flex: 1 1 100%; font-size: .75rem; color: var(--tb-muted); max-width: 70ch;
}

/* The stage roll-up. */
.budget-table th { white-space: nowrap; }
.budget-table td { vertical-align: middle; }
.budget-table tfoot th { background: var(--tb-sunk); font-size: .8rem; }
.budget-bar-h { width: 9rem; }

/* % invoiced. The bar is capped at 100 in the template -- it cannot draw
   past full -- and the figure beside it is not, so "142%" still reads as
   142%. Navy is structure, which is what a progress bar is; it turns red
   only when the stage it belongs to is over budget. */
.budget-bar {
  display: inline-block; vertical-align: middle;
  width: 4.5rem; height: .45rem; border-radius: .25rem;
  background: var(--tb-sunk); overflow: hidden;
}
.budget-bar .fill { display: block; height: 100%; background: var(--tb-navy); border-radius: .25rem; }
.budget-bar .fill.is-over { background: var(--tb-crit); }
.budget-bar-n {
  margin-left: .4rem; font-size: .76rem; color: var(--tb-muted); font-variant-numeric: tabular-nums;
}

/* What moved this week. The Day Sheet's queue, with the money on the
   right where the due date sits there. */
.budget-moved .queue-row { text-decoration: none; color: inherit; }
.budget-moved .queue-meta .badge { font-variant-numeric: tabular-nums; }

/* 400px: the tiles stack two-up and the table becomes blocks, each money
   column a labelled line -- exactly what the bill does, because it is
   the same table read by the same thumb. */
@media (max-width: 575.98px) {
  .budget-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .budget-tiles .tile-n { font-size: 1.1rem; }

  .budget-f { flex: 1 1 8rem; }
  .budget-f-go { flex: 1 1 100%; }

  .budget-table thead { display: none; }
  .budget-table, .budget-table tbody, .budget-table tfoot,
  .budget-table tr, .budget-table td, .budget-table th { display: block; width: auto; }
  .budget-table tr { border-bottom: 1px solid var(--tb-line); padding: .5rem .25rem; }
  .budget-table td, .budget-table th { border: none; padding: .15rem .5rem; text-align: left !important; }
  .budget-table td[data-label]:not([data-label=""])::before,
  .budget-table th[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    display: inline-block; min-width: 5.5rem;
    font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .budget-table td[data-label="Stage"]::before { display: none; }
  .budget-table tfoot tr { background: var(--tb-sunk); }
  .budget-bar { width: 6rem; }
}
/* --- P22: rebates -----------------------------------------------------------
   Three chips, one banner, one dimmed row. All on existing tokens; nothing
   here invents a colour.

   THE KIND CHIPS ARE GREY, all three of them. "Counter", "Order" and
   "Reported" are three ways the same fact -- a member bought something --
   reached this system. None of them is good news or bad news, so none of
   them is red, amber or green, and none is orange because orange is an
   action and there is no action on a chip. This is P21's rule for
   "Not scanned", applied to the same kind of fact. */
.chip-kind.chip-rb-counter,
.chip-kind.chip-rb-po,
.chip-kind.chip-rb-reported {
  border-color: var(--tb-line-2); color: var(--tb-muted); background: var(--tb-surface);
  vertical-align: middle; margin-left: .2rem;
}

/* "Not on your report" IS amber, because it IS a status -- a sale
   TradeBuilders watched happen that the supplier's own figures do not
   appear to carry. Amber and not red: a sale late in the period that went
   onto next month's report reads exactly like this and is not a fault. */
.chip-kind.chip-rb-unreported {
  border-color: #e4cf9a; color: var(--tb-warn); background: #fff8e6;
  vertical-align: middle; margin-left: .2rem;
}

/* The signal above the tables. Same treatment as .pwned-banner, which is
   the app's existing "you should know this before you go on" shape. */
.rb-signal {
  background: #fff8e6; border: 1px solid #f3e2b8; border-left: 3px solid var(--tb-warn);
  border-radius: .4rem; padding: .6rem .9rem; font-size: .88rem;
  display: flex; gap: .6rem; align-items: flex-start;
}
.rb-signal i { color: var(--tb-warn); margin-top: .15rem; }
.rb-signal .small { color: var(--tb-muted); margin-top: .25rem; }

/* A line the supplier has taken off the report. Struck through and faded
   rather than hidden: it is still one of the invoices we found, and a row
   that vanished would leave them wondering what they had just done. */
.rb-lines tr.rb-off td { color: var(--tb-faint); }
.rb-lines tr.rb-off .num { text-decoration: line-through; }

/* The money columns on both tables are the point of the page. */
.rb-table .num, .rb-lines .num { font-variant-numeric: tabular-nums; }

/* "What we saw" leads the eye on the period list -- it is the column that
   is new. A weight, not a colour. */
.rb-table .rb-platform { font-weight: 600; }

/* The confirm card. A navy left edge, which is what this app uses for
   structure, so the one action on the page sits on something that reads as
   part of the report rather than as an alert about it. */
.rb-confirm { border-left: 3px solid var(--tb-navy); }

/* --- P-LOG --- */
/* The contact log, the site diary and escalation. Built on what is
   already here: the Day Sheet's .queue for the rows, the address book's
   .chip for the filters, the People tab's form grid for the form. The
   only genuinely new shapes are the thread and the entry card. */

/* The chip row and the search box, the same head the Contacts list has.
   At 400px the search drops under the chips and takes the full width --
   an input that stayed beside seven chips would be forty pixels wide. */
.log-head { row-gap: .5rem; }
.log-search { min-width: 14rem; }
@media (max-width: 575.98px) {
  .log-search { margin-left: 0 !important; width: 100%; }
}

/* THE KIND CHIP IS GREY, ALWAYS. A call, a council RFI and a defect are
   three things that happened and none of them is good or bad news --
   P22's rule for the Counter / Order / Reported chips, for the same
   reason. What IS coloured is the stripe and the status badge, because
   those say where the work is. */
.log-kind { vertical-align: middle; margin-right: .15rem; }

/* "With TradeBuilders" is navy -- structure, not alarm. An escalation is
   not a fault, it is a hand-over, and red would read as one. */
.chip-kind.log-escalated {
  border-color: #b9cdf0; color: var(--tb-navy); background: var(--tb-hover);
  vertical-align: middle; margin-left: .2rem;
}

/* A log row carries more than a lead row does -- a kind, who it is
   about, the job, and what was decided -- so its second line is allowed
   to wrap rather than being cut off mid-sentence by the ellipsis the
   Day Sheet's own rows use. */
.log-queue .queue-sub { white-space: normal; }
.log-decided { display: block; color: var(--tb-muted); }

/* Two queues in one card (the Day Sheet, where the overdue log items sit
   above the leads) must not lose the line between them. */
.queue + .queue .queue-row:first-child { border-top: 1px solid var(--tb-line); }

/* The form. THE PHONE IS THE POINT: one column at 400px, the kind first,
   the line second, the button under them. On a desk it is three columns
   so the whole thing is one glance. */
.log-form-grid {
  display: grid; gap: .5rem; align-items: end;
  grid-template-columns: minmax(10rem, 1fr) minmax(0, 2fr) 7rem;
}
.log-form-more { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); margin-top: .5rem; }
.log-f { display: block; min-width: 0; }
.log-f .form-label { margin-bottom: .15rem; font-size: .78rem; font-weight: 600; }
.log-f-detail { grid-column: 1 / -1; }
.log-f-wide { grid-column: span 2; }
@media (max-width: 575.98px) {
  .log-form-grid, .log-form-more { grid-template-columns: 1fr; }
}
/* A field the chosen kind has no use for is hidden by app.js. Bootstrap
   sets display on labels, so [hidden] needs to win. */
.log-form-card [hidden] { display: none !important; }

/* The entry itself: the same left edge the queue's stripe draws, so an
   entry opened from a red row is still red. */
.log-entry { border-left: 3px solid var(--tb-line-2); }
.log-entry-danger    { border-left-color: var(--tb-crit); }
.log-entry-warning   { border-left-color: var(--tb-warn); }
.log-entry-info      { border-left-color: var(--tb-navy); }
.log-entry-secondary { border-left-color: var(--tb-line-2); }
.log-detail { white-space: pre-line; }
.log-resolution {
  background: var(--tb-sunk); border-radius: .35rem; padding: .5rem .7rem;
  font-size: .9rem;
}
.log-photos { display: flex; flex-wrap: wrap; gap: .5rem; }
.log-photo {
  display: inline-flex; align-items: center; gap: .3rem; font-size: .82rem;
  border: 1px solid var(--tb-line-2); border-radius: .3rem; padding: .25rem .5rem;
  text-decoration: none; color: inherit; background: var(--tb-surface);
}
.log-photo:hover { background: var(--tb-hover); }

/* The thread. Navy ticks down the left, which is the shape a
   conversation already has on the lead page. */
.log-thread { display: flex; flex-direction: column; gap: .6rem; }
.log-event { border-left: 2px solid var(--tb-line-2); padding-left: .7rem; }
.log-event-status .log-event-body, .log-event-resolution .log-event-body { color: var(--tb-muted); }
.log-event-head { font-size: .8rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.log-event-body { font-size: .9rem; white-space: pre-line; }
.log-follow { display: flex; gap: .5rem; }
.log-follow .form-control { flex: 1; }

/* Settling it: the resolution, the note and the button in one row on a
   desk, stacked on a phone. */
.log-resolve-grid {
  display: grid; gap: .5rem; align-items: end;
  grid-template-columns: minmax(10rem, 1fr) minmax(0, 2fr) 8rem;
}
@media (max-width: 575.98px) { .log-resolve-grid { grid-template-columns: 1fr; } }

/* The escalation card. Orange is the ACTION colour in this application
   and the button is already orange; the card around it is navy-edged
   structure, because the words are what matter before it is pressed. */
.log-escalate { border-left: 3px solid var(--tb-navy); }
/* --- end P-LOG --- */
/* --- P26-P27: schedule -------------------------------------------------
   The Schedule tab, the chart on it, and "This week on site" on the Day
   Sheet.

   THE CHART IS A CSS GRID AND NOTHING ELSE. `.sch-track` has one column
   per DAY of the job's span (--sch-days, set inline from PHP), the week
   headings span seven of them, and a bar is placed with `grid-column`
   worked out from its own dates. No library, no canvas, no script: five
   bars do not justify 90 KB of somebody else's code, and this prints.

   COLOUR IS STATUS, NEVER ACTION, which is the app's rule and on this
   tab it is the whole picture: navy is work that is booked, navy with a
   tick is work a trade has confirmed, RED is nobody booked or a trade
   who said no -- the thing a builder opens this tab to find -- and green
   is done. Every one of them is an existing token; nothing here invents
   a colour, and the only orange on the tab is on the buttons.

   AT 400px THE CHART IS NOT DRAWN AT ALL. Below 576px the grid is
   display:none and the tables collapse to labelled rows the way the bill
   and the variations register already do. A Gantt chart on a phone is a
   smear; "Tue: Dave, pour slab" is not. */

.sch-top { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.sch-legend { display: flex; gap: .75rem; flex-wrap: wrap; font-size: .75rem; color: var(--tb-muted); }
.sch-legend span { display: inline-flex; align-items: center; gap: .3rem; }
.sch-key { width: .85rem; height: .55rem; border-radius: .15rem; display: inline-block; }

/* --- the chart --- */
.sch-chartwrap { overflow-x: auto; }
.sch-chart { min-width: 30rem; }
.sch-row { display: grid; grid-template-columns: 9rem 1fr; align-items: center; gap: .5rem; }
.sch-row + .sch-row { margin-top: .25rem; }
.sch-name {
  font-size: .8rem; color: var(--tb-ink); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.sch-task .sch-name { color: var(--tb-muted); padding-left: .75rem; }
.sch-track {
  display: grid; grid-template-columns: repeat(var(--sch-days, 28), 1fr);
  min-height: 1.35rem; align-items: center;
  background: repeating-linear-gradient(
    to right, var(--tb-sunk) 0, var(--tb-sunk) 1px, transparent 1px,
    transparent calc(100% / var(--sch-days, 28) * 7));
}
.sch-head .sch-track { background: none; min-height: 1.1rem; }
.sch-week {
  font-size: .7rem; color: var(--tb-faint); border-left: 1px solid var(--tb-line);
  padding-left: .2rem; white-space: nowrap; overflow: hidden;
}
/* Today, as a hairline down the chart. Amber would be a warning and navy
   would be a bar, so it is the faint rule -- a position, not a status. */
.sch-today { grid-row: 1; border-left: 2px solid var(--tb-line-2); height: 100%; }

.sch-bar {
  grid-row: 1; height: 1.05rem; border-radius: .2rem; min-width: .5rem;
  display: flex; align-items: center; gap: .25rem; padding: 0 .3rem;
  overflow: hidden;
}
.sch-bar-stage { height: 1.2rem; }
.sch-bar-t { font-size: .68rem; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sch-tick { font-size: .6rem; color: #fff; flex: 0 0 auto; }
.sch-nodate { grid-column: 1 / -1; font-size: .7rem; color: var(--tb-faint); }

.sch-bar.is-planned,    .sch-key.is-planned    { background: var(--tb-navy); }
.sch-bar.is-confirmed,  .sch-key.is-confirmed  { background: var(--tb-navy-2); box-shadow: inset 0 0 0 1px #fff3; }
.sch-bar.is-unassigned, .sch-key.is-unassigned { background: var(--tb-crit); }
.sch-bar.is-declined                           { background: var(--tb-crit); opacity: .75; }
.sch-bar.is-done,       .sch-key.is-done       { background: var(--tb-good); }
.sch-bar.is-cancelled { background: var(--tb-line-2); }
.sch-bar.is-cancelled .sch-bar-t { color: var(--tb-muted); text-decoration: line-through; }
.sch-bar-stage.is-planned { background: var(--tb-navy); opacity: .55; }

/* --- a stage and its tasks --- */
.sch-stagehead {
  display: flex; align-items: flex-end; gap: .75rem; flex-wrap: wrap;
  padding: .75rem 1rem; border-bottom: 1px solid var(--tb-line);
}
.sch-dates { display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap; }
.sch-f { display: block; }
.sch-f .form-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint); margin-bottom: .1rem; }
.sch-f-dur input { width: 5rem; }
/* The shift checkbox. Deliberately plain and deliberately unticked every
   time the page renders: it is the only control in this application that
   moves a date nobody typed, and it should read as a decision rather than
   as a setting that might be on. */
.sch-shift { display: inline-flex; align-items: center; gap: .35rem; font-size: .8rem; color: var(--tb-muted); padding-bottom: .35rem; }

.sch-table td { vertical-align: middle; }
.sch-title { font-weight: 600; }
.sch-none { color: var(--tb-crit); font-weight: 600; }
.sch-reason { font-style: italic; }
.sch-acts-h { width: 11rem; }
.sch-acts { white-space: nowrap; text-align: right; }
.sch-is-editing > td { background: var(--tb-hover); }
.sch-off td { color: var(--tb-faint); }
.sch-off .sch-title { text-decoration: line-through; }
.sch-formrow > td { background: var(--tb-sunk); }

/* The status badge is the only colour in the table, and it is status
   colour -- grey planned, navy confirmed, red declined, green done. */
.sch-st { font-weight: 600; }
.sch-st-planned   { background: var(--tb-sunk); color: var(--tb-muted); }
.sch-st-confirmed { background: var(--tb-navy); color: #fff; }
.sch-st-declined  { background: #fdecea; color: var(--tb-crit); }
.sch-st-done      { background: #e4f2e6; color: var(--tb-good); }
.sch-st-cancelled { background: var(--tb-sunk); color: var(--tb-faint); }

/* --- the add/edit form --- */
.sch-form { padding: .75rem 0 0; }
.sch-form-head { font-weight: 600; margin-bottom: .4rem; }
.sch-form-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: .5rem .6rem; align-items: end; }
.sch-f-title { grid-column: span 4; }
.sch-f-trade { grid-column: span 3; }
.sch-f-date  { grid-column: span 2; }
.sch-f-dur   { grid-column: span 1; }
.sch-f-item  { grid-column: span 4; }
.sch-f-notes { grid-column: span 6; }
.sch-f-go    { grid-column: span 2; }
.sch-newtrade > summary { cursor: pointer; font-size: .88rem; color: var(--tb-navy); }

/* --- the Day Sheet card --- */
.site-week .site-none { color: var(--tb-crit); font-weight: 600; }

@media (max-width: 575.98px) {
  /* The chart goes entirely. What is left is the same rows as a list. */
  .sch-chartwrap { display: none; }
  .sch-stagehead { align-items: stretch; }
  .sch-dates { width: 100%; }
  .sch-table thead { display: none; }
  .sch-table tr { display: block; border-bottom: 1px solid var(--tb-line); padding: .5rem 0; }
  .sch-table td { display: flex; gap: .75rem; border: 0; padding: .2rem 0; }
  .sch-table td::before {
    content: attr(data-label); flex: 0 0 5.5rem;
    font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint);
  }
  .sch-table td.sch-acts { text-align: left; }
  .sch-form-grid { grid-template-columns: repeat(2, 1fr); }
  .sch-f-title, .sch-f-trade, .sch-f-item, .sch-f-notes, .sch-f-go { grid-column: span 2; }
  .sch-f-date, .sch-f-dur { grid-column: span 1; }
  .sch-f-dur input { width: 100%; }
}
/* --- end P26-P27 --- */
/* --- P28: claims -------------------------------------------------------
   The Claims tab on the Job Desk, and the client's read-only statement
   at /c/{token}.

   IT BORROWS THE BILL'S AND THE VARIATION REGISTER'S FURNITURE ON
   PURPOSE. The toolbar, the row actions, the table and the phone
   collapse are .bill-act, .tb-table and the same data-label mechanism,
   because a claim is the same kind of object on the same screen and a
   second visual language for it would be one more thing to learn.

   COLOUR. The status badge is the only colour in the list -- amber sent,
   green paid, red overdue, grey draft and cancelled -- and red appears
   once more, on an outstanding total that has something overdue in it.
   Orange stays on the two actions (Raise claim, Send), which is the rule
   the whole app follows. */

.clm-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.clm-bar-tot { display: flex; align-items: baseline; gap: .4rem; font-variant-numeric: tabular-nums; }
.clm-tot-k { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); }
.clm-tot-v { font-weight: 700; font-size: .95rem; margin-right: .4rem; }
.clm-over { color: var(--tb-crit); }

/* The schedule's own table: an editable grid, so the cells are inputs and
   the rows have to stay short enough to read on a phone. */
.clm-table td { vertical-align: middle; }
.clm-table .sub { display: block; }
.clm-pct-h { width: 6rem; }
.clm-pct-in { width: 5rem; }
.clm-amt { white-space: nowrap; font-weight: 600; }
.clm-done { color: var(--tb-good); font-weight: 600; }
.clm-row-claimed > td { background: var(--tb-hover); }

/* The running total of the percentages. It goes amber when it is not
   100 -- a warning, never a refusal: these are the percentages in the
   builder's own contract and this screen is not a party to it. */
.clm-pct-total { display: flex; align-items: baseline; gap: .4rem; padding-bottom: .4rem; }
.clm-pct-total.is-off .clm-tot-v { color: var(--tb-warn); }

/* The note under the schedule form: whose percentages these are. A
   paragraph rather than a legend, because it is explaining a decision
   and not labelling a control. */
.clm-note { flex: 1 1 18rem; font-size: .82rem; color: var(--tb-muted); }
.clm-foot-note {
  padding: .75rem 1rem; border-top: 1px solid var(--tb-line);
  font-size: .82rem; color: var(--tb-muted);
}

.clm-list td { vertical-align: middle; }
.clm-no-h { width: 3.5rem; }
.clm-no { font-weight: 700; color: var(--tb-muted); }
.clm-title { font-weight: 600; }
.clm-total { font-weight: 700; white-space: nowrap; }
.clm-dates .sub { display: block; }
.clm-acts-h { width: 9rem; }
.clm-acts { white-space: nowrap; text-align: right; }
.clm-send, .clm-paid { display: inline-block; vertical-align: top; min-width: 8rem; }

/* The variations waiting to be claimed, inside the Raise form. Sunk,
   because it is a list of facts about the claim being raised rather than
   a second form. */
.clm-vars { background: var(--tb-sunk); border: 1px solid var(--tb-line); border-radius: .4rem; padding: .75rem .9rem; }
.clm-vars-head { font-weight: 600; display: block; margin-bottom: .35rem; }
.clm-vars-list { list-style: none; padding: 0; margin: 0 0 .35rem; }
.clm-vars-list li {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .2rem 0; font-size: .88rem; color: var(--tb-muted);
}

@media (max-width: 575.98px) {
  .clm-table thead, .clm-list thead { display: none; }
  .clm-table tr, .clm-list tr { display: block; border-bottom: 1px solid var(--tb-line); padding: .5rem 0; }
  .clm-table td, .clm-list td { display: flex; gap: .75rem; border: 0; padding: .2rem 0; }
  .clm-table td::before, .clm-list td::before {
    content: attr(data-label); flex: 0 0 6.5rem;
    font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint);
  }
  .clm-table td.clm-acts, .clm-list td.clm-acts, .clm-acts { text-align: left; }
  .clm-pct-in { width: 100%; }
}

/* --- the client's statement at /c/{token} ---
   Read on a phone more often than not, by somebody who will see this page
   once or twice. Sized for that: one column, large type, the arithmetic
   above one figure. The colours are literals rather than tokens for the
   same reason the sign-off page's are -- this page is served with the
   plain layout to somebody who has never seen the app, and it must not
   change appearance if a token moves. */
.cview { max-width: 42rem; margin: 0 auto; padding: 1.25rem 1rem 4rem; }
.cview-head { margin-bottom: 1.5rem; }
.cview-brand { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: #7d8899; }
.cview-builder { font-weight: 600; color: #1c2333; margin-top: .15rem; }
.cview-title { font-size: 1.35rem; font-weight: 700; margin: .5rem 0 .25rem; line-height: 1.25; }
.cview-where { color: #5a6478; font-size: .9rem; }
.cview-card .card-body { padding: 1.1rem 1.15rem; }
.cview-lines-head { font-size: .75rem; letter-spacing: .06em; text-transform: uppercase; color: #7d8899; margin-bottom: .4rem; }
.cview-lines { list-style: none; padding: 0; margin: 0 0 1rem; }
.cview-lines li { padding: .5rem 0; border-bottom: 1px solid #eef0f4; display: flex; gap: .75rem; align-items: baseline; }
.cview-lines li:last-child { border-bottom: 0; }
.cview-line-d { flex: 1 1 auto; }
.cview-line-n { display: block; color: #5a6478; font-size: .85rem; }
.cview-line-v { white-space: nowrap; font-variant-numeric: tabular-nums; }
.cview-total {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  border-top: 2px solid #1c2333; padding-top: .8rem; margin-top: .4rem;
}
.cview-total-k { font-weight: 600; }
.cview-due { display: block; font-weight: 400; color: #5a6478; font-size: .85rem; }
.cview-total-v { font-size: 1.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.cview-pay { background: #f5f6fa; }
.cview-bank { display: grid; grid-template-columns: 7rem 1fr; gap: .3rem 1rem; margin: 0; }
.cview-bank dt { font-weight: 400; color: #5a6478; }
.cview-bank dd { margin: 0; font-weight: 600; }
/* --- end P28 --- */

/* --- P-FIX1 ----------------------------------------------------------------
   NS-55. "Order 4154-2656-1" beside a supplier's name on the Ledger: the
   same kind of chip as P21's "Counter", and for the same reason -- a fact
   about how the row came to exist, never a status and never an action. It
   borrows .chip-counter's shape and takes the navy edge .chip-po already
   uses for a purchase order in the bill, so an order reads as an order
   wherever it appears. */
.chip-kind.chip-fromorder {
  border-color: var(--tb-navy); color: var(--tb-navy); background: var(--tb-surface);
  font-weight: 600; vertical-align: middle; margin-left: .35rem;
}
/* --- end P-FIX1 --- */
/* --- P28b ---------------------------------------------------------------
   The retention pot on the Claims tab: what the client is holding out of
   the claims they have paid, what has been asked back, and the release
   forms that ask for it.

   IT IS A PANEL ON THE SAME TAB AND USES THE SAME FURNITURE. .tb-table,
   .clm-tot-k / .clm-tot-v and the data-label phone collapse are P28's,
   reused rather than restated: a builder should not have to learn a
   second table on the same screen. Everything here is layout and one
   piece of emphasis.

   COLOUR. None that is new. "Still held" is the member's own money and
   takes the orange INK token (never white on orange, and never orange as
   a status); a cancelled release is greyed and struck through, which is
   the same thing the Ledger does to an excluded line. Orange stays on
   the action buttons, which Bootstrap's .btn-primary already carries. */

.rtn-card .rtn-tot { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; font-variant-numeric: tabular-nums; }
.rtn-left { color: var(--tb-orange-ink); }

.rtn-table td { vertical-align: middle; }
.rtn-table .sub { display: block; }

.rtn-sub-head { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); margin-bottom: .4rem; }

.rtn-releases { padding: .85rem 1rem; border-top: 1px solid var(--tb-line); }
.rtn-rel-list { list-style: none; margin: 0; padding: 0; }
.rtn-rel-list > li { padding: .4rem 0; border-bottom: 1px dashed var(--tb-line); }
.rtn-rel-list > li:last-child { border-bottom: 0; }
.rtn-rel-head { display: flex; justify-content: space-between; gap: 1rem; font-weight: 600; }
.rtn-rel-void { color: var(--tb-faint); }
.rtn-rel-void .rtn-rel-head { text-decoration: line-through; }

.rtn-terms { padding: .85rem 1rem; border-top: 1px solid var(--tb-line); background: var(--tb-hover); }
.rtn-terms .form-label { font-size: .8rem; margin-bottom: .2rem; }

.rtn-raise { padding: .85rem 1rem; border-top: 1px solid var(--tb-line); }
.rtn-form { border: 1px solid var(--tb-line); border-radius: .5rem; padding: .75rem; height: 100%; }
.rtn-form-head { font-weight: 600; margin-bottom: .2rem; }
.rtn-form .sub { display: block; font-size: .8rem; color: var(--tb-muted); }

/* "Send again" sits under Mark paid in the actions cell. It is a link,
   not a second orange button: re-sending a copy of something is not the
   action the row is for. */
.rtn-again { padding: 0; font-size: .8rem; text-decoration: none; }
/* --- end P28b --- */
/* --- P11b --- */
/* The template register (NS-39) and the draft review queue (NS-38).
   Everything here leans on the `.tpl*` rows P10 already drew for the
   picker on an empty bill and on the `.bill-*` form grid from P11: the
   same list in two places should not be two lists. What is new is the
   action column on a register row, the read-only note, and the outline
   a draft is read through. */
.tpl-reg { align-items: flex-start; }
.tpl-reg .tpl-name a { color: inherit; text-decoration: none; }
.tpl-reg .tpl-name a:hover { text-decoration: underline; }
.tpl-acts { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; flex: 0 0 auto; }
.tpl-acts form { margin: 0; }
.tpl-ro { font-size: .74rem; color: var(--tb-faint); }
.tpl-unused { color: var(--tb-faint); font-style: italic; }
.tpl-nocat { color: var(--tb-faint); }
.tpl-crumb { font-size: .78rem; color: var(--tb-muted); margin-bottom: .1rem; }
.tpl-crumb a { color: var(--tb-muted); }

/* Read-only, and why. Ground rather than a status colour: this is not a
   warning, it is how the screen works. */
.tpl-ro-note {
  display: flex; align-items: flex-start; gap: .2rem;
  background: var(--tb-ground); font-size: .86rem; color: var(--tb-muted);
}
.tpl-ro-note strong { color: var(--tb-ink); }

/* The editor's table: no money columns, so the description takes the
   room the four of them had on the bill. */
.tpl-table td, .tpl-table th { vertical-align: middle; }
.tpl-form-grid, .tpl-group-grid { align-items: end; }
.tpl-addrow td { background: var(--tb-ground); }
.tpl-addline {
  cursor: pointer; font-size: .8rem; font-weight: 600; color: var(--tb-orange-ink);
  list-style: none; padding: .15rem 0;
}
.tpl-addline::-webkit-details-marker { display: none; }
.tpl-addline:hover { text-decoration: underline; }

/* Reading a draft. Indented plain lists, because the question being
   asked of this page is "are these the right lines", and a table of one
   column is a list wearing a table's clothes. */
.tpl-draft-read { margin-top: -.25rem; }
.tpl-draft-stage { margin-top: .6rem; }
.tpl-draft-grp { font-size: .82rem; font-weight: 600; margin: .35rem 0 .1rem; }
.tpl-draft-lines { margin: 0 0 .3rem; padding-left: 1.1rem; font-size: .82rem; color: var(--tb-muted); }
.tpl-draft-lines li { padding: .05rem 0; }
.tpl-draft-lines .sub { font-size: .76rem; color: var(--tb-faint); }

@media (max-width: 575.98px) {
  .tpl-acts { width: 100%; }
  .tpl-ro { width: 100%; }
}
/* --- end P11b --- */

/* --- P-NOTIFY -------------------------------------------------------------
   The bell in both navbars, the notifications page, and the preferences
   table.

   COLOUR. One new use of orange and no new tokens. The unread mark is
   ORANGE because an unread notification is THE MEMBER'S OWN POSITION --
   the same rule the tier marker and the active tab follow -- and it is
   never red, because nothing here is a fault. Everything else is navy
   for structure and grey for what has been dealt with; the kind chip
   reuses .chip-kind exactly as it is, grey always (P22, P-LOG).

   The bell sits in the top bar on BOTH sides. It is not a sixth tab: the
   phone tab bar is full, and a bell is a state of the app rather than a
   section of it. It inherits the navbar's own white-on-navy, so the
   supplier's teal bar needs no override. */

.navbell {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: .35rem; flex: 0 0 auto;
  color: rgba(255,255,255,.82); text-decoration: none; font-size: 1.05rem;
}
.navbell:hover, .navbell:focus-visible { color: #fff; background: rgba(255,255,255,.1); }
.navbell.has-unread { color: #fff; }

/* The count rides on the bell rather than beside it, so the control is
   one target at 32px however many digits it carries. Dark ink on the
   orange, never white -- white on #F87800 is 2.9:1. */
.navbell-n {
  position: absolute; top: -.15rem; right: -.25rem;
  min-width: 1.05rem; padding: 0 .22rem; border-radius: .6rem;
  background: var(--tb-orange); color: var(--tb-ink);
  font-size: .62rem; font-weight: 700; line-height: 1.05rem; text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 1.5px var(--tb-navy);
}
body:has(.app-navbar--supplier) .navbell-n { box-shadow: 0 0 0 1.5px var(--tb-supplier); }

/* The same number in the account menu, where the background is white. */
.notif-menu-n {
  display: inline-block; margin-left: .3rem; padding: 0 .35rem; border-radius: .6rem;
  background: var(--tb-orange); color: var(--tb-ink);
  font-size: .68rem; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* --- the list --- */
.notif-list { list-style: none; margin: 0; padding: 0; }
.notif-row {
  display: flex; align-items: flex-start; gap: .6rem;
  padding: .7rem 1rem; border-bottom: 1px solid var(--tb-line);
}
.notif-row:last-child { border-bottom: 0; }
.notif-row:hover { background: var(--tb-hover); }
.notif-row.is-unread { background: var(--tb-orange-soft); }
.notif-row.is-unread:hover { background: #ffe9d4; }

/* A dot in the gutter, so the unread state survives a screenshot, a
   colour-blind reader and the row being read out. It keeps its space
   when the row is read, which is what stops the whole list shifting
   left as things are opened. */
.notif-dot {
  width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 auto; margin-top: .45rem;
  background: transparent;
}
.notif-row.is-unread .notif-dot { background: var(--tb-orange); }

.notif-main { min-width: 0; flex: 1 1 auto; }
.notif-head { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.notif-title { font-weight: 600; color: var(--tb-ink); text-decoration: none; }
.notif-title:hover { color: var(--tb-navy); text-decoration: underline; }
.notif-body { font-size: .9rem; color: var(--tb-muted); }
.notif-meta { font-size: .76rem; color: var(--tb-faint); margin-top: .15rem; }
.notif-state-new { color: var(--tb-orange-ink); font-weight: 600; }
.notif-state-old { color: var(--tb-faint); }
.notif-state-err { color: var(--tb-crit); }

/* "Mark read" is a link and not a button: the ACTION on this row is
   opening the thing it points at, and a second orange button beside the
   title would argue with it. */
.notif-mark { flex: 0 0 auto; }
.notif-mark-btn { padding: 0; font-size: .8rem; text-decoration: none; white-space: nowrap; }

@media (max-width: 575.98px) {
  .notif-row { flex-wrap: wrap; }
  .notif-mark { width: 100%; padding-left: 1.1rem; }
  .notif-mark-btn { padding-left: 0; }
}

/* --- preferences --- */
/* A settings page, not a data table: capped width so the label and the
   control it governs sit within one glance of each other instead of at
   opposite edges of a wide screen. */
.notif-prefs-wrap { max-width: 58rem; }

/* The quiet-hours note. Navy-edged structure, not an amber warning: it
   explains how the system behaves, it is not telling anybody off. */
.notif-quiet {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .85rem 1rem; border-left: 3px solid var(--tb-navy);
  font-size: .9rem;
}
.notif-quiet .bi { color: var(--tb-navy); font-size: 1.1rem; line-height: 1.3; }

/* What the four words mean, said once above the table rather than in a
   paragraph under it that nobody reads after they have pressed Save. */
.notif-legend {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .35rem 1.5rem;
  margin: 0; padding: .85rem 1rem; border-bottom: 1px solid var(--tb-line);
  font-size: .82rem; color: var(--tb-muted);
}
.notif-legend > div { display: flex; gap: .4rem; align-items: baseline; }
.notif-legend dt { flex: 0 0 auto; font-weight: 700; color: var(--tb-ink); }
.notif-legend dd { margin: 0; }

.notif-prefs th:first-child, .notif-prefs td:first-child { padding-left: 1rem; }
.notif-prefs th:last-child,  .notif-prefs td:last-child  { padding-right: 1rem; }
.notif-prefs td { vertical-align: middle; padding-top: .55rem; padding-bottom: .55rem; }
.notif-prefs tbody tr:last-child td { border-bottom: 0; }
.notif-prefs-ch { width: 1%; white-space: nowrap; }
.notif-off { border-color: var(--tb-line-2); color: var(--tb-faint); margin-left: .35rem; }

/* Four radios that read as ONE segmented control -- the same trick the
   P-LOG kind picker uses, and for the same reason: the <input> stays in
   the DOM and keeps the keyboard and the screen reader working, it is
   moved out of sight rather than display:none. The segments touch, so
   the eye reads one control with one answer, not four buttons. */
.notif-choices { display: inline-flex; }
.notif-choice { margin: 0; }
.notif-choice input {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.notif-choice span {
  display: inline-block; cursor: pointer; user-select: none; position: relative;
  min-width: 3.6rem; text-align: center;
  padding: .3rem .7rem; border: 1px solid var(--tb-line-2);
  font-size: .8rem; color: var(--tb-muted); background: var(--tb-surface);
}
.notif-choice + .notif-choice span { margin-left: -1px; }
.notif-choice:first-child span { border-radius: .3rem 0 0 .3rem; }
.notif-choice:last-child  span { border-radius: 0 .3rem .3rem 0; }
.notif-choice span:hover { border-color: var(--tb-navy); color: var(--tb-navy); z-index: 1; }
.notif-choice input:checked + span {
  background: var(--tb-navy); border-color: var(--tb-navy); color: #fff; font-weight: 600; z-index: 2;
}
.notif-choice input:focus-visible + span { outline: 2px solid var(--tb-orange); outline-offset: 1px; z-index: 3; }

/* "off" checked is grey and not navy: switching something off is not a
   selection to be pleased about, it is the absence of one. */
.notif-choice input[value="off"]:checked + span {
  background: var(--tb-faint); border-color: var(--tb-faint);
}

/* The supplier side picks in teal, like its other chips. */
body:has(.app-navbar--supplier) .notif-quiet { border-left-color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .notif-quiet .bi { color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .notif-choice span:hover { border-color: var(--tb-supplier); color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .notif-choice input:checked + span { background: var(--tb-supplier); border-color: var(--tb-supplier); }
body:has(.app-navbar--supplier) .notif-choice input[value="off"]:checked + span { background: var(--tb-faint); border-color: var(--tb-faint); }

.notif-prefs-foot {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .85rem 1rem; border-top: 1px solid var(--tb-line);
}

/* Phone: the label on its own line, the control full-width underneath,
   each segment a thumb-sized quarter. */
@media (max-width: 575.98px) {
  .notif-legend { grid-template-columns: 1fr; }
  .notif-prefs thead { display: none; }
  .notif-prefs tr { display: block; padding: .6rem 1rem; border-bottom: 1px solid var(--tb-line); }
  .notif-prefs tbody tr:last-child { border-bottom: 0; }
  .notif-prefs td, .notif-prefs td:first-child, .notif-prefs td:last-child {
    display: block; padding: 0; border: 0; width: auto; white-space: normal; background: transparent; box-shadow: none;
  }
  .notif-prefs-ev { font-weight: 500; margin-bottom: .4rem; }
  .notif-choices { display: flex; width: 100%; }
  .notif-choice { flex: 1 1 0; }
  .notif-choice span { display: block; min-width: 0; padding: .45rem .25rem; }
  .notif-prefs-foot .btn { width: 100%; }
}
/* --- end P-NOTIFY --- */
/* --- P-FIX2 -----------------------------------------------------------------
   Two small marks on the schedule, and neither is an action.

   .sch-clash  AMBER. The trade on this task is standing on another of the
               member's jobs on an overlapping day. Amber because it is a
               status and a warning -- red is "nobody is booked", which is a
               hole, and this is not: two half-days on two sites is a normal
               Tuesday, and only the builder knows which it is. It sits inside
               .sub on both screens it appears on (the Schedule tab's Trade
               cell and the Day Sheet's "this week on site" row), so it takes
               that block's size and spacing and only restates the colour.

   .sch-delivered  GREEN. The supplier has said the material for this line
               landed. The same green every "it happened" reads in, and the
               same shape as the bill's chips so a fact about an order looks
               like a fact about an order wherever it turns up. */
.sch-clash {
  color: var(--tb-warn);
  display: block;
}
.sch-clash .bi { margin-right: .2rem; }
.queue-sub .sch-clash { display: inline-block; margin-left: .35rem; font-size: .78rem; }
.sch-delivered {
  display: inline-block;
  margin-left: .35rem;
  padding: 0 .35rem;
  border: 1px solid var(--tb-good);
  border-radius: 999px;
  background: var(--tb-surface);
  color: var(--tb-good);
  font-size: .72rem;
  font-weight: 600;
  vertical-align: middle;
  white-space: nowrap;
}
.sch-delivered .bi { margin-right: .2rem; }
/* --- end P-FIX2 --- */
/* --- P24-CSV ----------------------------------------------------------------
   Three things, one block: the files a price request carries (NS-46), the
   two NS-49 additions to the comparison panel, and the Xero download.

   Nothing here is a new colour. The attach rows borrow .req-sup's shape
   because they are the same kind of thing one card lower -- a tick box, a
   name and a fact about it -- and the cancel-the-rest card borrows the
   alert's amber because it is a STATUS (somebody is still waiting on you)
   and not an action; the one orange on it is the button, which is the
   .btn-primary the rest of the application already owns.
   -------------------------------------------------------------------------- */

.req-files .card-heading { margin-bottom: .35rem; }

.req-file {
  display: flex; align-items: flex-start; gap: .7rem;
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  padding: .55rem .8rem; margin-bottom: .4rem; cursor: pointer;
}
.req-file:hover { background: var(--tb-hover); }
.req-file-main { display: flex; flex-direction: column; min-width: 0; }
.req-file-name { font-weight: 600; word-break: break-word; }
.req-file-meta { font-size: .78rem; color: var(--tb-muted); }

/* The per-supplier line. Closed, it is one quiet row under the supplier;
   open, it is a box. A <details> rather than a permanently visible
   textarea because eight suppliers would otherwise be eight boxes
   nobody asked for. */
.req-sup-note { margin: -.2rem 0 .7rem 2.1rem; }
.req-sup-note > summary {
  font-size: .8rem; color: var(--tb-navy); cursor: pointer;
  list-style: none; padding: .15rem 0;
}
.req-sup-note > summary::-webkit-details-marker { display: none; }
.req-sup-note > summary::before { content: "+ "; color: var(--tb-faint); }
.req-sup-note[open] > summary::before { content: "\2212 "; }
.req-sup-note > textarea { margin-top: .35rem; }

/* NS-49: which lines a quote is being taken for. */
.cmp-pick-lines { margin-bottom: .5rem; }
.cmp-pick {
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .2rem 0; cursor: pointer; font-size: .9rem;
}
.cmp-pick input { margin-top: .2rem; flex: 0 0 auto; }

/* NS-49: the offer to cancel what is still out. */
.cmp-cancel {
  border-left: 3px solid var(--tb-warn);
  background: #fffaf0;
}
.cmp-cancel .card-heading { color: var(--tb-warn); }

/* The Xero download, both places it appears. */
.xero-export .form-text { max-width: 68ch; margin-top: .5rem; }
.xero-export-link { white-space: nowrap; }

@media (max-width: 575.98px) {
  .req-sup-note { margin-left: 0; }
  .xero-export-link { width: 100%; }
}
/* --- end P24-CSV --- */

/* --- P-CLIENT --- */

/* The Client panel on the People tab, and the share toggle on Files. */
.clink-card .clink-url {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8rem;
}
.clink-fresh {
  background: var(--tb-orange-soft);
  border-top: 1px solid var(--tb-line);
  border-bottom: 1px solid var(--tb-line);
}
.clink-table .clink-acts-h { width: 2.5rem; }
.clink-table .clink-acts { text-align: right; white-space: nowrap; }
.clink-dead td { color: var(--tb-faint); }
.clink-dead .row-link, .clink-dead .num { color: var(--tb-faint); }
.clink-new .clink-f { flex: 1 1 14rem; min-width: 10rem; }
.clink-new .form-text { margin-top: .5rem; }
.clink-files-note { max-width: 72ch; }

/* The toggle is a STATE, not an action, so it is navy and grey rather
   than orange -- orange is the member's own next move and this is a
   fact about a row. */
.clink-toggle {
  display: inline-flex; align-items: center; gap: .3rem;
  border: 1px solid var(--tb-line-2); background: var(--tb-surface);
  color: var(--tb-muted); border-radius: 999px;
  padding: .1rem .55rem; font-size: .78rem; line-height: 1.6;
  cursor: pointer;
}
.clink-toggle:hover { background: var(--tb-hover); border-color: var(--tb-navy); color: var(--tb-navy); }
.clink-toggle.is-on {
  background: #e8f1ff; border-color: var(--tb-navy); color: var(--tb-navy); font-weight: 600;
}

/* ------------------------------------------------------------------
   The client's own page. It reuses .cview-brand from P28 and nothing
   else: that page is one document on a phone and this one is a whole
   job, so the rhythm is sections rather than a single card.
   ------------------------------------------------------------------ */
.cjob { max-width: 44rem; margin: 0 auto; padding: 1.25rem 1rem 4rem; }
.cjob-head { margin-bottom: 1rem; }
.cjob-title { font-size: 1.5rem; font-weight: 700; margin: .35rem 0 .2rem; line-height: 1.2; }
.cjob-where { color: var(--tb-muted); font-size: .95rem; }
.cjob-builder { color: var(--tb-muted); font-size: .9rem; margin-top: .4rem; }
.cjob-intro {
  color: var(--tb-muted); font-size: .9rem; max-width: 60ch;
  border-left: 3px solid var(--tb-line-2); padding-left: .75rem; margin-bottom: 1.75rem;
}

.cjob-sec { margin-bottom: 2rem; }
.cjob-h {
  font-size: .78rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--tb-faint); font-weight: 600; margin-bottom: .6rem;
}
.cjob-empty { color: var(--tb-muted); font-size: .92rem; margin: 0; }
.cjob-now { font-size: .95rem; margin-bottom: .75rem; }
.cjob-sum { font-size: .9rem; color: var(--tb-muted); margin: .75rem 0 0; max-width: 60ch; }

/* The stage list. Green is a state and never an action, per the house
   rule, so a finished stage is --tb-good and the current one is navy. */
.cjob-stages { list-style: none; padding: 0; margin: 0; }
.cjob-stage { display: flex; gap: .6rem; align-items: baseline; padding: .4rem 0; }
.cjob-stage-mark { flex: 0 0 1.1rem; color: var(--tb-line-2); }
.cjob-stage-d { flex: 1 1 auto; }
.cjob-stage-n { display: block; color: var(--tb-muted); font-size: .84rem; }
.cjob-stage.is-complete .cjob-stage-mark { color: var(--tb-good); }
.cjob-stage.is-complete .cjob-stage-d { color: var(--tb-muted); }
.cjob-stage.is-current .cjob-stage-mark { color: var(--tb-navy); }
.cjob-stage.is-current .cjob-stage-d { font-weight: 700; }
.cjob-stage.is-upcoming .cjob-stage-d { color: var(--tb-faint); }

/* Claims and variations share a row. */
.cjob-rows { list-style: none; padding: 0; margin: 0; }
.cjob-row {
  display: flex; gap: .75rem; align-items: baseline;
  padding: .6rem 0; border-bottom: 1px solid var(--tb-line);
}
.cjob-row:last-child { border-bottom: 0; }
.cjob-row-d { flex: 1 1 auto; }
.cjob-row-n { display: block; color: var(--tb-muted); font-size: .84rem; }
.cjob-row-v { white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }
.cjob-tag {
  display: block; font-size: .72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; margin-top: .1rem;
}
.cjob-tag.is-paid  { color: var(--tb-good); }
.cjob-tag.is-owing { color: var(--tb-warn); }
.cjob-tag.is-dead  { color: var(--tb-faint); }

.cjob-files { list-style: none; padding: 0; margin: 0; }
.cjob-files li { border-bottom: 1px solid var(--tb-line); }
.cjob-files li:last-child { border-bottom: 0; }
.cjob-file {
  display: flex; gap: .6rem; align-items: baseline;
  padding: .6rem 0; text-decoration: none; color: var(--tb-navy);
}
.cjob-file:hover { color: var(--tb-navy-2); }
.cjob-file-d { flex: 1 1 auto; }
.cjob-file-n { display: block; color: var(--tb-muted); font-size: .84rem; }

.cjob-foot {
  margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--tb-line);
  color: var(--tb-muted); font-size: .88rem;
}
.cjob-foot-n { color: var(--tb-faint); font-size: .8rem; }

@media (max-width: 575.98px) {
  .cjob-row { flex-wrap: wrap; }
  .cjob-row-v { text-align: left; }
  .cjob-tag { display: inline; margin-left: .4rem; }
  .clink-new form { flex-direction: column; align-items: stretch; }
  .clink-new .btn { width: 100%; }
}
/* --- end P-CLIENT --- */
/* --- P-MEMBERSHIP -----------------------------------------------------------
   NS-29's grace period and NS-18's tier badge.

   TWO IDEAS, AND THEY USE DIFFERENT COLOUR FAMILIES ON PURPOSE.

   The grace period is a STATE, so it is amber -- the same amber the
   lapsed card and the pwned-password banner already use. Red/amber/green
   are status on this system and never actions. Grace is told apart from
   lapsed by the WORD and by the ICON, never by the shade: nobody can
   tell two ambers apart, and one man in twelve cannot tell amber from
   green at all.

   The tier badge is NOT a status and must not borrow those three. It is
   three MUTED METALLICS -- a warm brown, a cool grey and a soft ochre --
   each one desaturated enough to sit beside orange without arguing with
   it. Orange #F87800 is the action colour and the member's own position
   on the tier bar; a gold chip that shouted would read as a button. The
   ink in each chip is dark enough on its own tint to pass AA, because
   these appear at .74rem on a phone held at arm's length across a trade
   counter.

   A tier the reference list grows later gets `--plain`, which is the
   muted grey every other neutral chip on this system uses -- a new tier
   renders as a chip rather than as nothing. */

.tier-badge {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .78rem; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; white-space: nowrap;
  padding: .2rem .55rem; border-radius: 999px;
  border: 1px solid transparent;
}
.tier-badge.is-small { font-size: .7rem; padding: .12rem .45rem; }
.tier-badge i { font-size: .8em; opacity: .75; }

.tier-badge--bronze { background: #f7efe5; border-color: #ddc7a9; color: #7d5a2e; }
.tier-badge--silver { background: #eef1f5; border-color: #c9d1dc; color: #56606d; }
.tier-badge--gold   { background: #fbf4dd; border-color: #e3d2a0; color: #7d6416; }
.tier-badge--plain  { background: var(--tb-sunk); border-color: var(--tb-line-2); color: var(--tb-muted); }

/* On the member's own card the chip sits on navy, so it keeps its light
   fill and loses the border, which would read as a seam against the
   gradient. The card's own orange band is still the brand mark. */
.mcard-tier { background: none; padding: 0; }
.mcard-tier .tier-badge { border-color: transparent; }

/* --- the grace period -------------------------------------------------------
   The card, within grace: the date goes amber like a lapsed one, but the
   BAND stays orange. The band is the brand mark; recolouring it would
   say "this card has stopped", and it has not. */
.mcard.is-grace .mcard-date { color: #ffcf6b; }
.mcard-warn.is-grace { border-left-color: #b07d16; }
.mcard-warn.is-grace i { color: #b07d16; }

/* The public verify page's fourth verdict. Amber, like lapsed -- see the
   header. The tick in the icon and the words "renewal due" are what tell
   them apart, and the verdict wraps rather than shrinking, because it is
   read at arm's length. */
.vcheck--grace  { border-top-color: var(--tb-warn); }
.vcheck--grace  .vcheck-verdict { color: var(--tb-warn); }
.vcheck--grace  .vcheck-verdict span { font-size: .78em; line-height: 1.15; }

/* The counter's chip. Same amber as LAPSED, different word. */
.scan-state.is-grace { border-color: var(--tb-warn); color: var(--tb-warn); background: #fdf5e3; }

/* --- the amber bar in the member chrome -------------------------------------
   Above the flashes, below the breach banner. The same construction as
   .pwned-banner, which is the one other thing on this system that is
   neither a flash nor page content -- so the two stack without arguing
   about what a full-width amber strip means. */
.renew-banner {
  display: flex; align-items: flex-start; gap: .6rem; flex-wrap: wrap;
  background: #fff8e6; border: 1px solid #f3e2b8; border-left: 3px solid var(--tb-warn);
  border-radius: var(--tb-radius); padding: .7rem .9rem; margin-bottom: 1rem;
  font-size: .92rem;
}
.renew-banner > i { color: var(--tb-warn); margin-top: .15rem; font-size: 1.05rem; }
.renew-banner-t { flex: 1 1 18rem; min-width: 0; }
.renew-banner-s { color: var(--tb-muted); font-size: .85rem; margin-top: .15rem; }
.renew-banner-f { margin: 0; flex: none; }
.renew-banner-x { margin: 0; flex: none; }
.renew-banner-x .btn-close { font-size: .7rem; opacity: .5; }
.renew-banner-x .btn-close:hover { opacity: .9; }

/* "Renewal requested 18/09/2026" -- what replaces the button for seven
   days. GREEN, and not a disabled button: a greyed-out button is
   something to keep pressing, and a sentence saying it is done is not. */
.renew-done {
  display: inline-flex; align-items: center; gap: .35rem; flex: none;
  color: var(--tb-good); font-weight: 600; font-size: .88rem; white-space: nowrap;
}

/* The sentence above the Renew button on the account page. */
.renew-note { font-size: .88rem; color: var(--tb-muted); margin-bottom: .75rem; }
.renew-note.is-grace  { color: var(--tb-warn); }
.renew-note.is-lapsed { color: var(--tb-warn); }

/* NS-18. What is left on the Ledger where the three benefit columns
   were: one line, the tracked-spend sentence and the free-membership
   rule, which is the only one of those benefits that was ever real. */
.tier-note { font-size: .88rem; color: var(--tb-muted); margin: -.25rem 0 1rem; }
.tier-note strong { color: var(--tb-ink); }

@media (max-width: 575.98px) {
  /* The button goes full width under the words rather than being
     squeezed beside them -- it is the one thing on the bar to press. */
  .renew-banner-f { flex: 1 1 100%; }
  .renew-banner-f .btn { width: 100%; }
  .renew-banner-x { order: -1; margin-left: auto; }
}
/* --- end P-MEMBERSHIP --- */

/* --- P-SCOREMIX --- */
/* NS-47: every comparison on the scorecard now says WHAT IT IS AGAINST,
   and a comparison with fewer than `scorecard.min_group` suppliers
   behind it is replaced by the sentence saying so. Both are the same
   slot on the tile -- one line, under the figures, in the same place on
   every window of every measure -- because a reader has to be able to
   find "what am I being compared with" without hunting for it, and
   because the absence of a comparison has to be as visible as its
   presence. A blank where the line normally sits reads as a page that
   failed to load. */
.sc-vs {
  display: block; margin-top: .25rem;
  font-size: .7rem; line-height: 1.3;
  color: var(--tb-muted);
  letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
}

/* The not-enough line. Fainter and italic: it is an absence, and it must
   not be mistaken at a glance for a figure. Never red -- there is
   nothing wrong with being the only supplier in your trade, and red on
   this card means a measure that needs attention. */
.sc-vs-thin { color: var(--tb-faint); font-style: italic; }

/* The within-trade line sits under the network one on the win rate, so
   it gets a little air and a rule to say it is a second thing and not a
   continuation of the first. */
.sc-vs-trade {
  margin-top: .3rem; padding-top: .3rem;
  border-top: 1px solid var(--tb-line);
}

@media (max-width: 575.98px) {
  /* Stacked windows are already narrow; the comparison line wraps to two
     lines there and needs the room between it and the next window. */
  .sc-vs { margin-top: .3rem; }
}
/* --- end P-SCOREMIX --- */
/* --- P-GST -----------------------------------------------------------------
   GST, NS-64. Every figure this system stores is ex-GST; the documents
   that leave it -- a client's claim statement, a variation's sign-off
   page, a supplier's purchase order -- add GST as a line.

   THIS BLOCK ADDS NO COLOUR AND NO NEW TOKEN. A GST line is not a
   status, so it never takes red, amber or green; it is not an action, so
   it never takes orange. It is working: the arithmetic under the figure
   that matters, in the muted ink the rest of the application already
   uses for a subtitle. Four rules, two of which are one word wide.
   ------------------------------------------------------------------------ */

/* The subtotal / GST rows above a big total -- on the client's claim
   statement and the variation sign-off page, both of which sit on
   layout/plain and carry their own light palette rather than the app
   tokens (see .cview-* and .vsign-* above, which hard-code the same
   greys). Same row shape as .cview-total, one step down in weight, so
   the eye runs past them to the figure it is looking for. */
.gst-sub {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  font-size: .92rem; color: #5a6478; padding: .15rem 0;
}
.gst-sub:first-of-type { border-top: 1px solid #e3e6ee; padding-top: .6rem; margin-top: .5rem; }
.gst-sub-v { font-variant-numeric: tabular-nums; }

/* "ex $48,640.00 · GST $4,864.00" under an inc-GST figure, inside a
   table cell or a definition list. Reuses .sub's meaning -- a fact about
   the line above -- and only adds the tabular figures, because these are
   numbers a member reads down a column. */
.gst-work {
  display: block; font-weight: 400; font-size: .8rem; color: var(--tb-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* "All figures ex GST", once per screen. Deliberately the quietest thing
   on the page: it is a caption for forty numbers, not a warning about
   one, and a member should read it the first time and never again. */
.gst-page-note {
  font-size: .82rem; color: var(--tb-muted); margin: -.25rem 0 .75rem;
}
.gst-page-note.is-inline { display: inline; margin: 0; }
.gst-page-note strong, .gst-page-note em { color: var(--tb-ink); }

/* A two-line column heading -- "Total / inc GST" -- so the Claims tab
   can say which side of GST its money column is on without a wider
   table. */
.clm-th-sub { display: block; font-weight: 400; font-size: .75rem; color: var(--tb-muted); }

/* The inc-GST row of a purchase order's footer is the one a supplier
   checks against their invoice, so it carries the rule the plain total
   used to. */
.po-total-inc th { border-top: 2px solid var(--tb-line); }
/* --- end P-GST --- */

/* --- P-TAXINV --- */
/* The compliant header on the claim statement (/c/{token}) and the two
   lines the account page shows of it. Tokens and greys as .cview-*
   above, one step quieter: this is a letterhead, not the figure the
   client is reading for. */
.taxinv-word {
  font-size: .8rem; font-weight: 700; letter-spacing: .14em;
  color: var(--tb-navy, #1c2333); margin: .35rem 0 .6rem;
}
.taxinv-word-sub { font-weight: 400; letter-spacing: .04em; color: #5a6478; margin-left: .4rem; }
.taxinv-abn { color: #1c2333; font-size: .9rem; }
.taxinv-line { display: block; color: #5a6478; font-size: .85rem; }
/* Invoice / date of issue / to, in the same two-column grid the bank
   details use, so the two cards read as one document. */
.taxinv-meta { display: grid; grid-template-columns: 9rem 1fr; gap: .35rem 1rem; margin: 0; }
.taxinv-meta dt { font-weight: 400; color: #5a6478; }
.taxinv-meta dd { margin: 0; font-weight: 600; }
/* The ATO's words, under the total they are about. */
.taxinv-incgst {
  margin-top: .5rem; text-align: right; color: #5a6478; font-size: .85rem;
}
/* /account/business: what the claim header will say, read-only. */
.taxinv-head-note { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--tb-line, #eef0f4); }
.taxinv-head-dl { display: grid; grid-template-columns: 7rem 1fr; gap: .3rem 1rem; margin: .4rem 0 0; }
.taxinv-head-dl dt { font-weight: 400; color: #5a6478; }
.taxinv-head-dl dd { margin: 0; }
@media (max-width: 420px) {
  .taxinv-meta, .taxinv-head-dl { grid-template-columns: 1fr; gap: 0 0; }
  .taxinv-meta dd, .taxinv-head-dl dd { margin-bottom: .45rem; }
}
/* --- end P-TAXINV --- */

/* --- P-RENEW --- */
/* A member paying their own renewal, on pages they reach with NO LOGIN:
   the chrome-less layout/plain, a phone in a ute, often at night. Every
   rule below is for those five pages and the two buttons that lead to
   them; nothing here restyles anything that existed before. */

/* The Renew button that used to be a submit and is now a link. Sized
   like the form it replaced so the amber bar does not shift, and full
   width on a phone where it is the thing being aimed at. */
.renew-go { flex: none; }
@media (max-width: 575.98px) {
  .renew-go { width: 100%; }
}

/* The page itself. Narrow, centred and capped: this is one decision with
   one number on it, and a wide column would make it look like a form to
   fill in. */
.renew-wrap {
  max-width: 34rem; margin: 0 auto; padding: 1.5rem 1rem 3rem;
}
.renew-card { padding: 1.35rem 1.4rem; }
.renew-biz {
  font-weight: 600; color: var(--tb-ink); margin-bottom: 1rem;
}
.renew-biz .sub { font-weight: 400; color: var(--tb-muted); font-size: .85rem; }
.renew-kv { margin-bottom: 1rem; }

/* The three money lines. Tabular figures so the cents line up under each
   other -- a fee table where the decimal points wander is the one a
   bookkeeper does not trust -- and a rule above the total, which is the
   number that will appear on the statement. */
.renew-fee {
  width: 100%; margin: 0 0 1.1rem; font-size: .95rem;
  font-variant-numeric: tabular-nums;
}
.renew-fee th {
  text-align: left; font-weight: 400; color: var(--tb-muted);
  padding: .3rem 0;
}
.renew-fee td { text-align: right; padding: .3rem 0; white-space: nowrap; }
.renew-fee-total th, .renew-fee-total td {
  border-top: 1px solid var(--tb-line); padding-top: .5rem;
  font-weight: 700; color: var(--tb-ink); font-size: 1.05rem;
}
.renew-fee-total th { color: var(--tb-ink); }
.renew-fee-total .sub { font-weight: 400; font-size: .8rem; color: var(--tb-muted); }

/* Two ways to pay, side by side on a desktop and stacked on a phone.
   Card is the orange action; transfer is an outline BESIDE it, not a
   lesser thing said in grey -- the office is happy with either. */
.renew-actions { display: flex; flex-wrap: wrap; gap: .6rem; }
.renew-actions form { margin: 0; flex: 1 1 13rem; }
.renew-actions .btn { width: 100%; }

/* The transfer reference. The largest thing on the deposit page, in a
   box of its own, because a transfer that arrives without it costs
   somebody an afternoon of matching. Monospace and letter-spaced so it
   can be copied by eye off a phone onto a banking app. */
.renew-ref {
  border: 1px dashed var(--tb-line); border-radius: var(--tb-radius);
  background: var(--tb-sunk); padding: .7rem .9rem; margin-bottom: 1rem;
}
.renew-ref-l {
  display: block; font-size: .78rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--tb-muted); margin-bottom: .15rem;
}
.renew-ref-v {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.15rem; letter-spacing: .04em; color: var(--tb-ink);
  word-break: break-all;
}

/* The verdict on the way home. A word as well as a colour -- one man in
   twelve cannot tell the green from the amber -- the same rule the
   membership card's verify page follows. */
.renew-verdict {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  font-size: 1.35rem; font-weight: 700; color: var(--tb-good);
  margin-bottom: .75rem;
}
.renew-verdict i { font-size: 1.5rem; }
.renew-verdict--quiet { color: var(--tb-muted); }

/* And the answer to the question they came with: the new expiry date. */
.renew-newexp {
  text-align: center; border: 1px solid var(--tb-line);
  border-radius: var(--tb-radius); background: var(--tb-sunk);
  padding: .8rem; margin-bottom: 1rem;
}
.renew-newexp-l {
  display: block; font-size: .8rem; color: var(--tb-muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.renew-newexp-v { font-size: 1.5rem; color: var(--tb-ink); }
/* --- end P-RENEW --- */

/* --- P-STAFF-1 ---
   The staff area.

   ONE RULE DECIDES EVERY COLOUR HERE, and it is the one at the top of
   this file: navy is STRUCTURE, orange is ACTION and the member's own
   position, red/amber/green are STATUS and never actions. The staff
   area is the system looking at itself, so its bar is navy with NO
   orange mark -- the member bar is navy plus orange, the supplier bar
   is teal, and the one with no customer colour in it is the one that
   can change everybody's records. Somebody with three tabs open can
   tell them apart without reading the tab.

   Nothing below restyles a shared class outright. Where a staff screen
   needs a shared element to sit differently (.section-card has no
   padding of its own, because the member templates add Bootstrap's),
   the rule is scoped under `body:has(.app-navbar--staff)` so the member
   and supplier pages are untouched. */

.app-navbar--staff { background: var(--tb-navy); }
.brand-tag--staff {
  background: rgba(255,255,255,.16); color: #fff;
  border: 1px solid rgba(255,255,255,.3);
}

/* The staff templates use .section-card bare. Padding here rather than a
   p-3 on forty elements, and scoped so nothing else moves. */
body:has(.app-navbar--staff) .section-card { padding: 1rem 1.15rem; }
body:has(.app-navbar--staff) .entity-bar { background: var(--tb-navy); }
body:has(.app-navbar--staff) .login-side { background: var(--tb-navy); }

.page-head { margin-bottom: 1rem; }
.page-head h1 { font-size: 1.45rem; font-weight: 700; margin: 0; }
.page-sub { color: var(--tb-muted); font-size: .9rem; margin: .2rem 0 0; }

.section-title {
  font-size: .95rem; font-weight: 700; margin: 0 0 .6rem;
  display: flex; align-items: center; gap: .45rem;
}
.form-subhead {
  font-size: .78rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--tb-muted); margin: .4rem 0 0;
}
.empty-line { color: var(--tb-muted); font-size: .92rem; margin: 0; }
.table-wrap { overflow-x: auto; }

/* Two columns at desk width, one on a phone. Two and not four: these are
   labelled fields somebody reads down, and a four-column form of thirty
   boxes is a wall. */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .65rem .9rem; }
.form-grid > .span-2 { grid-column: 1 / -1; }
@media (max-width: 767.98px) { .form-grid { grid-template-columns: 1fr; } }

/* --- the desk ------------------------------------------------------- */
.staff-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: .75rem; }
.staff-tile {
  background: var(--tb-surface); border-radius: var(--tb-radius); box-shadow: var(--tb-shadow);
  padding: .85rem 1rem; text-decoration: none; color: inherit; display: block;
  border-left: 3px solid var(--tb-navy);
}
.staff-tile:hover { background: var(--tb-hover); }
.staff-tile-n { display: block; font-size: 1.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.staff-tile-l { display: block; font-size: .82rem; color: var(--tb-muted); }

/* The quick search. The result list is positioned over the page rather
   than pushing it down: a list that moves the form while somebody is
   typing into it is how a click lands on the wrong row. */
.staff-quick-wrap { position: relative; }
.staff-quick-results {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + .2rem);
  margin: 0; padding: .25rem; list-style: none; max-height: 22rem; overflow-y: auto;
  background: var(--tb-surface); border: 1px solid var(--tb-line-2);
  border-radius: var(--tb-radius); box-shadow: 0 6px 20px rgba(16,24,40,.14);
}
.staff-quick-hit {
  display: flex; align-items: baseline; gap: .55rem; padding: .35rem .55rem;
  border-radius: .3rem; text-decoration: none; color: inherit;
}
.staff-quick-hit:hover { background: var(--tb-hover); }
.staff-quick-hit.is-inactive { color: var(--tb-muted); }
.staff-quick-kind {
  flex: 0 0 auto; font-size: .68rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--tb-muted); min-width: 4.4rem;
}
.staff-quick-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.staff-quick-no { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--tb-muted); font-size: .84rem; }

/* --- the record summary strip --------------------------------------- */
.staff-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .75rem; }
.staff-stat {
  background: var(--tb-surface); border-radius: var(--tb-radius); box-shadow: var(--tb-shadow);
  padding: .7rem .9rem;
}
.staff-stat-l { display: block; font-size: .74rem; font-weight: 700; letter-spacing: .04em;
                text-transform: uppercase; color: var(--tb-muted); }
.staff-stat-n { display: block; font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.staff-stat-s { display: block; font-size: .78rem; color: var(--tb-muted); }

/* --- filters -------------------------------------------------------- */
.staff-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: .6rem .8rem; align-items: end; }
.staff-filters .grow { grid-column: span 2; }
.staff-filters-actions { grid-column: span 2; align-self: end; }
@media (max-width: 575.98px) {
  .staff-filters { grid-template-columns: 1fr; }
  .staff-filters .grow, .staff-filters-actions { grid-column: span 1; }
}

.staff-table th a { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--tb-line-2); }
.staff-pager { display: flex; align-items: center; gap: .75rem; margin-top: .75rem; }

/* --- status pills ----------------------------------------------------
   STATUS COLOURS, so red / amber / green and never orange. `active` is
   green because it is the healthy state; `inactive` is red because the
   business is gone; `prospective`, `limbo`, `subcontracting` and
   `retired` are amber or grey -- they are neither, and colouring them
   red would make a third of the supplier list look broken. */
.pill {
  display: inline-block; font-size: .72rem; font-weight: 600; line-height: 1.5;
  padding: .1rem .5rem; border-radius: 1rem; border: 1px solid transparent;
}
.pill--active         { background: #e7f5ee; color: var(--tb-good); border-color: #bfe3d0; }
.pill--inactive       { background: #fdeceb; color: var(--tb-crit); border-color: #f5c9c6; }
.pill--prospective,
.pill--limbo,
.pill--subcontracting { background: #fdf4e0; color: var(--tb-warn); border-color: #ecd9a8; }
.pill--retired,
.pill--soft           { background: var(--tb-sunk); color: var(--tb-muted); border-color: var(--tb-line-2); }

/* --- contacts ------------------------------------------------------- */
.contact-row {
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  padding: .75rem .85rem; margin-bottom: .6rem;
}
.contact-row.is-off { background: var(--tb-sunk); }
.contact-row-foot { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: center; }

.cat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .25rem .75rem; }

/* --- saving --------------------------------------------------------- */
.staff-save-bar {
  position: sticky; bottom: 0; z-index: 20;
  display: flex; align-items: center; gap: .85rem; flex-wrap: wrap;
  background: var(--tb-surface); border-top: 1px solid var(--tb-line);
  border-radius: var(--tb-radius); box-shadow: 0 -2px 10px rgba(16,24,40,.06);
  padding: .7rem 1rem;
  padding-bottom: calc(.7rem + env(safe-area-inset-bottom, 0px));
}

/* The status box is bordered in the crit colour because of what it does
   -- it signs every person at a supplier out -- and the BUTTON inside it
   is an outline-danger for the same reason. That is the one place in
   this application where red is on a control, and it is deliberate: the
   colour is describing the consequence, which is a status. */
.status-card { border-left: 3px solid var(--tb-crit); }
.status-form { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
.status-form .form-control { max-width: 26rem; }

/* --- the activity list ---------------------------------------------- */
.staff-log { list-style: none; margin: 0; padding: 0; font-size: .88rem; }
.staff-log li {
  display: flex; gap: .6rem; padding: .3rem 0;
  border-bottom: 1px solid var(--tb-line); flex-wrap: wrap;
}
.staff-log li:last-child { border-bottom: 0; }
.staff-log-when { flex: 0 0 auto; color: var(--tb-muted); font-variant-numeric: tabular-nums; min-width: 8.5rem; }
.staff-log-who  { flex: 0 0 auto; font-weight: 600; min-width: 8rem; }
.staff-log-what { flex: 1 1 14rem; min-width: 0; }

/* --- the sign-in page ----------------------------------------------- */
.plain-main--narrow { grid-template-columns: 1fr; place-items: center; padding: 2rem 1rem; }
.plain-main--narrow .login-form-wrap { width: 100%; max-width: 27rem; }
/* --- end P-STAFF-1 --- */

/* --- P-STAFF-2 -------------------------------------------------------
   Issues and the escalation queue, the chase desk, the activity log,
   the reports, and the register's small screens.

   EXISTING TOKENS ONLY. Nothing here introduces a colour: --tb-navy is
   structure, --tb-crit / --tb-warn / --tb-good are status and never
   actions, and orange stays what app.css says it is -- the action colour
   and the member's own position -- so it appears on no staff screen.

   The one thing this block has that no earlier one needed is a visible
   difference between an INTERNAL note and a SHARED one. That is the
   fence between what stays inside TradeBuilders and what a builder reads
   on their own log page, and a staff member writing a reply has to be
   able to see at a glance which of their previous notes went out. So it
   is a left border and a word, on every row, in both directions -- not a
   hover, not an icon, and not colour alone. --tb-good on shared because
   an answer reaching the member is the healthy state; the muted ground
   on internal because it is the quiet one. */

/* --- the issue queue ------------------------------------------------- */
.issue-row.is-overdue td:first-child { box-shadow: inset 3px 0 0 var(--tb-crit); }

.issue-flag {
  display: inline-block; margin-left: .35rem;
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: .05rem .4rem; border-radius: 1rem;
  background: var(--tb-sunk); color: var(--tb-muted); border: 1px solid var(--tb-line-2);
}
.issue-p1 {
  display: inline-block; margin-left: .35rem;
  font-size: .68rem; font-weight: 700;
  padding: .05rem .4rem; border-radius: 1rem;
  background: #fdeceb; color: var(--tb-crit); border: 1px solid #f5c9c6;
}

/* --- the timeline, and the fence ------------------------------------- */
.issue-timeline { list-style: none; margin: 0; padding: 0; }
.issue-event {
  border-left: 3px solid var(--tb-line-2);
  padding: .55rem .75rem; margin-bottom: .55rem;
  background: var(--tb-sunk); border-radius: 0 var(--tb-radius) var(--tb-radius) 0;
}
.issue-event--shared { border-left-color: var(--tb-good); background: var(--tb-surface); }
.issue-event-head { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; margin-bottom: .2rem; }

.issue-vis {
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: .05rem .4rem; border-radius: 1rem; border: 1px solid var(--tb-line-2);
  color: var(--tb-muted); background: var(--tb-surface);
}
.issue-vis--shared { color: var(--tb-good); border-color: #bfe3d0; background: #e7f5ee; }

/* --- two columns where a screen has a record and its actions --------- */
.staff-cols { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 1rem; align-items: start; }
@media (max-width: 62rem) { .staff-cols { grid-template-columns: 1fr; } }

.staff-dl { display: grid; grid-template-columns: auto 1fr; gap: .2rem .75rem; margin: 0; font-size: .9rem; }
.staff-dl dt { color: var(--tb-muted); font-weight: 500; }
.staff-dl dd { margin: 0; }

.plain-list { list-style: none; margin: 0 0 .5rem; padding: 0; font-size: .9rem; }
.plain-list li { padding: .15rem 0; }

/* Text somebody typed, with their line breaks kept. */
.preserve { white-space: pre-wrap; overflow-wrap: anywhere; margin: 0; }

/* A row that is off, retired, a draft or already dealt with. */
.is-muted, .is-muted a { color: var(--tb-muted); }

/* --- the chase desk -------------------------------------------------- */
.chase-body {
  white-space: pre-wrap; overflow-wrap: anywhere; margin: 0;
  font-size: .84rem; line-height: 1.5;
  background: var(--tb-sunk); border: 1px solid var(--tb-line);
  border-radius: var(--tb-radius); padding: .7rem .85rem;
}
.chase-actions { border-top: 1px solid var(--tb-line); margin-top: .75rem; padding-top: .75rem; }

/* --- the network-gaps file ------------------------------------------
   Rendered as PLAIN TEXT. The report is Markdown and turning it into
   HTML would mean a parser this application does not have, or a
   hand-rolled one that would one day render something the report did not
   say. A monospaced block is what the file is. */
.report-md {
  white-space: pre-wrap; overflow-wrap: anywhere; margin: 0;
  font-size: .82rem; line-height: 1.55;
}

/* --- the SQL result grid --------------------------------------------- */
.sql-result { font-size: .82rem; }
.sql-result td { max-width: 24rem; overflow-wrap: anywhere; }
.form-control.code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .84rem; }

/* --- the template register ------------------------------------------- */
.template-group td { font-weight: 600; background: var(--tb-sunk); }
.template-line-form { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.template-line-form .grow { flex: 1 1 16rem; min-width: 0; }
.template-line-form .form-select { flex: 0 1 12rem; }
/* --- end P-STAFF-2 --- */

/* --- review sign-in hints (Support/ReviewLogins) ---------------------
   Only rendered for a browser carrying the review cookie. Amber, not the
   orange action colour: it is a note about the page, not a thing to do. */
.review-logins { border: 1px dashed #d9a84a; background: #fff8e8; border-radius: .5rem; padding: .6rem .75rem; margin-bottom: 1rem; }
.review-head { display: flex; align-items: center; gap: .5rem; font-size: .78rem; color: #6b4e0a; margin-bottom: .45rem; }
.review-tag { font-size: .62rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; background: #d9a84a; color: #1b1b1b; border-radius: 3px; padding: .1em .45em; }
.review-row { display: block; width: 100%; text-align: left; background: #fff; border: 1px solid #ecd9a8; border-radius: .35rem; padding: .45rem .6rem; margin-top: .35rem; cursor: pointer; font: inherit; color: inherit; }
.review-row:hover, .review-row:focus-visible { border-color: #d9a84a; outline: none; box-shadow: 0 0 0 2px rgba(217,168,74,.35); }
.review-label { display: block; font-weight: 600; font-size: .82rem; }
.review-cred { display: block; font-size: .78rem; margin-top: .1rem; }
.review-cred code { background: #f4efe2; padding: .05em .3em; border-radius: 3px; }
.review-note { display: block; font-size: .72rem; color: #6c757d; margin-top: .15rem; }

/* --- P-CAT: the supplier catalogue -----------------------------------
   Navy for structure (the tree, the crumbs), orange ink for the price
   because the price is the figure the member came for, and red is not
   used at all: nothing on these screens is a status. A product with no
   current price says so in muted grey, not in red -- it is the normal
   state of most of this catalogue, not an error. */
.cat-search { margin-bottom: 1rem; max-width: 34rem; }

.cat-crumbs { font-size: .78rem; color: var(--tb-muted); margin-bottom: .5rem; }
.cat-crumbs a { color: var(--tb-muted); text-decoration: none; }
.cat-crumbs a:hover { color: var(--tb-navy); text-decoration: underline; }
.cat-crumbs span { margin: 0 .35rem; color: var(--tb-faint); }
.cat-crumbs span[aria-current] { margin: 0; color: var(--tb-ink); font-weight: 600; }

.cat-tree { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 1.5rem; }
@media (max-width: 575.98px) { .cat-tree { columns: 1; } }
.cat-tree-item { break-inside: avoid; padding: .18rem 0; font-size: .875rem; }
.cat-tree-item a { text-decoration: none; color: var(--tb-ink); }
.cat-tree-item a:hover { color: var(--tb-navy); text-decoration: underline; }
.cat-depth-0 { font-weight: 600; margin-top: .5rem; }
.cat-depth-0:first-child { margin-top: 0; }
.cat-depth-1 { padding-left: .9rem; }
.cat-depth-1 a { color: var(--tb-muted); }

.cat-suppliers { list-style: none; margin: 0; padding: 0; max-height: 26rem; overflow-y: auto; }
.cat-suppliers li {
  display: flex; align-items: baseline; justify-content: space-between; gap: .5rem;
  padding: .22rem 0; font-size: .875rem; border-bottom: 1px solid var(--tb-line);
}
.cat-suppliers li:last-child { border-bottom: 0; }
.cat-suppliers a { text-decoration: none; color: var(--tb-ink); }
.cat-suppliers a:hover { color: var(--tb-navy); text-decoration: underline; }

.cat-count {
  font-size: .7rem; color: var(--tb-muted); background: var(--tb-sunk);
  border-radius: 999px; padding: .05em .5em; font-variant-numeric: tabular-nums;
}

.cat-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem; }
.cat-chip {
  display: inline-flex; align-items: center; gap: .35rem;
  border: 1px solid var(--tb-line-2); border-radius: 999px;
  padding: .2rem .7rem; font-size: .8rem; text-decoration: none; color: var(--tb-ink);
}
.cat-chip:hover { border-color: var(--tb-navy); color: var(--tb-navy); background: var(--tb-hover); }

.cat-sku { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .78rem; color: var(--tb-muted); }
.cat-desc { font-size: .85rem; color: var(--tb-muted); margin: .5rem 0 0; max-width: 62ch; }

.cat-price { font-weight: 700; color: var(--tb-orange-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cat-unit  { font-size: .78rem; color: var(--tb-muted); }
.cat-until { display: block; font-size: .7rem; color: var(--tb-faint); }
.cat-noprice { font-size: .82rem; color: var(--tb-faint); font-style: italic; }
.cat-upcoming { display: block; font-size: .7rem; color: var(--tb-navy); }

.cat-price-box { flex: 0 0 auto; text-align: right; font-size: 1.1rem; }
.cat-price-box .cat-price { font-size: 1.35rem; }

.cat-note { font-size: .78rem; color: var(--tb-muted); margin-top: .9rem; }

.cat-specs { display: grid; grid-template-columns: minmax(8rem, 14rem) 1fr; gap: .25rem .9rem; margin: 0; font-size: .85rem; }
.cat-specs dt { font-weight: 600; color: var(--tb-muted); }
.cat-specs dd { margin: 0; }
@media (max-width: 575.98px) { .cat-specs { grid-template-columns: 1fr; } .cat-specs dd { margin-bottom: .4rem; } }

.cat-pager { display: flex; align-items: center; gap: .75rem; margin-top: .9rem; }
/* --- end P-CAT --- */

/* --- P-QUOTE-1: the request record ------------------------------------
   The Requests list at the top of the Leads tab, and the request page.
   NOTHING HERE IS ORANGE except by inheriting .btn-primary on the one
   action the page offers ("Compare the quotes"): a request's type and
   its status are facts about the record, not things to press. The type
   pill is navy ink on a tinted ground for a quote and plain grey for a
   question -- two kinds of record, not two levels of alarm, so neither
   is red, amber or green. */
.rq-bar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }

.rq-pill {
  display: inline-block; border-radius: 999px; padding: .05em .6em;
  font-size: .7rem; font-weight: 600; letter-spacing: .01em;
  border: 1px solid var(--tb-line-2); color: var(--tb-muted); background: var(--tb-sunk);
}
.rq-pill-quote { color: var(--tb-navy); border-color: var(--tb-navy); background: transparent; }

.rq-counts {
  display: flex; flex-wrap: wrap; gap: 1.25rem;
  font-size: .85rem; color: var(--tb-muted); font-variant-numeric: tabular-nums;
}
.rq-counts strong { color: var(--tb-ink); font-size: 1.05rem; }

.rq-counts-cell { font-size: .82rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.rq-go-h { width: 6.5rem; }
.rq-go-c { text-align: right; white-space: nowrap; }

.rq-message { white-space: normal; max-width: 70ch; margin-bottom: 0; }
/* --- end P-QUOTE-1 --- */
/* =====================================================================
   P-QUOTE-2 -- tasks: the staff register and editor, and the two small
   things a member sees (the kind pill, and a task's trades standing in
   for a line's own).

   COLOUR. Nothing here is orange. Orange is the action colour and the
   member's own position, and a kind pill is neither -- it is a label on
   somebody else's fact. `product` and `trade` are navy-on-sunk, the same
   weight as a unit or a category; `quote` gets no pill on a member's
   line at all, because it is what every line already is and a pill on
   all of them says nothing. Red/amber/green stay out: a kind is not a
   status.
   ===================================================================== */

.task-kind {
  display: inline-block;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  padding: .1rem .4rem;
  border-radius: .25rem;
  background: var(--tb-sunk);
  color: var(--tb-navy);
  white-space: nowrap;
}
.task-kind--composite { background: var(--tb-navy); color: #fff; }

.task-tid { font-size: .7rem; color: var(--tb-faint); font-variant-numeric: tabular-nums; margin-left: .35rem; }

.task-chips { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; margin: 0 0 .6rem; padding: 0; }
.task-chip {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .82rem;
  padding: .15rem .3rem .15rem .55rem;
  border: 1px solid var(--tb-line);
  border-radius: 1rem;
  background: var(--tb-sunk);
}
.task-chip form { display: inline; margin: 0; }
.task-chip-x {
  border: 0;
  background: none;
  color: var(--tb-muted);
  font-size: 1rem;
  line-height: 1;
  padding: 0 .25rem;
  cursor: pointer;
}
.task-chip-x:hover { color: var(--tb-crit); }

.task-hits { list-style: none; margin: .6rem 0 0; padding: 0; font-size: .9rem; }
.task-hits li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--tb-line);
}
.task-hits li:last-child { border-bottom: 0; }
.task-hits form { margin: 0; }
.task-qty { max-width: 5.5rem; }

/* The member's side: the task's trades where the line has none of its
   own. Faint, because it is an inherited answer and not one the member
   typed -- and it must not read as the line's own category. */
.task-who { color: var(--tb-muted); }
.task-who-from { font-size: .7rem; color: var(--tb-faint); }
/* --- end P-QUOTE-2 --- */

/* --- P-EST-5 --- */
/* A lead becomes a job card. The convert form is the proposal's
   section 1: two columns that fall to one on a phone (its `.two`),
   small muted labels, a quiet sentence under the choice, two buttons.
   The status pills are STATUS, so they use the status tokens and never
   orange: new is navy (structure: nothing has happened yet), contacted
   is plain, a job created is green, lost is faint. */
.enq-card-wrap { max-width: 860px; }
.enq-crumb { font-size: .8rem; color: var(--tb-muted); margin-bottom: .25rem; }
.enq-crumb a { color: var(--tb-muted); }
.enq-two {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: .5rem 1.25rem;
}
.enq-field { margin-bottom: .9rem; border: 0; padding: 0; min-width: 0; }
.enq-field .form-label,
.enq-field legend.form-label { font-size: .78rem; font-weight: 600; color: var(--tb-muted); margin-bottom: .2rem; float: none; width: auto; }
.enq-addr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4rem; }
.enq-addr .enq-addr-wide { grid-column: span 3; }
.enq-static {
  border: 1px dashed var(--tb-line-2);
  border-radius: .375rem;
  padding: .45rem .7rem;
  font-size: .875rem;
  color: var(--tb-faint);
  background: var(--tb-surface);
}
.enq-sentence { font-size: .875rem; color: var(--tb-muted); margin: .25rem 0 1rem; }

.enq-pill {
  display: inline-block;
  font-size: .7rem;
  font-weight: 600;
  padding: .1rem .55rem;
  border-radius: 999px;
  border: 1px solid var(--tb-line-2);
  color: var(--tb-muted);
  white-space: nowrap;
  vertical-align: middle;
}
.enq-pill-new       { border-color: var(--tb-navy); color: var(--tb-navy); background: var(--tb-hover); }
.enq-pill-contacted { border-color: var(--tb-line-2); color: var(--tb-ink); }
.enq-pill-converted { border-color: var(--tb-good); color: var(--tb-good); background: #e8f4ee; }
.enq-pill-lost      { border-color: var(--tb-line); color: var(--tb-faint); }

.enq-table .sub { display: block; font-size: .78rem; color: var(--tb-muted); }
.enq-actions { white-space: nowrap; }
.enq-actions form { margin: 0; }

.enq-kv { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1.25rem; margin: 0; font-size: .925rem; }
.enq-kv dt { color: var(--tb-muted); font-weight: 600; font-size: .8rem; padding-top: .1rem; }
.enq-kv dd { margin: 0; }
.enq-notes { font-size: .925rem; }
/* --- end P-EST-5 --- */
/* --- P-EST-3 -------------------------------------------------------------
   Templates with versions: the register's rows, the editor laid out like
   the estimate (section rail, groups, lines, sub items), the flagged-new
   banner with Keep / Remove, and the versions list with its change block.
   Ported from docs/estimate-proposal.html (.pill-*, .secrail, .bar,
   .diff, .note) under the `tv-` prefix, on this file's tokens.

   COLOUR. Orange is the action (Save version, Keep) and the member's own
   position (the section they are in, "New from TradeBuilders" -- a line
   waiting on THEIR decision). Navy is structure and TradeBuilders' own
   facts (the master pill, the section heading rows). The change block's
   green / red / amber are STATUS -- added, removed, changed -- and never
   on a button.
   ------------------------------------------------------------------------ */

.tv-pill {
  display: inline-block; font-size: .68rem; font-weight: 600; line-height: 1.5;
  padding: 0 .5rem; border-radius: 999px; border: 1px solid var(--tb-line-2);
  color: var(--tb-muted); background: var(--tb-surface); vertical-align: middle; white-space: nowrap;
}
.tv-pill--tb   { border-color: var(--tb-navy); color: var(--tb-navy); background: #e4ecf9; }
.tv-pill--new  { border-color: var(--tb-orange); color: var(--tb-orange-ink); background: var(--tb-orange-soft); }
.tv-pill--kind { background: var(--tb-sunk); }
.tv-pill--q    { border-color: var(--tb-navy); color: var(--tb-navy); background: var(--tb-surface); }

/* the register */
.tv-list { list-style: none; margin: 0; padding: 0; }
.tv-list > li {
  display: flex; justify-content: space-between; align-items: flex-start; gap: .75rem; flex-wrap: wrap;
  padding: .65rem 0; border-bottom: 1px solid var(--tb-line);
}
.tv-list > li:last-child { border-bottom: 0; }
.tv-list .tv-name { font-weight: 600; color: var(--tb-ink); text-decoration: none; }
.tv-list .tv-name:hover { text-decoration: underline; }
.tv-meta { display: block; font-size: .8rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; margin-top: .1rem; }
.tv-acts { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.tv-acts form { margin: 0; }
.tv-more > summary { list-style: none; cursor: pointer; }
.tv-more > summary::-webkit-details-marker { display: none; }
.tv-more[open] { flex-basis: 100%; }
.tv-more .tv-more-body { display: flex; gap: .4rem; flex-wrap: wrap; align-items: end; margin-top: .4rem; }
.tv-foot { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: 1rem; }

/* the editor: rail + table, as the estimate */
.tv-est { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 1rem; align-items: start; }
@media (max-width: 760px) { .tv-est { grid-template-columns: minmax(0, 1fr); } }
.tv-rail {
  position: sticky; top: .75rem; font-size: .82rem;
  border-right: 1px solid var(--tb-line); padding-right: .75rem;
}
@media (max-width: 760px) {
  .tv-rail { position: static; border-right: 0; border-bottom: 1px solid var(--tb-line); padding: 0 0 .6rem;
             display: flex; flex-wrap: wrap; gap: .35rem; }
  .tv-rail .tv-bar, .tv-rail .tv-rail-tot { display: none; }
}
.tv-rail a {
  display: flex; justify-content: space-between; gap: .5rem; padding: .3rem .5rem;
  border-radius: .35rem; color: var(--tb-ink); text-decoration: none;
}
.tv-rail a:hover { background: var(--tb-hover); }
.tv-rail a.on { background: var(--tb-orange-soft); color: var(--tb-orange-ink); font-weight: 600; }
.tv-rail a span:last-child { color: var(--tb-muted); font-weight: 400; font-size: .75rem; font-variant-numeric: tabular-nums; }
.tv-rail a .tv-rail-new { color: var(--tb-orange-ink); font-weight: 600; }
.tv-bar { height: 5px; background: var(--tb-sunk); border-radius: 3px; overflow: hidden; margin: .1rem .5rem .35rem; }
.tv-bar i { display: block; height: 100%; background: var(--tb-navy); }
.tv-rail-tot { border-top: 1px solid var(--tb-line-2); margin-top: .35rem; padding: .45rem .5rem 0; color: var(--tb-muted); }
.tv-rail-tot b { color: var(--tb-ink); }

.tv-table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.tv-table th {
  text-align: left; font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint);
  font-weight: 600; padding: .35rem .5rem; border-bottom: 1px solid var(--tb-line-2); white-space: nowrap;
}
.tv-table td { padding: .4rem .5rem; border-bottom: 1px solid var(--tb-line); vertical-align: middle; }
.tv-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tv-table .tv-sec td { background: var(--tb-sunk); font-weight: 700; font-size: .8rem; color: var(--tb-navy); }
.tv-table .tv-sec td .tv-sec-sub { font-weight: 500; color: var(--tb-muted); float: right; }
.tv-table .tv-group td { font-weight: 600; }
.tv-table .tv-child td:first-child { padding-left: 1.6rem; }
.tv-table .tv-sub td { color: var(--tb-muted); font-size: .8rem; border-bottom: 1px dashed var(--tb-line); }
.tv-table .tv-sub td:first-child { padding-left: 2.6rem; }
.tv-table .tv-flag td { background: #fffaf3; }
.tv-table .tv-addrow td { background: var(--tb-ground); }
.tv-table .tv-addrow summary { color: var(--tb-navy); cursor: pointer; list-style: none; font-size: .82rem; }
.tv-table .tv-addrow summary::-webkit-details-marker { display: none; }
.tv-table .tv-sub-add td:first-child { padding-left: 2.6rem; }
.tv-table tr:target td { box-shadow: inset 3px 0 0 var(--tb-orange); }

.tv-inline { display: inline-flex; gap: .25rem; align-items: center; margin: 0; }
.tv-inline input.form-control { width: 6.5rem; padding: .1rem .35rem; font-size: .82rem; text-align: right; }
.tv-inline .tv-unit { color: var(--tb-faint); font-size: .75rem; white-space: nowrap; }
.tv-inline .tv-go { padding: .05rem .4rem; font-size: .75rem; }
.tv-line-menu { display: inline-flex; gap: .3rem; flex-wrap: wrap; align-items: center; }
.tv-line-menu form { margin: 0; display: inline; }
.tv-link {
  background: none; border: 0; padding: 0; font-size: .78rem; color: var(--tb-navy); text-decoration: none; cursor: pointer;
}
.tv-link:hover { text-decoration: underline; }
.tv-toggle { font-size: .75rem; padding: .05rem .45rem; border-radius: 999px; border: 1px solid var(--tb-line-2);
             background: var(--tb-surface); color: var(--tb-muted); cursor: pointer; }
.tv-toggle.on { border-color: var(--tb-navy); color: var(--tb-navy); background: #e4ecf9; font-weight: 600; }
.tv-sub-form { display: flex; gap: .35rem; flex-wrap: wrap; align-items: end; }
.tv-sub-form .form-control, .tv-sub-form .form-select { font-size: .8rem; padding: .15rem .4rem; width: auto; }
.tv-sub-form .tv-sub-desc { min-width: 14rem; flex: 1 1 14rem; }

/* settings and save-version bar */
.tv-bar-top { display: flex; gap: 1rem; flex-wrap: wrap; align-items: end; justify-content: space-between; }
.tv-bar-top form { margin: 0; display: flex; gap: .5rem; flex-wrap: wrap; align-items: end; }
.tv-bar-top .form-control { width: 6rem; }
.tv-bar-top .tv-reason { width: 16rem; max-width: 100%; }
.tv-pending { font-size: .78rem; color: var(--tb-muted); }
.tv-pending b { color: var(--tb-orange-ink); }

/* the flagged-new banner */
.tv-note {
  border-left: 3px solid var(--tb-orange); background: var(--tb-orange-soft);
  padding: .6rem .85rem; border-radius: 0 var(--tb-radius) var(--tb-radius) 0; font-size: .88rem; margin-bottom: 1rem;
}
.tv-note b { color: var(--tb-orange-ink); }

/* the change block */
.tv-diff { font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem; }
.tv-diff div { padding: .2rem .5rem; border-left: 3px solid var(--tb-line-2); margin-bottom: .2rem; overflow-wrap: anywhere; }
.tv-diff .add { border-left-color: var(--tb-good); background: #e3f2ea; }
.tv-diff .del { border-left-color: var(--tb-crit); background: #fbe9e7; }
.tv-diff .chg { border-left-color: var(--tb-warn); background: #fff6d6; }
.tv-diff-row { flex-basis: 100%; }
.tv-diff-under { margin: .2rem 0 .3rem 1rem; }
.tv-vhead b { font-variant-numeric: tabular-nums; }
.tv-current { font-size: .78rem; color: var(--tb-orange-ink); font-weight: 600; }
/* --- end P-EST-3 --- */
/* --- P-EST-1 ---
   =====================================================================
   The estimate: the section rail, the filter bar, one price state per
   line, sub items folded under their line, prices typed in place, and
   the summary. Ported from the proposal's .secrail / .bar / .pill-* /
   .state / .sub under the est- prefix, on this file's tokens.

   Orange is the member's OWN position and action only: the section they
   are on, the filter they chose, the mark they put on a line, the input
   they are typing in. Green / amber / navy on the state pills are
   STATUS -- where a price came from -- and never something to press.
   ===================================================================== */

.est-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .6rem; }
.est-title { font-size: 1.15rem; font-weight: 700; margin: 0; }

/* The pills: the proposal's .pill set. The soft fills are local because
   the token block has the three status inks and not their tints. */
.est-pill {
  display: inline-block; font-size: .7rem; font-weight: 600; line-height: 1.5;
  padding: .05rem .5rem; border-radius: 999px; border: 1px solid var(--tb-line-2);
  color: var(--tb-muted); background: var(--tb-surface); vertical-align: middle; white-space: nowrap;
}
.est-pill-good { border-color: var(--tb-good); color: var(--tb-good); background: #e3f2ea; }
.est-pill-warn { border-color: var(--tb-warn); color: var(--tb-warn); background: #fff6d6; }
.est-pill-tb   { border-color: var(--tb-navy); color: var(--tb-navy); background: #e4ecf9; }
.est-pill-mark { border-color: var(--tb-orange-ink); color: var(--tb-orange-ink); background: var(--tb-orange-soft); }
.est-pill-kind { background: var(--tb-sunk); }
.est-none { color: var(--tb-faint); font-weight: 600; font-size: .8rem; }
.est-why  { font-size: .74rem; color: var(--tb-faint); }

/* The filter bar. */
.est-filters { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin-bottom: .8rem; }
.est-filters-k { font-size: .8rem; color: var(--tb-muted); margin-right: .15rem; }
.est-filter { font-size: .78rem; padding: .15rem .6rem; text-decoration: none; }
.est-filter b { font-weight: 700; margin-left: .1rem; font-variant-numeric: tabular-nums; }
.est-filter:hover { background: var(--tb-hover); color: var(--tb-ink); }
.est-filter.is-on { border-color: var(--tb-orange-ink); color: var(--tb-orange-ink); background: var(--tb-orange-soft); }
.est-scope { font-size: .76rem; color: var(--tb-muted); margin-left: .25rem; }

/* The rail beside the lines. */
.est { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 1rem; align-items: start; }
.est-rail {
  position: sticky; top: 1rem; font-size: .85rem;
  border-right: 1px solid var(--tb-line); padding-right: .75rem;
}
.est-rail-row {
  display: flex; justify-content: space-between; gap: .5rem;
  padding: .3rem .5rem; border-radius: .375rem; color: var(--tb-ink); text-decoration: none;
}
a.est-rail-row:hover { background: var(--tb-hover); color: var(--tb-ink); }
.est-rail-row.is-on { background: var(--tb-orange-soft); color: var(--tb-orange-ink); font-weight: 600; }
.est-rail-n { color: var(--tb-muted); font-variant-numeric: tabular-nums; font-weight: 400; font-size: .76rem; white-space: nowrap; }
.est-bar { height: 6px; background: var(--tb-sunk); border-radius: 3px; overflow: hidden; margin: .1rem .5rem .45rem; }
.est-bar i { display: block; height: 100%; background: var(--tb-good); }
.est-rail-tot {
  border-top: 1px solid var(--tb-line-2); border-radius: 0; margin-top: .4rem; padding-top: .5rem;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.est-rail-fact { color: var(--tb-muted); }

/* The table. The wrapper scrolls sideways only on a narrow screen, so
   the row menus can drop below the table on a wide one. */
.est-table th { white-space: nowrap; }
.est-table td { vertical-align: middle; }
.est-sec > td { background: var(--tb-sunk); font-weight: 700; font-size: .82rem; color: var(--tb-navy); }
.est-sec-sub { float: right; font-weight: 500; color: var(--tb-muted); font-variant-numeric: tabular-nums; }
.est-group > td { font-weight: 700; border-bottom-color: var(--tb-line-2); }
.est-group .num { font-variant-numeric: tabular-nums; }
.est-line > td:first-child { padding-left: 1.1rem; }
.est-sub > td { font-size: .82rem; color: var(--tb-muted); border-bottom: 1px dashed var(--tb-line); }
.est-sub > td:first-child { padding-left: 2.4rem; }
.est-crumb { color: var(--tb-muted); }
.est-off > td { color: var(--tb-faint); }
.est-off .bill-desc, .est-struck { text-decoration: line-through; }
.est-off .est-pill { text-decoration: none; }
.est-grey { color: var(--tb-faint); }

.est-qty, .est-price { display: flex; align-items: center; gap: .25rem; justify-content: flex-end; margin: 0; }
.est-qty .est-in   { width: 4.6rem; text-align: right; }
.est-unit          { font-size: .74rem; color: var(--tb-muted); min-width: 2.4rem; text-align: left; }
.est-price .est-rate   { width: 4.4rem; text-align: right; }
.est-price .est-amount { width: 6.4rem; text-align: right; }
.est-in { font-variant-numeric: tabular-nums; }
.est-in:focus { border-color: var(--tb-orange); box-shadow: 0 0 0 .15rem var(--tb-orange-soft); }
.est-in.is-saving { background: var(--tb-sunk); }
.est-in.is-saved  { border-color: var(--tb-good); }
.est-in.is-error  { border-color: var(--tb-crit); }
.est-save {
  border: none; background: none; padding: .1rem .2rem; line-height: 1;
  color: var(--tb-faint); opacity: .35; cursor: pointer;
}
.est-table tr:hover .est-save, .est-price:focus-within .est-save { opacity: 1; color: var(--tb-orange-ink); }

/* The row's actions: text links in navy, the proposal's .line-menu. */
.est-acts { white-space: nowrap; text-align: right; }
.est-acts-h { width: 1%; }
.est-menu { display: inline-flex; gap: .55rem; align-items: center; justify-content: flex-end; flex-wrap: nowrap; }
.est-inline { display: inline; margin: 0; }
.est-act {
  border: 0; background: none; padding: 0; font-size: .78rem; color: var(--tb-navy);
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.est-act:hover, .est-act:focus-visible { text-decoration: underline; color: var(--tb-navy-2); }
.est-pop { position: relative; display: inline-block; }
.est-pop > summary { list-style: none; }
.est-pop > summary::-webkit-details-marker { display: none; }
.est-pop-body {
  position: absolute; z-index: 20; top: calc(100% + .3rem); right: 0; width: 15rem;
  background: var(--tb-surface); border: 1px solid var(--tb-line-2);
  border-radius: var(--tb-radius); box-shadow: var(--tb-shadow); padding: .6rem; text-align: left; white-space: normal;
}
.est-pop-menu { display: flex; flex-direction: column; gap: .1rem; margin: 0; }
.est-pop-narrow { width: 9rem; padding: .3rem; }
.est-nn { display: flex; gap: .35rem; margin: 0 0 .35rem; padding-bottom: .45rem; border-bottom: 1px solid var(--tb-line); }
.est-nn .btn { white-space: nowrap; }
.est-pop-menu a, .est-pop-menu button {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  padding: .3rem .45rem; font-size: .82rem; color: var(--tb-ink); text-decoration: none; border-radius: .25rem;
}
.est-pop-menu a:hover, .est-pop-menu button:hover { background: var(--tb-hover); }
.est-pop-menu .est-del:hover { color: var(--tb-crit); }

/* "+ Add a line item" and "+ Add a sub item". */
.est-add > td { border-bottom: 0; }
.est-add summary { color: var(--tb-navy); cursor: pointer; font-size: .85rem; list-style: none; }
.est-add summary::-webkit-details-marker { display: none; }
.est-add details[open] > summary { margin-bottom: .5rem; }
.est-add-sub > td { padding-left: 2.4rem; }
.est-sub-form { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; margin: 0; }
.est-sub-k { color: var(--tb-navy); font-size: .82rem; }
.est-sub-desc { width: 16rem; max-width: 100%; }
.est-sub-kind, .est-sub-unit { width: auto; }
.est-sub-qty { width: 4.6rem; }
.est-f-desc { grid-column: span 12; }
.est-f-nq { grid-column: span 2; display: flex; gap: .4rem; align-items: center; align-self: center; }

.est-buttons { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: .8rem; }
.est-note { font-size: .82rem; color: var(--tb-muted); margin: .6rem 0 0; }

/* The summary (proposal, section 9). */
.est-sum-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 1rem; align-items: start; }
.est-sum-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.est-sum-strong > td { font-weight: 700; background: var(--tb-sunk); }
.est-tiles { display: grid; gap: .6rem; }
.est-tiles > div { background: var(--tb-sunk); border-radius: .375rem; padding: .55rem .7rem; font-size: .85rem; color: var(--tb-muted); }
.est-tiles b { display: block; font-size: 1rem; color: var(--tb-ink); font-variant-numeric: tabular-nums; }
.est-settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: .5rem; align-items: end; }

/* A tablet: the rail becomes a row of pills over the lines. */
@media (max-width: 767.98px) {
  .est { grid-template-columns: minmax(0, 1fr); }
  .est-rail {
    position: static; border-right: 0; border-bottom: 1px solid var(--tb-line);
    padding: 0 0 .6rem; display: flex; flex-wrap: wrap; gap: .35rem;
  }
  .est-rail .est-bar { display: none; }
  .est-rail-row { border: 1px solid var(--tb-line-2); border-radius: 999px; padding: .15rem .6rem; }
  .est-rail-tot { margin-top: 0; padding-top: .15rem; border-top: 1px solid var(--tb-line-2); border-radius: 999px; }
  .est-scroll { overflow-x: auto; }
  .est-pop-body { position: static; width: auto; box-shadow: none; margin-top: .3rem; }
  .est-f-nq { grid-column: span 3; }
}

/* A phone: each row is a short list, each cell a labelled line. */
@media (max-width: 575.98px) {
  .est-scroll { overflow-x: visible; }
  .est-table thead { display: none; }
  .est-table, .est-table tbody, .est-table tr, .est-table td { display: block; width: auto; }
  .est-table tr { border-bottom: 1px solid var(--tb-line); padding: .35rem 0; }
  .est-table td { border: 0; padding: .15rem .6rem; text-align: left; }
  .est-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label); display: inline-block; min-width: 5.5rem;
    font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint);
  }
  .est-qty, .est-price, .est-menu { justify-content: flex-start; display: inline-flex; }
  .est-acts { text-align: left; }
  .est-sub > td:first-child, .est-add-sub > td { padding-left: 1.4rem; }
  .est-sec-sub { float: none; display: block; }
}
/* --- end P-EST-1 --- */
/* --- P-EST-2 ---
   =====================================================================
   Predictive add: the list under the add-a-line and add-a-sub-item
   boxes (proposal section 5, `.drop`). One list for the page, fixed to
   the viewport under the input by estimate.js so a table that scrolls
   sideways cannot clip it. The highlighted row is the member's own
   position (hover tint, as the proposal draws it); the last row, "Add
   “…” as a new line item", is an action, so its words are orange ink.
   ===================================================================== */

.est-sug { position: relative; }
span.est-sug { display: inline-block; }
.est-drop {
  position: fixed; z-index: 1060; max-height: 22rem; overflow-y: auto;
  border: 1px solid var(--tb-line-2); border-radius: 6px;
  background: var(--tb-surface); box-shadow: var(--tb-shadow);
  font-size: .9rem; text-align: left;
}
.est-drop[hidden] { display: none; }
.est-drop-row {
  display: flex; justify-content: space-between; align-items: baseline; gap: .6rem;
  padding: .45rem .65rem; border-bottom: 1px solid var(--tb-line); cursor: pointer;
}
.est-drop-row:last-child { border-bottom: 0; }
.est-drop-row > span { min-width: 0; overflow-wrap: anywhere; color: var(--tb-ink); }
.est-drop-row > small { flex: none; max-width: 55%; color: var(--tb-faint); font-size: .74rem; text-align: right; }
.est-drop-row.hi, .est-drop-row:hover { background: var(--tb-hover); }
.est-drop-new > span { color: var(--tb-orange-ink); font-weight: 600; }

/* The staff side: a task waiting to be grouped. */
.task-wait {
  display: inline-block; font-size: .7rem; font-weight: 600; padding: .05rem .5rem;
  border-radius: 999px; border: 1px solid var(--tb-warn); color: var(--tb-warn); background: #fff6d6;
  vertical-align: middle;
}
.task-merge-hits { list-style: none; padding: 0; margin: .5rem 0 0; }
.task-merge-hits li {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  padding: .35rem 0; border-bottom: 1px solid var(--tb-line);
}
.task-merge-hits li:last-child { border-bottom: 0; }
/* --- end P-EST-2 --- */
/* --- P-EST-4 ---
   =====================================================================
   Pull prices from another quote, and Change template: the proposal's
   .choice cards (the picked one ringed in orange -- the member's own
   selection), its .summary tiles and its .tbl .sec / .child rows, ported
   under est4-. The state pills are P-EST-1's .est-pill set, reused.
   ===================================================================== */
.est4-crumb { font-size: .82rem; color: var(--tb-muted); margin: .25rem 0 .35rem; }
.est4-h { font-size: 1.15rem; font-weight: 700; margin: 1.4rem 0 .75rem; }
.est4-note { border-left: 3px solid var(--tb-navy); background: var(--tb-sunk); padding: .55rem .85rem; border-radius: 0 var(--tb-radius) var(--tb-radius) 0; font-size: .9rem; }
.est4-fine { font-size: .8rem; color: var(--tb-faint); margin: .5rem 0 0; }
.est4-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .75rem; }
.est4-choice { display: block; border: 1px solid var(--tb-line-2); border-radius: var(--tb-radius); padding: .75rem .9rem; background: var(--tb-surface); color: var(--tb-ink); text-decoration: none; }
.est4-choice:hover, .est4-choice:focus-visible { border-color: var(--tb-navy); background: var(--tb-hover); color: var(--tb-ink); outline: none; }
.est4-choice.is-on { border-color: var(--tb-orange); box-shadow: 0 0 0 2px var(--tb-orange-soft); }
.est4-choice b { display: block; font-size: .95rem; margin-bottom: .25rem; }
.est4-choice small { display: block; color: var(--tb-muted); font-size: .8rem; }
.est4-m { display: block; margin-top: .5rem; font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .75rem; color: var(--tb-orange-ink); }
.est4-table td, .est4-table th { vertical-align: top; }
.est4-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.est4-tick { width: 2.2rem; }
.est4-table td:nth-child(2) { min-width: 12rem; }
.est4-sec > td { background: var(--tb-sunk); font-weight: 700; font-size: .82rem; color: var(--tb-navy); }
.est4-child > td:first-child { padding-left: 1.6rem; }
.est4-dash { color: var(--tb-faint); }
.est4-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: .6rem; }
.est4-tiles > div { background: var(--tb-sunk); border-radius: .375rem; padding: .55rem .7rem; font-size: .85rem; color: var(--tb-muted); }
.est4-tiles b { display: block; font-size: 1rem; color: var(--tb-ink); font-variant-numeric: tabular-nums; }
.est4-go { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; padding: .9rem 1rem; }
.est4-pick select { max-width: 460px; }
.est4-dropped summary { cursor: pointer; font-size: .9rem; color: var(--tb-muted); }
@media (max-width: 575.98px) {
  .est4-child > td:first-child { padding-left: .9rem; }
  .est4-go .btn { width: 100%; }
}
/* --- end P-EST-4 --- */
/* --- P-EST-8 --- */
/* The site address: one box with Google's suggestions over the six
   parts, and the site on a small map. The suggestion list highlights in
   navy (structure: a choice being browsed, not an action); the box is an
   ordinary .form-control, and with no key it is a read-only line with the
   parts open under it. The map has a fixed height so the page does not
   jump when Google draws into it. */
.geo-box { position: relative; }
.geo-box .form-control[readonly] { background: var(--tb-sunk); color: var(--tb-muted); }
.geo-list {
  position: absolute; z-index: 20; left: 0; right: 0; top: 100%;
  margin: .15rem 0 0; padding: .25rem 0; list-style: none;
  background: var(--tb-surface);
  border: 1px solid var(--tb-line-2);
  border-radius: var(--tb-radius);
  box-shadow: 0 6px 18px rgba(16, 24, 40, .12);
  max-height: 16rem; overflow-y: auto;
}
.geo-list li { padding: .4rem .75rem; font-size: .9rem; cursor: pointer; }
.geo-list li:hover,
.geo-list li[aria-selected="true"] { background: var(--tb-hover); color: var(--tb-navy); }
.geo-parts { margin-top: .5rem; }
.geo-parts > summary { font-size: .8rem; color: var(--tb-muted); cursor: pointer; width: max-content; }
.geo-parts[open] > summary { margin-bottom: .4rem; }
.geo-parts-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .5rem .6rem; }
.geo-parts-grid .form-label { font-size: .78rem; font-weight: 600; color: var(--tb-muted); margin-bottom: .15rem; }
.geo-p-lot_number, .geo-p-street_number, .geo-p-state { grid-column: span 1; }
.geo-p-street { grid-column: span 4; }
.geo-p-suburb { grid-column: span 3; }
.geo-p-postcode { grid-column: span 2; }
.geo-map-wrap { margin-top: .75rem; }
.geo-map-wrap > summary { font-size: .8rem; color: var(--tb-muted); cursor: pointer; width: max-content; }
.geo-map {
  height: 220px; width: 100%; margin-top: .4rem;
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  background: var(--tb-sunk);
}
.desk-main > .geo-map-wrap { margin: -.5rem 0 .75rem; }
.desk-main > .geo-map-wrap .geo-map { height: 260px; }
@media (max-width: 575.98px) {
  .geo-parts-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .geo-p-street, .geo-p-suburb { grid-column: span 3; }
  .geo-p-street_number, .geo-p-postcode { grid-column: span 2; }
  .geo-p-lot_number, .geo-p-state { grid-column: span 1; }
}
/* --- end P-EST-8 --- */

/* --- P-EST-7 --- */
/* =====================================================================
   P-EST-7: the pipeline (proposal, "The pipeline"). The stage bar, the
   board with a column per stage, cards with days-in-stage that turn
   amber past the stage's limit, one button per card for the next stage
   plus Lost. Orange is the one action a card is waiting for (Start
   estimating, Won) and the column the member is looking at; navy is
   structure; amber is status (waiting too long) and never a button.
   ===================================================================== */
.pipe-pill { display: inline-block; border: 1px solid var(--tb-line-2); border-radius: 999px; padding: .05rem .6rem; font-size: .78rem; font-weight: 600; color: var(--tb-muted); background: var(--tb-surface); white-space: nowrap; }
.pipe-pill-warn { border-color: var(--tb-warn); color: var(--tb-warn); background: #fff8e6; }
.pipe-stagebar { display: flex; flex-wrap: wrap; gap: .3rem; margin: .5rem 0 .75rem; }
.pipe-stagebar a { padding: .2rem .6rem; border: 1px solid var(--tb-line-2); border-radius: 999px; font-size: .8rem; color: var(--tb-ink); background: var(--tb-surface); text-decoration: none; white-space: nowrap; }
.pipe-stagebar a b { color: var(--tb-navy); font-variant-numeric: tabular-nums; }
.pipe-stagebar a.is-empty { color: var(--tb-faint); }
.pipe-stagebar a.is-empty b { color: var(--tb-faint); font-weight: 500; }
.pipe-stagebar a:hover { background: var(--tb-hover); }
.pipe-stagebar a.on { background: var(--tb-orange-soft); border-color: var(--tb-orange); }
.pipe-stagebar .pipe-closed-link { margin-left: auto; background: var(--tb-sunk); color: var(--tb-muted); }
.pipe-due { color: var(--tb-warn); font-weight: 600; }
/* Flex, not a grid: a column with cards takes the room, an empty one
   shrinks to its heading, so a board with one busy stage is not six
   empty panels and one full one. */
.pipe-board { display: flex; gap: .5rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.pipe-board.is-one { display: block; max-width: 520px; }
.pipe-col { flex: 1 1 210px; max-width: 300px; background: var(--tb-sunk); border-radius: var(--tb-radius); padding: .45rem; min-width: 190px; }
.pipe-col.is-empty { flex: 0 0 auto; min-width: 0; padding: .45rem .6rem; }
.pipe-col-h { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-navy); font-weight: 700; margin: 0 .1rem .4rem; white-space: nowrap; display: flex; justify-content: space-between; gap: .5rem; }
.pipe-col.is-empty .pipe-col-h { margin: 0; color: var(--tb-faint); }
.pipe-col-h span { font-variant-numeric: tabular-nums; }
.pipe-card { display: block; position: relative; border: 1px solid var(--tb-line); border-radius: .375rem; padding: .4rem .5rem; margin-bottom: .35rem; background: var(--tb-surface); }
.pipe-card.is-late { border-color: var(--tb-warn); box-shadow: inset 3px 0 0 var(--tb-warn); }
.pipe-card.is-late .pipe-meta { color: var(--tb-warn); }
.pipe-title { font-weight: 600; font-size: .92rem; line-height: 1.25; color: var(--tb-ink); text-decoration: none; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: 1.6rem; }
.pipe-title:hover { color: var(--tb-navy); text-decoration: underline; }
.pipe-meta { font-size: .78rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; }
.pipe-actions { display: flex; gap: .3rem; align-items: center; margin-top: .3rem; }
.pipe-actions form { margin: 0; }
.pipe-go { padding: .1rem .5rem; font-size: .8rem; }
.pipe-more > summary { list-style: none; }
.pipe-more > summary::-webkit-details-marker { display: none; }
/* The menu sits on the card's top-right corner and opens over the
   column instead of pushing the card taller. */
.pipe-menu { position: absolute; top: .2rem; right: .2rem; }
.pipe-menu > summary { padding: 0 .4rem; line-height: 1.4; font-weight: 700; letter-spacing: .05em; }
.pipe-pop { position: relative; }
.pipe-menu.pipe-pop { position: absolute; }
.pipe-pop[open] > .pipe-form { position: absolute; z-index: 20; top: 100%; right: 0; width: 240px; box-shadow: 0 6px 18px rgba(16,24,40,.16); background: var(--tb-surface); }
.pipe-pop:not(.pipe-menu)[open] > .pipe-form { left: 0; right: auto; }
.pipe-quiet { color: var(--tb-muted); background: transparent; border-color: transparent; font-weight: 500; }
.pipe-quiet:hover { color: var(--tb-ink); background: var(--tb-hover); }
.pipe-form { display: grid; gap: .4rem; margin-top: .25rem; padding: .5rem; border: 1px solid var(--tb-line); border-radius: .375rem; background: var(--tb-sunk); }
.pipe-form label { font-size: .75rem; font-weight: 600; color: var(--tb-muted); }
.pipe-form label .form-control { margin-top: .15rem; }
.pipe-inline { display: grid; gap: .3rem; padding-top: .35rem; border-top: 1px solid var(--tb-line); }
.pipe-menu-link { font-size: .85rem; font-weight: 600; }
.pipe-fine { font-size: .75rem; color: var(--tb-faint); }
.pipe-more-link { font-size: .8rem; color: var(--tb-muted); text-decoration: none; text-align: center; }
.pipe-more-link:hover { background: var(--tb-hover); color: var(--tb-ink); }
.pipe-closed { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .5rem; }
.pipe-closed > div > .pipe-pill { margin-bottom: .2rem; }
.pipe-mono { font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .8rem; }
.pipe-history { list-style: none; padding: 0; font-size: .88rem; }
.pipe-history li { padding: .25rem 0; border-bottom: 1px solid var(--tb-line); }
.pipe-history li:last-child { border-bottom: 0; }
.pipe-history .num { color: var(--tb-muted); margin-right: .35rem; font-variant-numeric: tabular-nums; }
/* Two lines a card: the title (with the menu in its corner), then the
   suburb and days with the one button beside them. */
.pipe-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: .4rem; align-items: center; }
.pipe-card .pipe-title { grid-column: 1 / -1; }
.pipe-card .pipe-actions { margin-top: 0; grid-column: 2; grid-row: 2; }
.pipe-closed .pipe-card .pipe-actions { grid-column: 1 / -1; grid-row: auto; margin-top: .3rem; }
.pipe-col-h a { color: inherit; text-decoration: none; }
.pipe-col-h a:hover { text-decoration: underline; }
/* On a wide screen the column headings are the stage bar; the chips
   stay for a phone, where empty columns are hidden. On a wide screen
   Lost and done is a button beside Website form. */
@media (min-width: 768px) {
  .pipe-stagebar { display: none; }
}
@media (max-width: 767.98px) {
  /* A phone stacks the columns, busy ones only; the chips jump to one. */
  .pipe-board { flex-direction: column; overflow-x: visible; }
  .pipe-col { max-width: none; width: 100%; flex: 0 0 auto; }
  .pipe-col.is-empty { display: none; }
}
/* --- end P-EST-7 --- */

/* --- P-EST-9 --- */
/* The staff Library. Navy structure, no orange: this is TradeBuilders'
   own screen, and orange is the member's position and the action colour
   (the primary buttons already carry it). The editor reuses P-EST-3's
   `tv-` styles as they are; only what is new is here. */
.lib-nav { display: flex; flex-wrap: wrap; gap: .25rem; border-bottom: 1px solid var(--tb-line); padding-bottom: .5rem; }
.lib-nav a { padding: .3rem .7rem; border-radius: var(--tb-radius); color: var(--tb-muted); text-decoration: none; font-size: .9rem; }
.lib-nav a:hover { background: var(--tb-hover); color: var(--tb-ink); }
.lib-nav a.on { background: var(--tb-sunk); color: var(--tb-navy); font-weight: 600; }
.lib-tiles .lib-tile-wait .staff-tile-n { color: var(--tb-warn); }
.lib-dl dt { margin-top: .6rem; }
.lib-dl dt:first-child { margin-top: 0; }
.lib-dl dd { margin: .15rem 0 0; color: var(--tb-muted); font-size: .9rem; max-width: 75ch; }
.lib-cols { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem; align-items: start; }
.lib-log li { display: block; }
.lib-log .sub { color: var(--tb-muted); font-variant-numeric: tabular-nums; margin-right: .4rem; }
.lib-behind { display: inline-block; margin-left: .35rem; padding: 0 .4rem; border-radius: 1rem; font-size: .75rem;
  background: var(--tb-sunk); color: var(--tb-warn); }
.lib-task { font-size: .75rem; color: var(--tb-faint); text-decoration: none; margin-left: .25rem; }
.lib-task:hover { color: var(--tb-navy); text-decoration: underline; }
.lib-rec { display: inline-block; margin-left: .35rem; padding: 0 .45rem; border-radius: 1rem; font-size: .72rem;
  font-weight: 600; background: var(--tb-sunk); color: var(--tb-navy); vertical-align: middle; }
.lib-areas { max-width: 22rem; color: var(--tb-muted); }
.lib-used { color: var(--tb-muted); white-space: nowrap; }
.lib-sub-set { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.lib-sub-set .form-select { width: auto; min-width: 7.5rem; }
.lib-sub-set .task-qty { width: 5.5rem; }
.lib-sub-new .form-select { min-width: 7rem; }
.lib-flow p { max-width: 75ch; }
.lib-vers { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
.lib-vers li { padding: .45rem 0; border-bottom: 1px solid var(--tb-line); }
.lib-vers li:last-child { border-bottom: 0; }
.lib-vers .diff { margin-top: .35rem; padding: .4rem .6rem; background: var(--tb-sunk); border-radius: var(--tb-radius); font-size: .85rem; }
@media (max-width: 767.98px) {
  .lib-cols { grid-template-columns: 1fr; }
  .lib-used { white-space: normal; }
}
/* --- end P-EST-9 --- */
/* --- P-EST-10 --- */
/* The quote overview and its line panel (proposal, section 7), under
   qo-. Navy is structure and the TB recommends pill; orange is the
   member's own -- My supplier and a ticked box -- and the action;
   red is the "Nobody to ask yet" status and never a button. */
.qo-crumb { font-size: .82rem; color: var(--tb-muted); margin: .25rem 0 .35rem; }
.qo-h { font-size: 1.25rem; font-weight: 700; margin: .25rem 0 1rem; }
.qo-crit { color: var(--tb-crit); }
.qo-card { display: block; background: var(--tb-surface); border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  box-shadow: var(--tb-shadow); margin-bottom: .6rem; }
.qo-red { border-color: var(--tb-crit); padding: .75rem .9rem; margin-bottom: 1rem; }
.qo-red-h { color: var(--tb-crit); font-weight: 700; margin-bottom: .4rem; }
.qo-list { list-style: none; margin: 0; padding: 0; }
.qo-list li { display: flex; flex-wrap: wrap; gap: .4rem .75rem; align-items: center; justify-content: space-between;
  padding: .45rem 0; border-top: 1px solid var(--tb-line); }
.qo-list li:first-child { border-top: 0; }
.qo-list-t { font-size: .92rem; }
.qo-acts { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.qo-inline { display: inline; margin: 0; }
.qo-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 .75rem; align-items: start; }
.qo-sum { list-style: none; cursor: pointer; display: flex; flex-wrap: wrap; gap: .25rem .5rem; align-items: baseline;
  padding: .65rem .9rem .65rem 1.7rem; position: relative; }
.qo-sum::-webkit-details-marker { display: none; }
.qo-sum::before { content: "\25B8"; color: var(--tb-faint); font-size: .8rem; position: absolute; left: .75rem; top: .8rem; }
details[open] > .qo-sum::before { content: "\25BE"; }
.qo-sum:hover, .qo-sum:focus-visible { background: var(--tb-hover); }
.qo-name { font-weight: 700; }
.qo-meta { flex-basis: 100%; font-size: .82rem; color: var(--tb-muted); }
.qo-meta b { color: var(--tb-ink); font-weight: 600; }
.qo-adds { color: var(--tb-navy); font-weight: 600; }
.qo-pill { display: inline-block; font-size: .7rem; font-weight: 600; line-height: 1.5; padding: 0 .45rem; border-radius: 1rem;
  border: 1px solid var(--tb-line-2); background: var(--tb-sunk); color: var(--tb-muted); white-space: nowrap; }
.qo-pill-tb { border-color: var(--tb-navy); color: var(--tb-navy); background: #e4ecf9; }
.qo-pill-self { border-color: var(--tb-orange); color: var(--tb-orange-ink); background: var(--tb-orange-soft); }
.qo-label { font-size: .75rem; color: var(--tb-muted); }
.qo-body { padding: 0 .9rem .8rem; border-top: 1px solid var(--tb-line); }
.qo-table td { vertical-align: top; }
.qo-table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--tb-muted); }
.qo-sec > td { background: var(--tb-sunk); font-weight: 700; font-size: .78rem; color: var(--tb-navy); }
.qo-q { font-style: italic; font-size: .85rem; color: var(--tb-muted); margin-top: .15rem; }
.qo-line-acts { white-space: nowrap; text-align: right; width: 1%; }
.qo-line-acts a { font-size: .82rem; margin-left: .5rem; }
.qo-note .form-label { font-size: .82rem; margin-bottom: .2rem; }
.qo-note-row { display: flex; gap: .4rem; }
.qo-note-row .form-control, .qo-note-row .form-select { flex: 1 1 auto; min-width: 0; }
.qo-card-acts { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .5rem; }
.qo-send { padding: 1rem 1.1rem; margin-top: 1rem; }
.qo-send-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem; }
.qo-go { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: 1rem; }
.qo-why { margin: .6rem 0 0; font-size: .85rem; color: var(--tb-muted); max-width: 75ch; }
.qo-panel-h { display: flex; flex-wrap: wrap; gap: .25rem 1rem; justify-content: space-between; align-items: baseline; }
.qo-panel-t { font-size: 1.1rem; }
.qo-panel-k { color: var(--tb-muted); font-size: .88rem; font-variant-numeric: tabular-nums; }
.qo-sub { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: var(--tb-navy); font-weight: 700; margin: 1.1rem 0 .4rem; }
.qo-empty { font-size: .9rem; color: var(--tb-muted); margin: 0 0 .4rem; }
.qo-fine { font-size: .8rem; color: var(--tb-faint); margin: 0 0 .4rem; }
.qo-picks { list-style: none; margin: 0; padding: 0; }
.qo-pick { display: flex; flex-wrap: wrap; gap: .2rem .75rem; justify-content: space-between; align-items: center;
  padding: .35rem 0; border-top: 1px solid var(--tb-line); }
.qo-pick:first-child { border-top: 0; }
.qo-tick { display: inline-flex; gap: .5rem; align-items: center; background: none; border: 0; padding: .15rem 0; color: var(--tb-ink);
  font: inherit; text-align: left; cursor: pointer; }
.qo-tick.is-off { cursor: default; color: var(--tb-faint); }
.qo-box { flex: 0 0 auto; width: 1.05rem; height: 1.05rem; border: 1.5px solid var(--tb-line-2); border-radius: .25rem; background: var(--tb-surface);
  position: relative; }
.qo-tick.is-on .qo-box { background: var(--tb-orange); border-color: var(--tb-orange); }
.qo-tick.is-on .qo-box::after { content: ""; position: absolute; left: .3rem; top: .08rem; width: .32rem; height: .6rem;
  border: solid #1b1b1b; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.qo-tick:hover:not(.is-off) .qo-box { border-color: var(--tb-orange); }
.qo-pick-meta { font-size: .82rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; }
.qo-row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: flex-start; margin-top: .9rem; }
.qo-own > summary { list-style: none; }
.qo-own > summary::-webkit-details-marker { display: none; }
.qo-own-body { margin-top: .6rem; padding: .75rem; background: var(--tb-sunk); border-radius: var(--tb-radius); min-width: min(34rem, 100%); }
.qo-own-new { margin-top: .6rem; }
.qo-own-grid { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: .4rem; }
.qo-question { margin-top: 1.1rem; }
.qo-also { margin-top: .75rem; padding: .55rem .8rem; border-left: 3px solid var(--tb-navy); background: var(--tb-sunk);
  border-radius: 0 var(--tb-radius) var(--tb-radius) 0; font-size: .9rem; }
.qo-link { background: none; border: 0; padding: 0; color: var(--tb-navy); text-decoration: underline; font: inherit; cursor: pointer; }
.qo-link:hover { color: var(--tb-navy-2); }
.qo-done { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--tb-line); }
.qo .is-saving { opacity: .6; }
.est-nobody { color: var(--tb-crit); font-weight: 600; }
.est-going { font-size: .88rem; }
.est-getq { margin-top: .75rem; align-items: center; }
@media (max-width: 767.98px) {
  .qo-cards, .qo-send-grid { grid-template-columns: 1fr; }
  .qo-own-grid { grid-template-columns: 1fr; }
  .qo-list li { display: block; }
  .qo-acts { margin-top: .35rem; }
  .qo-go .btn { width: 100%; }
}
/* --- end P-EST-10 --- */

/* --- JOBBAR (25 Sep) --- */
/* The job list as a bar above the job instead of a left column, so the
   estimate and every other tab take the full width. */
.desk.desk-full { display: block; }
.jobbar { display: flex; align-items: center; gap: .5rem; margin-bottom: .75rem; }
.jobbar-pick { position: relative; flex: 0 1 26rem; min-width: 0; }
.jobbar-pick > summary { list-style: none; }
.jobbar-pick > summary::-webkit-details-marker { display: none; }
.jobbar-current { display: flex; align-items: center; gap: .5rem; padding: .3rem .65rem; border: 1px solid var(--tb-line-2); border-radius: .375rem; background: var(--tb-surface); cursor: pointer; }
.jobbar-current:hover, .jobbar-pick[open] > .jobbar-current { border-color: var(--tb-navy); }
.jobbar-label { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-faint); }
.jobbar-name { flex: 1; min-width: 0; font-weight: 600; font-size: .9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jobbar-panel { position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; width: min(26rem, calc(100vw - 2rem)); background: var(--tb-surface); border: 1px solid var(--tb-line-2); border-radius: var(--tb-radius); box-shadow: 0 8px 24px rgba(16,24,40,.16); overflow: hidden; }
.jobbar-search { padding: .5rem; border-bottom: 1px solid var(--tb-line); }
.jobbar-list { max-height: 55vh; overflow-y: auto; }
.jobbar-none { padding: .75rem; font-size: .85rem; color: var(--tb-muted); }
.jobbar-all { display: block; padding: .5rem .75rem; border-top: 1px solid var(--tb-line); font-size: .82rem; font-weight: 600; text-decoration: none; }
.jobbar-all:hover { background: var(--tb-hover); }
.rail-page .rail-list { max-height: none; }
.rail-page .rail-new { display: none; }
/* --- end JOBBAR --- */
/* --- P-EST-15 --- */
/* Leads that qualify, with files. The qualifying answers are a quiet
   line of facts -- navy ink, no status colour, because "$450k · finance
   ✓" is information about the client, not a verdict. On the pipeline
   card it is a third line, only on a lead that has answers. The lead's
   file list reuses the job Files tab's form grid (.ns20-*). */
.enq-qual { font-size: .85rem; color: var(--tb-navy); font-weight: 600; margin-top: .15rem; font-variant-numeric: tabular-nums; }
.pipe-card .pipe-qual { grid-column: 1 / -1; color: var(--tb-navy); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pipe-card.is-late .pipe-qual { color: var(--tb-navy); }
.enq-file-list { list-style: none; padding: 0; margin: 0 0 .75rem; }
.enq-file-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .6rem; padding: .35rem 0; border-bottom: 1px solid var(--tb-line); }
.enq-file-list li:last-child { border-bottom: 0; }
.enq-file-list li a { font-weight: 600; word-break: break-all; }
.enq-file-list li form { margin-left: auto; }
/* The phone tab bar is two rows (ten tabs) and .app-main leaves room
   for one, so the last button on a lead's form sat under it. Scoped to
   the lead pages this packet owns; the app-wide padding is not. */
@media (max-width: 767.98px) {
  .enq-card-wrap { padding-bottom: 3rem; }
}
/* --- end P-EST-15 --- */

/* --- P-EST-11 ---
   The walkthrough's fixes to the estimate screen and summary.

   ONE LINE A ROW at desktop width: the line item's name, its kind and
   who prices it sit on one line and an over-long name is cut with an
   ellipsis (the whole name is in the Edit form and the title); the
   actions are Price (or Open / Restore) and the ··· menu. Below 992px
   the old wrapping comes back, and the phone layout is P-EST-1's.

   The "needs a quote" pill is a button now -- the toggle -- but still
   looks like the pill: orange, because it is the member's own mark.
   ===================================================================== */
button.est11-nq { cursor: pointer; font-family: inherit; }
button.est11-nq:hover, button.est11-nq:focus-visible { text-decoration: line-through; }

@media (min-width: 992px) {
  .est-table { table-layout: auto; }
  .est-table td.est-desc {
    max-width: 0; width: 100%;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .est-table td.est-desc .sub { display: inline; margin-left: .45rem; }
  .est-table td.est-desc .chip-asked { margin-left: .35rem; }
  .est-table td.est-from { white-space: nowrap; }
  .est-table td.est-from .est-why {
    display: inline-block; max-width: 11rem; vertical-align: middle;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
}

/* The rate box, there on every measured line, wide enough for its
   placeholder ("rate / day"). */
.est-price .est-rate { width: 5.6rem; }

/* The ··· menu's first form: Pull in, and Needs a quote. */
.est11-menu-top { margin: 0 0 .35rem; padding-bottom: .35rem; border-bottom: 1px solid var(--tb-line); }

/* The summary: Mark quoted on an unfinished estimate asks first. */
.est11-unready {
  display: flex; flex-wrap: wrap; gap: .5rem .9rem; align-items: center; justify-content: space-between;
  margin-top: .6rem; padding: .5rem .6rem; border-radius: .375rem;
  background: #fff6d6; border: 1px solid var(--tb-warn);
}
.est11-unready-tick { display: flex; gap: .4rem; align-items: center; margin: 0; font-size: .85rem; }
.est11-unready-tick .form-check-input { margin: 0; }
/* --- end P-EST-11 --- */

/* --- P-EST-13 --- */
/* Trade packages on the quote overview and the package panel, under
   qo-pkg. A package is structure, so its tag is navy; the part-ticked box
   is the member's own choice, so it is orange like a full tick. */
.qo-pkg-tag { display: inline-block; font-size: .68rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  line-height: 1.5; padding: 0 .4rem; border-radius: .25rem; border: 1px solid var(--tb-navy); color: var(--tb-navy);
  background: #e4ecf9; vertical-align: .08em; }
.qo-pkg-of { display: block; font-size: .8rem; color: var(--tb-muted); margin-top: .1rem; }
.qo-list li.qo-pkg-row .qo-list-t { flex: 1 1 22rem; min-width: 0; }
.qo-pkg > td:first-child { border-left: 3px solid var(--tb-navy); }
.qo-pkg-note { margin: .6rem 0 0; padding: .45rem .75rem; font-size: .88rem; background: var(--tb-sunk);
  border-left: 3px solid var(--tb-navy); border-radius: 0 var(--tb-radius) var(--tb-radius) 0; }
.qo-pkg-lines { font-size: .9rem; }
.qo-pkg-parent > td { font-weight: 600; font-size: .85rem; color: var(--tb-muted); padding-bottom: .15rem; }
.qo-pkg-sub > td:first-child { padding-left: 1.4rem; }
.qo-tick.is-some .qo-box { border-color: var(--tb-orange); }
.qo-tick.is-some .qo-box::after { content: ""; position: absolute; left: .18rem; right: .18rem; top: 50%; height: 2px;
  margin-top: -1px; background: var(--tb-orange); }
.req-package > td { background: var(--tb-sunk); color: var(--tb-navy); font-size: .88rem; }
/* --- end P-EST-13 --- */

/* --- P-EST-14 --- */
/* Statutory lines calculated. On the estimate, "calculated" is how the
   line got its price -- structure, so navy like the Indicative pill --
   and "use the calculation" is a quiet link, not an action button. The
   staff page (/staff/library/statutory) stacks its two tables, each full
   width (side by side, the edit rows did not fit), and on a phone each
   row becomes a labelled card. */
.stat14-calc { border-color: var(--tb-navy); color: var(--tb-navy); background: #e4ecf9; }
.stat14-why { white-space: normal; }
/* P-EST-11 cuts a Price-from detail at 11rem on a wide screen; a statutory
   line's says why it has no price ("rates not set yet — TradeBuilders
   staff"), so it wraps instead of being cut. */
@media (min-width: 992px) {
  .est-table td.est-from .est-why.stat14-why { max-width: 14rem; white-space: normal; line-height: 1.25; }
}
.stat14-back { display: inline; margin: 0; }
.stat14-link { border: 0; background: none; padding: 0; font-size: .74rem; color: var(--tb-navy);
  text-decoration: underline; cursor: pointer; }
.stat14-link:hover { color: var(--tb-navy-2); }
.stat14-sum { border-top: 1px solid var(--tb-line); }
.stat14-sum-head > td { font-size: .78rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: var(--tb-muted); padding-top: .7rem; }
.stat14-sum .est-why { display: block; }

.stat14-note { padding: .75rem 1rem; margin-bottom: 1rem; border-left: 3px solid var(--tb-navy); font-size: .92rem; }
.stat14-cols { display: grid; grid-template-columns: 1fr; gap: 1rem; }
.stat14-cols > .section-card { padding: 1rem; min-width: 0; }
.stat14-how { font-size: .88rem; color: var(--tb-muted); }
.stat14-how b { color: var(--tb-ink); }
.stat14-unset { color: var(--tb-warn) !important; }
.stat14-tag { display: inline-block; font-size: .7rem; font-weight: 700; padding: 0 .4rem; border-radius: .25rem;
  border: 1px solid var(--tb-line-2); color: var(--tb-muted); white-space: nowrap; }
.stat14-tag.stat14-now { border-color: var(--tb-good); color: var(--tb-good); background: #e3f2ea; }
.stat14-tag.stat14-past { color: var(--tb-faint); }
.stat14-table td { vertical-align: middle; }
.stat14-table input.form-control { min-width: 6.5rem; }
.stat14-table input[type="date"] { min-width: 9rem; }
.stat14-acts { white-space: nowrap; text-align: right; }
.stat14-by { font-size: .78rem; color: var(--tb-faint); }
.stat14-table-head { font-weight: 600; font-size: .9rem; margin: .5rem 0 .25rem; }
.stat14-add { border-top: 1px solid var(--tb-line); padding-top: .75rem; margin-top: .5rem; }
.stat14-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .5rem; align-items: end; }
.stat14-grid label { margin: 0; }
.stat14-tasks { padding-left: 1.1rem; }
@media (max-width: 575.98px) {
  .stat14-table thead { display: none; }
  .stat14-table tr { display: block; padding: .5rem 0; border-bottom: 1px solid var(--tb-line); }
  .stat14-table td { display: flex; justify-content: space-between; align-items: center; gap: .75rem; border: 0; padding: .2rem 0; }
  .stat14-table td[data-label]::before { content: attr(data-label); font-size: .78rem; color: var(--tb-muted); }
  .stat14-table td.num { text-align: right; }
  .stat14-table input.form-control { max-width: 11rem; }
  .stat14-acts { justify-content: flex-end !important; }
  .stat14-by { font-size: .74rem; }
}
/* --- end P-EST-14 --- */
/* --- P-EST-12 --- */
/* Sub item quantities from the line, and TradeBuilders benchmarks, under
   e12-. "Use benchmark" is something the member presses, so it is an
   orange pill button (the action colour); the figure it writes is
   Indicative, drawn by P-EST-1's navy .est-pill-tb. Where a sub item's
   quantity comes from sits beside its unit in the Qty cell: in words on
   a phone ("× 0.145 per m² of the line", "set by hand · follow the
   line"), short on a wide screen ("×0.145", "by hand · follow") so the
   row stays one line tall (P-EST-11). "follow" is the one action in it. */
.e12-per, .e12-hand { font-size: .72rem; color: var(--tb-faint); white-space: nowrap; }
.e12-hand { color: var(--tb-warn); }
.e12-n { display: none; }
.e12-link { border: 0; background: none; padding: 0; font: inherit; color: var(--tb-orange-ink);
  text-decoration: underline; cursor: pointer; }
.e12-link:hover, .e12-link:focus-visible { color: var(--tb-ink); }
@media (min-width: 992px) {
  .e12-w { display: none; }
  .e12-n { display: inline; }
  /* With a note in the column, the qty boxes line up on the left and the
     notes trail them, rather than each note pushing its box out of line. */
  .est-table:has(.e12-per, .e12-hand) td.est-qty-cell .est-qty { justify-content: flex-start; }
}
@media (max-width: 991.98px) {
  .est-qty .e12-per, .est-qty .e12-hand { white-space: normal; }
}
.e12-bench-form { display: inline; margin: 0; }
button.e12-bench, .e12-bench { display: inline-block; font-size: .7rem; font-weight: 600; line-height: 1.5;
  padding: .05rem .5rem; border-radius: 999px; border: 1px solid var(--tb-orange); color: var(--tb-orange-ink);
  background: var(--tb-orange-soft); vertical-align: middle; white-space: nowrap; font-family: inherit; }
button.e12-bench { cursor: pointer; }
button.e12-bench:hover, button.e12-bench:focus-visible { background: var(--tb-orange); color: #1b1b1b; }
.e12-bench-off { border-color: var(--tb-line-2); color: var(--tb-faint); background: var(--tb-surface); }
/* The Library: a sub item's per-unit box beside its qty and unit. */
.e12-lib-per { display: inline-flex; align-items: center; gap: .25rem; }
.e12-lib-per-u { font-size: .75rem; color: var(--tb-muted); white-space: nowrap; }
/* The editors: a sub item's per-unit figure beside its kind. */
.e12-per-t { font-size: .74rem; color: var(--tb-faint); margin-left: .3rem; white-space: nowrap; }
/* --- end P-EST-12 --- */
/* --- P-EST-16 --- */
/* Suppliers answer line by line: the supplier lead page and the emailed
   link share one card (templates/supplier/leads/_answer.php). The answer
   is a three-way segmented choice; the chosen one is orange because it
   is the supplier's own position, never a status. Navy is structure
   (the package heading, the pills); amber only says "revision asked". */
.ans-card .ans-table { font-size: .88rem; }
.ans-table > thead th { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); font-weight: 600; }
.ans-table td { vertical-align: top; }
.ans-line + .ans-line > tr > td, .ans-pkg + .ans-line > tr > td { border-top: 1px solid var(--tb-line); }
.ans-table .ans-desc { min-width: 12rem; }
.ans-qty { font-size: .78rem; color: var(--tb-muted); }
.ans-in-pkg .ans-desc { padding-left: 1.1rem; }
.ans-choice { min-width: 15rem; }
.ans-price { min-width: 10rem; }
.ans-extra { min-width: 12rem; }
.ans-money { max-width: 11rem; margin-left: auto; }
.ans-unit { max-width: 5.5rem; }
.ans-seg { display: inline-flex; border: 1px solid var(--tb-line-2); border-radius: 999px; overflow: hidden; background: var(--tb-surface); }
.ans-seg-opt { margin: 0; cursor: pointer; position: relative; }
.ans-seg-opt input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.ans-seg-opt span { display: block; padding: .2rem .65rem; font-size: .8rem; font-weight: 600; color: var(--tb-muted); white-space: nowrap; }
.ans-seg-opt + .ans-seg-opt span { border-left: 1px solid var(--tb-line-2); }
.ans-seg-opt:hover span { background: var(--tb-hover); color: var(--tb-ink); }
.ans-seg-opt input:checked + span { background: var(--tb-orange); color: #1b1b1b; }
.ans-seg-opt input:focus-visible + span { outline: 2px solid var(--tb-orange); outline-offset: -2px; }
/* Where :has() is there, the boxes an answer does not use step aside;
   where it is not, they simply stay (the server ignores them). */
.ans-line:has(.ans-seg) .ans-alt { display: none; }
.ans-line:has(input[value="alternative"]:checked) .ans-alt { display: block; }
.ans-line:has(input[value="cannot"]:checked) .ans-price .ans-money { display: none; }
.ans-line:has(input[value="cannot"]:checked) .ans-price::after { content: "no price"; font-size: .78rem; color: var(--tb-faint); }
.ans-gone td { color: var(--tb-faint); }
.ans-gone-note { font-size: .8rem; font-style: italic; }
.ans-pkg > tr > td { border-top: 1px solid var(--tb-line-2); }
.ans-pkg-whole { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .75rem; margin-top: .35rem; font-size: .82rem; color: var(--tb-ink); }
.ans-pkg-whole .ans-money { margin-left: 0; }
.ans-pkg-whole .form-check-input { margin-right: .3rem; }
.ans-total td { border-top: 2px solid var(--tb-line-2); padding-top: .5rem; }
.ans-pill { display: inline-block; font-size: .72rem; font-weight: 600; padding: .1rem .55rem; border-radius: 999px;
  border: 1px solid var(--tb-line-2); color: var(--tb-muted); background: var(--tb-sunk); }
.ans-pill-sent { border-color: var(--tb-navy); color: var(--tb-navy); background: var(--tb-hover); }
.ans-pill-warn { border-color: var(--tb-warn); color: var(--tb-warn); background: #fff7e6; }
.ans-note { border-left: 3px solid var(--tb-navy); background: var(--tb-sunk); padding: .5rem .75rem;
  border-radius: 0 var(--tb-radius) var(--tb-radius) 0; font-size: .86rem; margin-bottom: .75rem; }
.ans-note-warn { border-left-color: var(--tb-warn); background: #fff7e6; }
.ans-word { font-weight: 600; font-size: .82rem; }
.ans-word-cannot { color: var(--tb-muted); }
.ans-word-alternative { color: var(--tb-navy); }
.ans-terms { border-top: 1px solid var(--tb-line); padding-top: .75rem; }
.ans-terms .form-label { font-size: .8rem; font-weight: 600; color: var(--tb-muted); margin-bottom: .2rem; }
.ans-withdraw summary, .ans-version summary { cursor: pointer; font-size: .85rem; color: var(--tb-navy); }
.ans-withdraw summary { color: var(--tb-muted); }
.ans-version { border-top: 1px solid var(--tb-line); padding: .4rem 0; }
.ans-vtable { font-size: .8rem; }
.ans-vtable td { padding: .2rem .4rem; }
@media (max-width: 767.98px) {
  .ans-table > thead { display: none; }
  .ans-table, .ans-table > tbody, .ans-table > tfoot, .ans-table tr, .ans-table td { display: block; width: auto; }
  .ans-table td { border: none; padding: .2rem .25rem; text-align: left !important; min-width: 0; }
  .ans-line > tr { padding: .6rem 0; }
  .ans-line + .ans-line > tr > td, .ans-pkg + .ans-line > tr > td { border-top: none; }
  .ans-line + .ans-line > tr, .ans-pkg + .ans-line > tr { border-top: 1px solid var(--tb-line); }
  .ans-table td[data-label]:not([data-label=""]):not(.ans-desc)::before {
    content: attr(data-label); display: block;
    font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint); margin-bottom: .1rem;
  }
  .ans-money { max-width: none; margin-left: 0; }
  .ans-seg { display: flex; }
  .ans-seg-opt { flex: 1 1 0; text-align: center; }
  .ans-seg-opt span { padding: .35rem .3rem; }
  .ans-in-pkg .ans-desc { padding-left: .25rem; }
  .ans-actions .btn { flex: 1 1 auto; }
}
/* --- end P-EST-16 --- */

/* --- P-EST-17 --- */
/* The builder chases, revises and chooses. Compare on the line: one ROW
   per supplier (never a column each), a labelled block at phone width.
   Orange is Choose; the one green is "chosen"; amber flags an
   alternative and "revision asked"; nothing colours the cheapest. */
.e17-line { padding: .25rem 0 1rem; border-bottom: 1px solid var(--tb-line); margin-bottom: 1rem; }
.e17-line:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.e17-line-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .25rem 1rem; margin-bottom: .5rem; }
.e17-line-name { font-weight: 600; color: var(--tb-ink); }
.e17-line-name a { color: var(--tb-navy); text-decoration: none; }
.e17-line-name a:hover { text-decoration: underline; }
.e17-line-qty { font-weight: 400; color: var(--tb-muted); font-size: .85rem; margin-left: .4rem; }
.e17-line-now { font-size: .85rem; color: var(--tb-ink); }
.e17-was, .e17-hint { color: var(--tb-muted); font-size: .78rem; }
.e17-chosen { color: var(--tb-good); font-weight: 600; font-size: .8rem; white-space: nowrap; }
.e17-unchoose { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: 0 0 .6rem; }
.e17-empty { color: var(--tb-muted); margin: 0; }
.e17-ans-table { border: 1px solid var(--tb-line); border-radius: var(--tb-radius); overflow: hidden; }
.e17-ans { display: grid; grid-template-columns: minmax(9rem, 1.4fr) minmax(9rem, 1.6fr) 6.5rem 6.5rem 7rem minmax(7rem, 1fr) minmax(8rem, auto);
  gap: .5rem .75rem; align-items: start; padding: .55rem .75rem; border-top: 1px solid var(--tb-line); font-size: .85rem; background: var(--tb-surface); }
.e17-ans:first-child { border-top: 0; }
.e17-ans-hd { background: var(--tb-sunk); color: var(--tb-muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; font-weight: 600; padding-top: .4rem; padding-bottom: .4rem; }
.e17-ans.is-chosen { background: #eef7f1; box-shadow: inset 3px 0 0 var(--tb-good); }
.e17-ans.is-off { color: var(--tb-faint); }
.e17-ans.is-off .e17-who a { color: var(--tb-faint); text-decoration: line-through; }
.e17-who a { font-weight: 600; color: var(--tb-ink); text-decoration: none; }
.e17-who a:hover { color: var(--tb-navy); }
.e17-sub { display: block; font-size: .75rem; color: var(--tb-muted); }
.e17-alt { color: var(--tb-warn); }
.e17-late { color: var(--tb-crit); }
.e17-diff { font-weight: 600; }
.e17-num { text-align: right; }
.e17-num strong { font-variant-numeric: tabular-nums; }
.e17-terms span { display: block; font-size: .78rem; }
.e17-incl summary, .e17-more summary { cursor: pointer; font-size: .78rem; color: var(--tb-navy); }
.e17-incl p { font-size: .78rem; margin: .25rem 0 0; }
.e17-act { display: flex; flex-direction: column; align-items: flex-end; gap: .3rem; }
.e17-act form { margin: 0; }
.e17-more { width: 100%; max-width: 16rem; }
.e17-more[open] { background: var(--tb-sunk); border-radius: var(--tb-radius); padding: .35rem .5rem; }
.e17-more form { margin-top: .35rem; }
.e17-close-form, .e17-inline-form { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.e17-close-form .form-select { max-width: 16rem; }
.e17-inline-form .form-control { max-width: 11rem; }
.e17-rq-acts { white-space: nowrap; text-align: right; }
.e17-chase { min-width: 11rem; }
.e17-chase-acts { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .35rem; margin-top: .25rem; }
.e17-chase .e17-more { max-width: 18rem; }
@media (max-width: 767.98px) {
  .e17-ans { grid-template-columns: 1fr 1fr; padding: .7rem; }
  .e17-ans-hd { display: none; }
  .e17-ans > [data-label]:not([data-label=""])::before { content: attr(data-label); display: block; font-size: .68rem;
    text-transform: uppercase; letter-spacing: .03em; color: var(--tb-faint); font-weight: 600; }
  .e17-ans > .e17-who { grid-column: 1 / -1; }
  .e17-ans > .e17-who::before { display: none !important; }
  .e17-num { text-align: left; }
  .e17-act { grid-column: 1 / -1; align-items: stretch; }
  .e17-act .btn { width: 100%; }
  .e17-more { max-width: none; }
  .e17-rq-acts { white-space: normal; text-align: left; }
  .e17-asked thead { display: none; }
  .e17-asked tr { display: block; padding: .6rem .75rem; border-bottom: 1px solid var(--tb-line); }
  .e17-asked td { display: flex; gap: .75rem; justify-content: space-between; border: 0; padding: .15rem 0; text-align: left !important; }
  .e17-asked td[data-label]:not([data-label=""])::before { content: attr(data-label); font-size: .7rem; text-transform: uppercase;
    letter-spacing: .03em; color: var(--tb-faint); font-weight: 600; flex: 0 0 6.5rem; }
  .e17-asked td.e17-chase { display: block; }
  .e17-asked td.e17-chase::before { display: block; }
}
/* --- end P-EST-17 --- */
/* --- P-EST-19 --- */
/* Can't supply is counted apart from the quotes: muted, never a status colour of its own. */
.e19-cant { color: var(--tb-muted); border-style: dashed; }
/* A line everybody asked could not price: a status (red text), and the way to ask someone else. */
.e19-nobody { color: var(--tb-crit); font-weight: 600; font-size: .8rem; margin-left: .35rem; white-space: nowrap; }
a.e19-nobody { text-decoration: underline; text-underline-offset: 2px; }
a.e19-nobody:hover { color: var(--tb-crit); }
/* On the estimate it sits on its own line under the line item's name, so it is never cut short. */
.est-table td.est-desc a.e19-nobody { display: block; margin-left: 0; white-space: normal; }
@media (max-width: 575.98px) {
  .e19-nobody { white-space: normal; display: inline-block; margin-left: 0; }
}
/* --- end P-EST-19 --- */

/* --- P-MSR --- the renewals desk (/staff/renewals) and the member's
   Membership tab. The house tiles, chips, desk-tabs, tb-table and pills
   do the work; these are the few joins between them. Navy is structure,
   orange only on the one button, red/amber/green only on status pills. */
.msr-head { display: flex; align-items: flex-end; justify-content: space-between; gap: .75rem; flex-wrap: wrap; }
.msr-mode { font-size: .78rem; color: var(--tb-muted); background: var(--tb-sunk); border: 1px solid var(--tb-line-2);
  border-radius: 1rem; padding: .15rem .65rem; }
.msr-tiles { grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); }
.msr-count { display: inline-block; min-width: 1.3rem; padding: 0 .35rem; margin-left: .2rem; border-radius: 1rem;
  background: var(--tb-sunk); color: var(--tb-ink); font-size: .72rem; text-align: center; font-variant-numeric: tabular-nums; }
.msr-bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.msr-chips { display: flex; gap: .35rem; flex-wrap: wrap; flex: 1 1 26rem; }
.msr-search { display: flex; gap: .4rem; flex: 0 1 16rem; }
.msr-search--wide { flex-basis: 24rem; }
.msr-search .form-select { max-width: 10rem; }
.msr-go { margin-left: auto; }
.msr-card { overflow: hidden; }
.msr-table td, .msr-table th { padding-top: .4rem; padding-bottom: .4rem; }
.msr-table tr.is-open > td { background: var(--tb-hover); }
.msr-detail > td { background: var(--tb-ground); border-top: 0; }
.msr-panel { padding: .75rem .9rem; display: grid; gap: .75rem; }
.msr-panel .kv { font-size: .84rem; }
.msr-panel > .btn { justify-self: start; }
.msr-panel-lists { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem; }
.msr-h3 { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-muted); margin: 0 0 .25rem; }
.msr-confirm { border-left: 3px solid var(--tb-navy); background: var(--tb-surface); border-radius: var(--tb-radius); }
.section-card.msr-confirm { padding: 1rem; }
.msr-next { color: var(--tb-navy); font-weight: 600; }
.msr-tier { font-size: .7rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--tb-navy); }
.msr-tier--diamond, .msr-tier--platinum { color: var(--tb-navy-2); }
.msr-tier--lifetime { color: var(--tb-muted); }
.msr-actions { white-space: nowrap; }
.msr-table code { color: var(--tb-ink); font-size: .8rem; }
.msr-actions .btn { padding: .1rem .45rem; font-size: .76rem; }
.msr-totals { color: var(--tb-muted); }
.msr-totals strong { color: var(--tb-ink); }
.msr-settings { display: grid; gap: .9rem; max-width: 44rem; }
.msr-settings > .btn { justify-self: start; }
.msr-set .form-label { font-weight: 600; font-size: .86rem; margin-bottom: .2rem; }
.msr-num { max-width: 11rem; }
.msr-help { font-size: .78rem; color: var(--tb-muted); margin: .25rem 0 0; }
.msr-help .sub { color: var(--tb-faint); }
.msr-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .9rem 1.2rem; }
.msr-fact { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.msr-fact-l { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-muted); }
.msr-fact-n { font-size: 1.05rem; font-weight: 700; }
.msr-fact-s { font-size: .78rem; color: var(--tb-muted); }
.msr-form { display: grid; gap: .35rem; max-width: 30rem; }
.msr-form .form-label { font-size: .8rem; margin: .3rem 0 0; color: var(--tb-muted); }
.msr-two { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 1rem; }
@media (min-width: 1100px) { .msr-two { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
@media (max-width: 575.98px) {
  .msr-search, .msr-search--wide { flex: 1 1 100%; }
  .msr-go { margin-left: 0; }
  .msr-due th:nth-child(3), .msr-due > tbody > tr:not(.msr-detail) > td:nth-child(3),
  .msr-due th:nth-child(4), .msr-due > tbody > tr:not(.msr-detail) > td:nth-child(4) { display: none; }
  main .section-card.msr-card { padding: .25rem .5rem; }
  .msr-due .btn { padding: .15rem .4rem; font-size: .76rem; }
  .msr-table .sub { white-space: normal; }
  .msr-tabs .nav-link { padding: .45rem .55rem; font-size: .82rem; }
  /* The Direct debit and Email log tables become one card per row. */
  .msr-stack thead { display: none; }
  .msr-stack, .msr-stack tbody, .msr-stack tr, .msr-stack td { display: block; width: 100%; }
  .msr-stack tr { padding: .5rem 0; border-bottom: 1px solid var(--tb-line); }
  .msr-stack td { border: 0; padding: .1rem 0; text-align: left !important; }
  .msr-stack td:empty { display: none; }
  .msr-stack td[data-l]::before { content: attr(data-l) ' '; color: var(--tb-muted); font-size: .76rem; margin-right: .3rem; }
  .msr-stack .msr-actions { white-space: normal; padding-top: .35rem; }
  .msr-stack tr.msr-detail { padding: 0; }
}
/* --- end P-MSR --- */
/* --- P-PRICES --- */
/* Supplier price updates (/staff/prices, /prices on the supplier host).
   Tiles, chips, .tb-style tables and pills are the classes already here;
   what is new is the layout of the detail panel and four status pills.
   Orange is Upload CSV and nothing else that is filled; red/amber/green
   mark a STATUS only (a pill, an end date that is close). */
.pp-tile-sub { display: block; font-size: .74rem; color: var(--tb-muted); font-variant-numeric: tabular-nums; margin-top: .1rem; }
.pp-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; margin-bottom: .6rem; }
.pp-chips { display: flex; flex-wrap: wrap; gap: .35rem; margin-right: auto; }
.pp-search { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.pp-search .form-control { width: 14rem; }
.pp-search .form-select { width: 16rem; }
.pp-only-phone { display: none !important; }
.pp-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem; margin-top: .5rem; }

.pp-table th { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-muted); white-space: nowrap; }
.pp-table th a { color: inherit; text-decoration: none; }
.pp-table th a:hover { color: var(--tb-navy); }
.pp-table td { font-size: .86rem; }
.pp-table tbody tr:hover { background: var(--tb-hover); }
.pp-suppliers tr.is-open { background: var(--tb-hover); box-shadow: inset 3px 0 0 var(--tb-navy); }
.pp-suppliers a.row-link { font-weight: 600; color: var(--tb-ink); text-decoration: none; }
.pp-suppliers a.row-link:hover { color: var(--tb-navy); }

.pill.pp-pill-ok   { background: #e7f5ee; color: var(--tb-good); border-color: #bfe3d0; }
.pill.pp-pill-warn { background: #fdf4e0; color: var(--tb-warn); border-color: #ecd9a8; }
.pill.pp-pill-crit { background: #fdeceb; color: var(--tb-crit); border-color: #f5c9c6; }
.pill.pp-pill-info { background: var(--tb-hover); color: var(--tb-navy); border-color: #cfe0ff; }

.pp-detail { scroll-margin-top: 1rem; padding: 1rem 1.15rem; }
.pp-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .6rem 1rem; margin-bottom: .75rem; }
.pp-title { font-size: 1.15rem; font-weight: 700; margin: 0; }
.pp-head-acts { display: flex; flex-wrap: wrap; gap: .4rem; }
.pp-sub { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-muted); margin: 0 0 .4rem; }
.pp-help { font-size: .78rem; color: var(--tb-muted); margin: .5rem 0 0; }
.pp-help code { font-size: .76rem; color: var(--tb-ink); background: var(--tb-sunk); padding: 0 .25rem; border-radius: .2rem; }

.pp-upload { margin-bottom: .75rem; }
.pp-upload > summary { list-style: none; display: inline-flex; align-items: center; gap: .35rem; }
.pp-upload > summary::-webkit-details-marker { display: none; }
.pp-upload[open] > summary { margin-bottom: .6rem; }
.pp-upload-form { background: var(--tb-sunk); border-radius: var(--tb-radius); padding: .75rem .9rem; }
.pp-upload-grid { display: grid; grid-template-columns: 2fr 1fr 1fr auto; gap: .5rem .75rem; }

.pp-batches { margin: .5rem 0 .25rem; }
.pp-batch { border: 1px solid var(--tb-line); border-left: 3px solid var(--tb-navy); border-radius: var(--tb-radius); padding: .45rem .7rem; margin-bottom: .45rem; }
.pp-batch > summary { cursor: pointer; font-size: .86rem; display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.pp-batch[open] > summary { margin-bottom: .5rem; }
.pp-batch-acts { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem; margin-top: .4rem; }
.pp-inline { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .45rem; }
.pp-inline .form-control { width: auto; }

.pp-prices .pp-tick { width: 2rem; }
.pp-prices .pp-newcol { width: 8.5rem; }
.pp-prices .pp-new { max-width: 7.5rem; text-align: right; }
.pp-prices tr.pp-group td { background: var(--tb-sunk); font-size: .84rem; padding-top: .35rem; padding-bottom: .35rem; }
.pp-prices tr.pp-var td:nth-child(3) { padding-left: 1.1rem; }
.pp-prices tr.is-expired td { color: var(--tb-faint); }
.pp-prices td { padding-top: .25rem; padding-bottom: .25rem; }
.pp-prices tr.is-expired td strong { color: var(--tb-faint); }
.pp-prices tr.is-expired .pp-new { color: var(--tb-ink); }
.pp-prices .pp-new { padding: .05rem .4rem; height: 1.65rem; font-size: .84rem; }
.pp-tiles .tile.is-live:not([class*="tile-"]) .tile-n { color: var(--tb-ink); }
.pp-type { display: inline-block; margin-left: .35rem; font-size: .7rem; color: var(--tb-muted); border: 1px solid var(--tb-line-2); border-radius: 1rem; padding: 0 .4rem; }
.pp-soon { color: var(--tb-warn); font-weight: 600; }
.pp-sched { color: var(--tb-navy); font-weight: 600; text-decoration: none; }
.pp-sched:hover { text-decoration: underline; }

.pp-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; margin-top: .75rem; }
.pp-act { border: 1px solid var(--tb-line); border-radius: var(--tb-radius); padding: .5rem .75rem .6rem; margin: 0; min-width: 0; }
.pp-act legend { float: none; width: auto; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-muted); padding: 0 .3rem; margin: 0; }

.pp-history { margin-top: 1rem; border-top: 1px solid var(--tb-line); padding-top: .6rem; }
.pp-history > summary { cursor: pointer; display: list-item; }
.pp-recent { list-style: none; padding: 0; margin: 0; }
.pp-recent li { padding: .15rem 0; }

.pp-problems { background: #fff8e6; border: 1px solid #f3e2b8; border-left: 3px solid var(--tb-warn); border-radius: var(--tb-radius); padding: .55rem .8rem; font-size: .84rem; margin-bottom: .75rem; }
.pp-problems ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.pp-when { border: 1px solid var(--tb-line); border-radius: var(--tb-radius); padding: .6rem .8rem; margin: .75rem 0; }
.pp-when legend { float: none; width: auto; padding: 0 .3rem; }
.pp-radio { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: .2rem 0; font-size: .9rem; }
.pp-radio .form-control { width: auto; }
.pp-confirm { display: flex; align-items: center; gap: .5rem; }

@media (max-width: 767.98px) {
  .pp-upload-grid, .pp-actions { grid-template-columns: 1fr; }
  .pp-search, .pp-search .form-control, .pp-search .form-select { width: 100%; }
  .pp-search .form-control, .pp-search .form-select { flex: 1 1 100%; }
}
@media (max-width: 575.98px) {
  .pp-table thead { display: none; }
  .pp-table, .pp-table tbody, .pp-table tr, .pp-table td { display: block; width: auto; }
  .pp-table tr { border-bottom: 1px solid var(--tb-line); padding: .45rem .15rem; }
  .pp-table td { border: none; padding: .1rem .35rem; text-align: left !important; }
  .pp-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label); display: inline-block; min-width: 5.5rem;
    font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-faint);
  }
  .pp-table td:empty { display: none; }
  .pp-suppliers td[data-label="Products"], .pp-suppliers td[data-label="Current"], .pp-suppliers td[data-label="Expired"] { display: inline-block; margin-right: .6rem; }
  .pp-table.pp-suppliers td[data-label="Products"]::before, .pp-table.pp-suppliers td[data-label="Current"]::before, .pp-table.pp-suppliers td[data-label="Expired"]::before { min-width: 0; margin-right: .3rem; }
  .pp-suppliers.has-open tbody tr:not(.is-open) { display: none; }
  .pp-only-phone { display: inline-block !important; }
  .pp-suppliers td:last-child { position: absolute; right: .25rem; top: .4rem; }
  .pp-suppliers tr { position: relative; padding-right: 4.5rem; }
  .pp-table td, .pp-table tr { background: transparent; }
  .pp-prices td[data-label="Unit"], .pp-prices td[data-label="Current"], .pp-prices td[data-label="Until"] { display: inline-block; margin-right: .5rem; }
  .pp-table.pp-prices td[data-label="Unit"]::before, .pp-table.pp-prices td[data-label="Current"]::before, .pp-table.pp-prices td[data-label="Until"]::before { min-width: 0; margin-right: .3rem; }
  .pp-prices .pp-tick { float: right; width: auto; }
  .pp-prices .pp-newcol { width: auto; }
  .pp-prices .pp-new { display: inline-block; width: 8rem; }
  .pp-prices tr.pp-var td:nth-child(3) { padding-left: .35rem; }
}
/* --- end P-PRICES --- */
/* --- P-REBATE --- */
/* The Rebate Tracker (/staff/rebates). Built on .tile, .chip, .pill,
   .desk-tabs, .section-card and .entity-bar; what is here is only the
   tracker's own layout: the toolbar (chips left, one search right), the
   compact line table with its report-reference group rows, the add-a-line
   row at the foot, and the detail panel. Orange only on .btn-primary;
   amber/green/red only on pills and tile edges that are a status. */
.rt-tiles { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }
.rt-tiles .tile-n { font-size: clamp(1.15rem, 2.2vw, 1.5rem); }
.rt-tile-sub { display: block; font-size: .76rem; color: var(--tb-muted); margin-top: .1rem; }
.rt-tabs { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; }
.rt-tabs .nav-link { white-space: nowrap; }
.rt-dot { display: inline-block; width: .45rem; height: .45rem; border-radius: 50%; margin-left: .3rem; vertical-align: middle; }
.rt-dot.is-good { background: var(--tb-good); }
.rt-dot.is-warn { background: var(--tb-warn); }
.rt-supplier-bar .btn-outline-light { --bs-btn-color: #fff; --bs-btn-border-color: rgba(255,255,255,.5); --bs-btn-hover-bg: rgba(255,255,255,.12); --bs-btn-hover-color: #fff; }

.rt-toolbar { display: flex; align-items: center; gap: .6rem 1rem; flex-wrap: wrap; }
.rt-chips { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.rt-label { font-size: .72rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--tb-muted); margin-right: .15rem; }
.rt-search { margin-left: auto; min-width: min(20rem, 100%); }
.rt-search input[type=date] { max-width: 10rem; }

.rt-table { font-size: .86rem; }
.rt-table th { font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--tb-muted); white-space: nowrap; border-bottom-color: var(--tb-line-2); }
.rt-table td { vertical-align: middle; }
.rt-table .pill { white-space: nowrap; }
.rt-table tr.rt-group td { background: var(--tb-sunk); font-size: .84rem; border-top: 1px solid var(--tb-line-2); }
.rt-table tr.rt-total td { font-weight: 700; background: var(--tb-surface); border-top: 2px solid var(--tb-line-2); }
.rt-table tr.rt-open td { background: var(--tb-hover); }
.rt-table tr.rt-muted td { color: var(--tb-muted); }
.rt-table tr.rt-deleted td { color: var(--tb-faint); }
.rt-table tr.rt-deleted td.tabular { text-decoration: line-through; }
.rt-del-why { display: block; font-size: .74rem; color: var(--tb-crit); }
.rt-check { width: 2rem; }
.rt-rename summary, .rt-exceptions summary { cursor: pointer; color: var(--tb-navy); }
.rt-rename form { min-width: 14rem; }

.rt-exceptions { margin-left: auto; font-size: .85rem; }
.rt-exceptions[open] { flex-basis: 100%; }
.rt-exceptions-body { margin-top: .5rem; padding: .6rem .75rem; background: var(--tb-sunk); border-radius: var(--tb-radius); max-width: 34rem; margin-left: auto; }

/* The add-a-line row: the table's foot, as its own form, laid out on the
   table's columns at desk width and stacked two-up on a phone. */
.rt-addrow {
  display: grid; gap: .45rem .6rem; align-items: end;
  grid-template-columns: minmax(7rem, 1fr) 9.5rem minmax(12rem, 2fr) 8rem 7rem auto;
  padding: .7rem .75rem; margin-top: .25rem;
  background: var(--tb-sunk); border: 1px solid var(--tb-line-2); border-radius: 0 0 var(--tb-radius) var(--tb-radius);
}
.rt-addrow-title { grid-column: 1 / -1; font-size: .72rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--tb-muted); }
.rt-f { display: flex; flex-direction: column; gap: .15rem; margin: 0; min-width: 0; }
.rt-f > span { font-size: .72rem; font-weight: 600; color: var(--tb-muted); }
.rt-f-go { justify-content: flex-end; }
.rt-addrow-opts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .35rem 1.1rem; align-items: center; font-size: .82rem; }
.rt-addrow-opts .form-check-input { margin-right: .3rem; }
@media (max-width: 767.98px) {
  .rt-addrow { grid-template-columns: 1fr 1fr; }
  .rt-f-member { grid-column: 1 / -1; }
  .rt-f-go { grid-column: 1 / -1; }
  .rt-f-go .btn { width: 100%; }
  .rt-search { margin-left: 0; width: 100%; }
  .rt-exceptions { margin-left: 0; }
}

.rt-panel { border-top: 3px solid var(--tb-navy); }
.rt-panel-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem 1.5rem; align-items: start; }
@media (max-width: 767.98px) { .rt-panel-grid { grid-template-columns: 1fr; } }
.rt-panel .form-grid label { margin: 0; }
.rt-delete { padding: .75rem; background: var(--tb-sunk); border-radius: var(--tb-radius); }

.rt-invhead { display: flex; align-items: flex-end; gap: .6rem 1rem; flex-wrap: wrap; }
.rt-invhead .rt-f { width: 12rem; }
.rt-invfoot { display: flex; align-items: center; gap: .6rem 1rem; flex-wrap: wrap; padding: .7rem .75rem; margin-top: .25rem; background: var(--tb-sunk); border: 1px solid var(--tb-line-2); border-radius: 0 0 var(--tb-radius) var(--tb-radius); }
.rt-invsum { margin-right: auto; font-size: .86rem; font-variant-numeric: tabular-nums; }
.rt-invfoot .form-check-input { margin-right: .3rem; }
@media (max-width: 767.98px) { .rt-invhead .rt-f { width: calc(50% - .5rem); } .rt-invsum { width: 100%; } }

.rt-searchgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .6rem .9rem; align-items: end; }
.rt-searchgrid label { margin: 0; }
.rt-searchgo { display: flex; align-items: center; gap: .75rem; justify-content: flex-end; flex-wrap: wrap; }

.rt-json { font-size: .75rem; background: var(--tb-sunk); padding: .6rem .75rem; border-radius: var(--tb-radius); max-height: 20rem; overflow: auto; }

/* The type-ahead list, over the page like the desk's quick search. */
.rt-ta-wrap { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.rt-ta {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + .15rem); min-width: 16rem;
  margin: 0; padding: .2rem; list-style: none; max-height: 18rem; overflow-y: auto;
  background: var(--tb-surface); border: 1px solid var(--tb-line-2); border-radius: var(--tb-radius); box-shadow: 0 6px 20px rgba(16,24,40,.14);
}
.rt-ta-hit { display: flex; gap: .5rem; align-items: baseline; padding: .3rem .5rem; border-radius: .3rem; cursor: pointer; font-size: .86rem; }
.rt-ta-hit:hover, .rt-ta-hit.is-active { background: var(--tb-hover); }
.rt-ta-hit.is-inactive { color: var(--tb-muted); }
.rt-ta-no { font-variant-numeric: tabular-nums; color: var(--tb-muted); min-width: 3.5rem; }
.rt-ta-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-ta-tag { font-size: .7rem; color: var(--tb-warn); }
.rt-ta-none { padding: .3rem .5rem; color: var(--tb-muted); font-size: .84rem; }
@media (max-width: 575.98px) {
  .rt-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
  .rt-tiles .tile { padding: .6rem .7rem; }
  .rt-sm-hide { display: none; }
  .rt-table { font-size: .8rem; }
  .rt-table td, .rt-table th { padding-left: .3rem; padding-right: .3rem; }
}
.rt-xconn code { overflow-wrap: anywhere; }
/* --- end P-REBATE --- */
/* --- P-CONSOLIDATE: the staff bar's submenus --- */
/* A group opens on tap (Bootstrap's dropdown) and, with a pointer, on
   hover as well, so the bar reads as one row of five and a screen is
   never more than one hover away. The active row inside a group is
   marked in orange, not filled, so the menu stays navy and white. */
.staff-nav-group .dropdown-toggle::after { opacity: .6; }
.staff-nav-group .dropdown-menu { margin-top: 0; border: 1px solid var(--tb-line); box-shadow: var(--tb-shadow); min-width: 11rem; }
.staff-nav-group .dropdown-item { padding: .45rem .9rem; }
.staff-nav-group .dropdown-item.active,
.staff-nav-group .dropdown-item:active { background: var(--tb-orange-soft); color: var(--tb-orange-ink); font-weight: 600; }
.staff-nav-group .dropdown-item .bi { color: var(--tb-muted); }
.staff-nav-group .dropdown-item.active .bi { color: var(--tb-orange-ink); }
@media (hover: hover) and (min-width: 768px) {
  .staff-nav-group:hover > .dropdown-menu { display: block; }
  .staff-nav-group:hover > .nav-link { color: #fff; background: rgba(255,255,255,.1); }
}
/* --- end P-CONSOLIDATE --- */

/* --- P-REBATE-2 --- */
/* Entry saves in place: the credit question on the add row, the row
   just saved, and the status line the script writes into. */
.rt-confirm {
  grid-column: 1 / -1; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .5rem .7rem; background: #fdf4e0; border: 1px solid #ecd9a8; border-radius: var(--tb-radius);
  font-size: .86rem; color: var(--tb-ink);
}
.rt-confirm[hidden] { display: none; }
.rt-confirm span { margin-right: auto; font-weight: 600; }
.rt-table tr.rt-new td { background: #e7f5ee; }
.rt-live:empty { display: none; }
.rt-addrow-live { grid-column: 1 / -1; }
.rt-addrow-live .alert { margin-bottom: 0 !important; }
.rt-f-go { flex-direction: row; gap: .4rem; align-items: flex-end; }
@media (max-width: 767.98px) { .rt-f-go .btn { width: auto; flex: 1 1 0; } }
/* The live Entry tab's arrangement (P-REBATE-2, part 2). */
.rt-js .rt-nojs { display: none !important; }
.rt-default-submit { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; padding: 0; }
.rt-h { font-size: .95rem; font-weight: 700; margin: 0 0 .75rem; display: flex; align-items: center; gap: .4rem; }
.rt-h .bi { color: var(--tb-navy); }
.rt-supbar {
  background: var(--tb-navy); color: #fff; padding: .6rem 1rem; border-radius: .375rem;
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
}
.rt-supbar > .bi { font-size: 1.2rem; }
.rt-supbar-name { font-weight: 700; font-size: 1.1rem; }
.rt-supbar-id { background: #fff; color: var(--tb-ink); font-size: .74rem; font-weight: 600; padding: .15rem .45rem; border-radius: .25rem; }
.rt-supbar-rates { display: flex; gap: .35rem; flex-wrap: wrap; }
.rt-rate {
  display: inline-block; font-size: .78rem; font-weight: 600; padding: .2rem .55rem; border-radius: .3rem;
  border: 1px solid rgba(255,255,255,.55); color: #fff; text-decoration: none;
}
.rt-rate:hover { background: rgba(255,255,255,.12); color: #fff; }
.rt-rate.is-on { background: #fff; color: var(--tb-navy); border-color: #fff; }
.rt-rate.is-none { opacity: .75; border-style: dashed; }
.rt-supbar-x { color: #fff; opacity: .85; font-size: 1.05rem; }
.rt-supbar-x:hover { color: #fff; opacity: 1; }
.rt-matches li { padding: .15rem 0; font-size: .9rem; }
.rt-addform .form-label { font-size: .86rem; margin-bottom: .25rem; }
.rt-gst-note { font-size: .8rem; color: var(--tb-muted); }
.rt-gst-note[hidden] { display: none; }
.rt-periods .btn { font-size: .8rem; }
.rt-periods .btn.active { background: var(--tb-navy); border-color: var(--tb-navy); color: #fff; }
.rt-memfilter { flex: 1 1 12rem; min-width: 10rem; }
.rt-date { max-width: 10rem; }
.rt-rowref { display: block; font-size: .74rem; color: var(--tb-muted); }
.rt-lines .pill { font-size: .68rem; }
.rt-group-label { white-space: normal; }
[data-rt-exc-actions][hidden], [data-rt-exc-add][hidden], [data-rt-exc-remove][hidden] { display: none !important; }
#rt-modal .rt-panel { margin: 0; box-shadow: none; }
.rt-suppanel-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1rem 1.5rem; align-items: start; }
@media (max-width: 991.98px) { .rt-suppanel-grid { grid-template-columns: minmax(0, 1fr); } }
/* --- end P-REBATE-2 --- */
/* --- P-NM-2 --- */
/* The staff orders desk (/staff/purchasing): the stage strip is the board. */
.nm-strip { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .4rem; }
.nm-strip li { position: relative; }
.nm-stage { display: flex; flex-direction: column; align-items: flex-start; gap: .05rem; padding: .5rem .7rem; height: 100%;
  background: var(--tb-surface); border: 1px solid var(--tb-line); border-radius: var(--tb-radius); text-decoration: none; color: var(--tb-ink); }
.nm-stage:hover { background: var(--tb-hover); border-color: #cfe0ff; }
.nm-stage-n { font-size: 1.35rem; font-weight: 700; line-height: 1.1; color: var(--tb-faint); }
.nm-stage.has-n .nm-stage-n { color: var(--tb-navy); }
.nm-stage-k { font-size: .76rem; font-weight: 600; color: var(--tb-muted); white-space: nowrap; }
.nm-stage.is-on { background: var(--tb-navy); border-color: var(--tb-navy); }
.nm-stage.is-on .nm-stage-n, .nm-stage.is-on .nm-stage-k { color: #fff; }
.nm-strip li + li::before { content: ""; position: absolute; left: -.33rem; top: 50%; width: .26rem; height: .26rem;
  border-top: 2px solid var(--tb-line-2); border-right: 2px solid var(--tb-line-2); transform: translateY(-50%) rotate(45deg); }
@media (max-width: 767.98px) {
  .nm-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .nm-strip li + li::before { display: none; }
  .nm-stage-k { white-space: normal; line-height: 1.15; }
}
.nm-table .nm-flags .pill { margin-left: .15rem; font-size: .68rem; }
.nm-owing { color: var(--tb-warn); font-weight: 600; }
.nm-ref { color: var(--tb-ink); text-decoration: none; }
.nm-ref:hover { text-decoration: underline; }
.nm-qty { width: 5.2rem; display: inline-block; text-align: right; }
.nm-amt { width: 7rem; }
.nm-act { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: 0 0 .45rem; }
.nm-act .form-control { flex: 1 1 10rem; min-width: 8rem; }
.nm-act .form-control.nm-amt { flex: 0 0 7rem; }
.nm-act textarea.form-control { flex-basis: 100%; }
.nm-mail { margin: 0; white-space: nowrap; color: var(--tb-muted); }
.nm-next { padding: .6rem .7rem; border-radius: var(--tb-radius); background: var(--tb-sunk); }
.nm-next .nm-act { margin: 0; }
.nm-more details { border-top: 1px solid var(--tb-line); }
.nm-more summary { padding: .4rem 0; font-size: .8rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--tb-muted); cursor: pointer; }
.nm-more-body { padding: .1rem 0 .5rem; }
.nm-pick { max-height: 11rem; overflow: auto; border: 1px solid var(--tb-line); border-radius: .35rem; padding: .3rem .5rem; }
.nm-find { max-width: 28rem; }
.nm-lower { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem 1.5rem; border-top: 1px solid var(--tb-line); padding-top: .75rem; }
@media (max-width: 991.98px) { .nm-lower { grid-template-columns: minmax(0, 1fr); } }
.nm-h { font-size: .8rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--tb-muted); margin: 0 0 .4rem; }
details > summary.nm-h { cursor: pointer; margin-top: .3rem; }
.nm-thread { max-height: 22rem; overflow: auto; }
.nm-msg { padding: .4rem .6rem; border-radius: .4rem; margin-bottom: .4rem; background: var(--tb-sunk); font-size: .86rem; }
.nm-msg--tb { background: var(--tb-hover); margin-left: 1.5rem; }
.nm-msg--nm { margin-right: 1.5rem; }
.nm-msg.is-issue { box-shadow: inset 3px 0 0 var(--tb-crit); }
.nm-msg-body { white-space: pre-wrap; }
/* --- end P-NM-2 --- */
/* --- P-NM-4 --- */
/* National Masonry's catalogue tabs (/staff/purchasing/prices, /categories,
   /import). Built on .tile, .chip, .pill and P-PRICES' .pp- layout (bar,
   chips, search, table, detail, foot); what is here is the tree indent,
   the thumbnails, the three-step strip and the form grids. Orange is the
   one action per screen; red/amber/green only mark a status pill. */
.nmc-guide { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem 1rem; margin: .25rem 0 .6rem; }
.nmc-guide-acts { display: flex; flex-wrap: wrap; gap: .4rem; }
.nmc-margin { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem; background: var(--tb-sunk); border-radius: var(--tb-radius); padding: .5rem .75rem; margin-bottom: .6rem; }
.nmc-margin .form-control { width: 5.5rem; }
.nmc-prices .nmc-price, .nmc-client .nmc-price { max-width: 7rem; margin-left: auto; text-align: right; font-variant-numeric: tabular-nums; }
.nmc-price.is-suggested { border-color: var(--tb-navy); background: var(--tb-hover); }
.pp-table tr.is-off td { color: var(--tb-faint); }
.pp-table tr.is-off td strong { color: var(--tb-muted); }
.pp-table tr.is-open { background: var(--tb-hover); box-shadow: inset 3px 0 0 var(--tb-navy); }
.nmc-tree a.row-link { font-weight: 600; color: var(--tb-ink); text-decoration: none; }
.nmc-tree a.row-link:hover { color: var(--tb-navy); }
.nmc-indent { display: inline-block; width: calc(var(--d, 0) * 1.1rem); }
.nmc-treewrap { max-height: 32rem; overflow-y: auto; }
.nmc-thumbcell { width: 2.6rem; }
.nmc-thumb { width: 2.2rem; height: 2.2rem; object-fit: contain; display: block; border-radius: .2rem; background: var(--tb-sunk); }
.nmc-nothumb { display: block; }
.nmc-photo { width: 4.5rem; height: 4.5rem; object-fit: contain; border-radius: var(--tb-radius); background: var(--tb-sunk); }
.nmc-rowacts { white-space: nowrap; }
.nmc-rowacts form { display: inline; }
.nmc-rowacts .btn-link { text-decoration: none; margin-right: .35rem; }
.nmc-forms { display: flex; flex-direction: column; gap: .45rem; }
.nmc-forms .form-select, .nmc-forms .form-control, .pp-detail .pp-inline .form-select { width: auto; max-width: 100%; }
.nmc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: .45rem .75rem; }
.nmc-grid label { display: flex; flex-direction: column; gap: .15rem; }
.nmc-grid .nmc-wide { grid-column: 1 / -1; }
.nmc-settings { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: .5rem .75rem; }
.nmc-settings label { display: flex; flex-direction: column; gap: .15rem; }
.nmc-admins { display: flex; flex-wrap: wrap; gap: .3rem 1rem; }
.nmc-found { display: flex; flex-wrap: wrap; gap: .3rem; }
.nmc-steps { list-style: none; display: flex; flex-wrap: wrap; gap: 0; padding: 0; margin: .25rem 0 .9rem; border-radius: var(--tb-radius); overflow: hidden; background: var(--tb-sunk); }
.nmc-steps li { flex: 1 1 10rem; padding: .5rem .8rem; font-size: .84rem; color: var(--tb-muted); display: flex; align-items: center; gap: .45rem; }
.nmc-steps li.is-now { background: var(--tb-navy); color: #fff; font-weight: 600; }
.nmc-steps li.is-done { color: var(--tb-good); }
.nmc-step-n { width: 1.35rem; height: 1.35rem; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: .72rem; font-weight: 700; background: rgba(0,0,0,.08); }
.nmc-steps li.is-now .nmc-step-n { background: rgba(255,255,255,.25); }
.nmc-filehead { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; margin-bottom: .6rem; }
.nmc-missing { border-left: 3px solid var(--tb-warn); padding: .35rem .7rem; background: var(--tb-sunk); border-radius: var(--tb-radius); }
details[open] > summary.btn-primary { background: transparent; color: var(--tb-ink); border-color: var(--tb-line-2); }
@media (max-width: 575.98px) {
  .nmc-margin .form-control { width: 5rem; }
  .nmc-indent { width: calc(var(--d, 0) * .6rem); }
  .nmc-prices .nmc-price, .nmc-client .nmc-price { display: inline-block; max-width: 8rem; }
  .nmc-treewrap { max-height: none; }
  .nmc-steps li { flex-basis: 100%; }
  .nmc-thumbcell { float: right; width: auto; }
  .nmc-prices td[data-label="Series"], .nmc-prices td[data-label="Group"], .nmc-prices td[data-label="Cost"], .nmc-prices td[data-label="Margin"],
  .nmc-products td[data-label="Cost BNE"], .nmc-products td[data-label="SC"], .nmc-products td[data-label="Ex Works"],
  .nmc-review td[data-label="Size"], .nmc-review td[data-label="Per pallet"], .nmc-review td[data-label="Kg"], .nmc-review td[data-label="Was"], .nmc-review td[data-label="Now"] { display: inline-block; margin-right: .6rem; }
  .nmc-prices td[data-label]::before, .nmc-products td[data-label]::before, .nmc-review td[data-label]::before { min-width: 0 !important; margin-right: .3rem; }
}
/* --- end P-NM-4 --- */
/* --- P-NM-5 --- */
/* The National Masonry member shop (/nm), the member's orders, and NM's token pages. */
.nms-grid { display: grid; grid-template-columns: minmax(0, 1fr) 24rem; gap: 1rem; align-items: start; }
.nms-cart { position: sticky; top: .75rem; }
@media (max-width: 1199.98px) { .nms-grid { grid-template-columns: minmax(0, 1fr) 21rem; } }
@media (max-width: 991.98px) { .nms-grid { grid-template-columns: minmax(0, 1fr); } .nms-cart { position: static; } }
.nms-products td { vertical-align: middle; }
.nms-img-h, .nms-img { width: 3.4rem; }
.nms-img img { width: 3rem; height: 3rem; object-fit: cover; border-radius: .3rem; background: var(--tb-sunk); display: block; }
.nms-sku { font-size: .74rem; color: var(--tb-muted); margin-right: .15rem; }
.nms-add-h { min-width: 15rem; }
.nms-add { display: flex; gap: .3rem; align-items: center; flex-wrap: wrap; margin: 0; }
.nms-add .form-select { flex: 1 1 9rem; min-width: 8rem; }
.nms-add .nm-qty { width: 4.6rem; }
.nms-lines { font-size: .84rem; }
.nms-lines .nm-qty { width: 4.2rem; margin-left: auto; }
.nms-totals { font-size: .86rem; }
.nms-totals td { padding: .15rem .25rem; border: 0; }
.nms-totals .nms-total td { font-weight: 700; border-top: 2px solid var(--tb-line-2); padding-top: .35rem; }
.nms-new { position: relative; }
.nms-new > summary { list-style: none; }
.nms-new > summary::-webkit-details-marker { display: none; }
.nms-pop { position: absolute; right: 0; z-index: 5; width: 17rem; margin-top: .3rem; padding: .6rem; background: var(--tb-surface);
  border: 1px solid var(--tb-line); border-radius: var(--tb-radius); box-shadow: var(--tb-shadow); }
.nms-cart-head { padding-bottom: .5rem; border-bottom: 1px solid var(--tb-line); }
.nms-bank { font-size: .88rem; max-width: 26rem; }
.nms-bank td:first-child { color: var(--tb-muted); width: 7rem; }
.nms-dl { display: grid; grid-template-columns: 8.5rem minmax(0, 1fr); gap: .2rem .75rem; font-size: .88rem; margin: 0; }
.nms-dl dt { font-weight: 600; color: var(--tb-muted); }
.nms-dl dd { margin: 0; }
.nms-narrow { max-width: 44rem; margin-inline: auto; }
.nms-clauses { padding-left: 1.2rem; }
.nms-clauses li { margin-bottom: .6rem; }
.nms-clauses .form-check { padding-left: 1.75rem; }
.nms-changes { padding: .6rem .75rem; border-radius: var(--tb-radius); background: #fdf4e0; border: 1px solid #ecd9a8; }
.nms-who .form-control { width: 11rem; }
.chip .nms-alert-n { color: var(--tb-crit); }
.nms-behalf { background: var(--tb-navy); color: #fff; padding: .55rem 0; font-size: .9rem; }
.nms-behalf .bi { color: var(--tb-orange); }
.nms-behalf-link { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
.nms-nmbar { background: #1a2744; color: #fff; padding: .7rem 0; }
.nms-nmbar-title { font-weight: 700; }
.nms-nmbar-sub { font-size: .74rem; color: #b9c6de; }
@media (max-width: 767.98px) {
  .nms-add-h { min-width: 0; }
  /* The shelf as cards on a phone: product, then price, then the add form, full width. */
  .nms-products thead { display: none; }
  .nms-products tr:not(.rt-group) { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr) auto; gap: .2rem .5rem; padding: .45rem 0; border-bottom: 1px solid var(--tb-line); }
  .nms-products tr:not(.rt-group) td { border: 0; padding: 0; }
  .nms-products tr:not(.rt-group) td:last-child { grid-column: 1 / -1; }
  .nms-products tr.rt-group { display: block; }
  .nms-products tr.rt-group td { display: block; }
  .nms-add .form-select { flex-basis: 100%; }
  .nms-add .nm-qty { flex: 1 1 5rem; width: auto; }
  .nms-dl { grid-template-columns: 7rem minmax(0, 1fr); }
  .nms-who { width: 100%; }
  .nms-who .form-control { flex: 1; width: auto; }
}
/* --- end P-NM-5 --- */

/* --- P-REBATE-4 --- */
/* The live tracker's Invoice Rebate, Invoice List, Search Rebates and Xero
   Invoices tabs on the staff stylesheet. Navy for the badges the live tab
   draws in blue; orange only on each tab's own action button. */
.pill--navy { background: var(--tb-hover); color: var(--tb-navy); border-color: #cfe0ff; }
.rt-badge-navy { background: var(--tb-navy); color: #fff; }
.rt-picked { min-height: calc(1.5em + .5rem + 2px); display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; }
.rt-rowlink { cursor: pointer; }
/* Invoice Rebate: the summary bar appears once a record is ticked. */
.rt-invsumbar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; padding: .75rem 1rem;
  background: var(--tb-sunk); border: 1px solid var(--tb-line-2); border-radius: var(--tb-radius); }
.rt-invsumbar .rt-invsum { flex: 1 1 14rem; margin: 0; }
.rt-js .rt-invsumbar.is-empty { display: none; }
#rt-invtable[data-hide-uninvoiced] .rt-inv-open { display: none; }
#rt-invtable:not([data-show-invoiced]) .rt-inv-done,
#rt-invtable:not([data-show-invoiced]) .rt-col-invref { display: none; }
/* Invoice List: one card per invoice, opened by its header. */
.rt-invcard > summary { list-style: none; cursor: pointer; background: var(--tb-surface); }
.rt-invcard > summary::-webkit-details-marker { display: none; }
.rt-invcard .rt-chev { transition: transform .2s; }
.rt-invcard[open] .rt-chev { transform: rotate(90deg); }
.rt-invcard .rt-inv-ref { color: var(--tb-navy); }
.rt-invedit { display: flex; flex-wrap: wrap; gap: .5rem .75rem; align-items: flex-end; font-size: .8rem; }
.rt-invedit label { display: flex; flex-direction: column; gap: .15rem; margin: 0; }
.rt-invedit label span { color: var(--tb-muted); font-weight: 600; }
.rt-invmore { border-top: 1px solid var(--tb-line); }
/* Search Rebates: what NewSystem adds, beneath the live layout. */
.rt-morefilters summary, .rt-boardwrap > summary { cursor: pointer; color: var(--tb-navy); }
.rt-boardwrap > summary { margin: .5rem 0; }
/* --- end P-REBATE-4 --- */
/* --- P-INBOX-1 --- the Inbox (/staff/inbox, and a record's email).
   Navy structure, status colours only on pills that are a status, one
   orange button (Send). The drawer sits beside the list on a wide screen
   and under it below 1200px. Prefix ibx-. */
.ibx-tiles { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.ibx-tabs .chip-n { margin-left: .2rem; }
.ibx-muted-n { color: var(--tb-muted) !important; font-weight: 600; }
.pill.ibx-pill-crit { background: #fdeceb; color: var(--tb-crit); border-color: #f5c9c6; }
.pill.ibx-pill-warn { background: #fdf4e0; color: var(--tb-warn); border-color: #ecd9a8; }
.pill.ibx-pill-good { background: #e7f5ee; color: var(--tb-good); border-color: #bfe3d0; }
.pill.ibx-pill-info { background: var(--tb-hover); color: var(--tb-navy); border-color: #cfe0ff; }
.pill.ibx-pill-new  { background: #fff; color: var(--tb-navy); border-color: #b9cdf0; }
.pill.ibx-pill-soft { background: var(--tb-sunk); color: var(--tb-muted); border-color: var(--tb-line-2); }
.ibx-work { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; align-items: start; }
@media (min-width: 1200px) {
  .ibx-work.has-drawer { grid-template-columns: minmax(0, 1fr) minmax(26rem, 34rem); }
  .ibx-work.has-drawer .ibx-drawer { position: sticky; top: .75rem; max-height: calc(100vh - 1.5rem); overflow-y: auto; }
  .ibx-work.has-drawer .ibx-c-who, .ibx-work.has-drawer .ibx-c-cat { display: none; }
}
.ibx-toolbar { display: grid; gap: .5rem; margin-bottom: .75rem; }
.ibx-views, .ibx-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.ibx-views .chip { font-weight: 600; }
.ibx-search { display: flex; flex-wrap: wrap; gap: .4rem; align-items: flex-start; }
.ibx-search > input[type=search] { flex: 1 1 14rem; max-width: 26rem; }
.ibx-more > summary { list-style: none; }
.ibx-more > summary::-webkit-details-marker { display: none; }
.ibx-more[open] { flex-basis: 100%; order: 5; }
.ibx-more-row { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .4rem; }
.ibx-more-row .form-select { width: auto; min-width: 9rem; }
.ibx-bulkbar { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; padding: .4rem .5rem; margin-bottom: .5rem;
  background: var(--tb-sunk); border-radius: var(--tb-radius); }
.ibx-bulkbar .form-select { width: auto; }
.ibx-bulkbar.is-idle { display: none; }
.ibx-table td, .ibx-table th { vertical-align: top; }
.ibx-table .pill, .ibx-d-pills .pill { white-space: nowrap; }
.ibx-table tbody tr { cursor: default; }
.ibx-table tbody tr:focus { outline: 2px solid var(--tb-navy); outline-offset: -2px; }
.ibx-table tr.ibx-open > td { background: var(--tb-hover); }
.ibx-table tr.ibx-hot > td:first-child { box-shadow: inset 3px 0 var(--tb-crit); }
.ibx-c-tick { width: 1.8rem; }
.ibx-c-src { width: 2.2rem; color: var(--tb-navy); white-space: nowrap; }
.ibx-clip { color: var(--tb-muted); margin-left: .15rem; font-size: .8rem; }
.ibx-c-when { width: 8.5rem; white-space: nowrap; }
.ibx-from { display: block; font-weight: 600; overflow-wrap: anywhere; }
.ibx-rec { display: inline-block; font-size: .8rem; color: var(--tb-navy); }
.ibx-rec.is-plain { color: var(--tb-muted); }
.ibx-subject { font-weight: 600; color: var(--tb-ink); text-decoration: none; overflow-wrap: anywhere; }
.ibx-subject:hover { color: var(--tb-navy); text-decoration: underline; }
.ibx-summary { font-size: .82rem; color: var(--tb-muted); }
.ibx-marks { display: flex; flex-wrap: wrap; gap: .5rem; font-size: .76rem; color: var(--tb-muted); }
.ibx-mobile-only { display: none; }
.ibx-wait { font-size: .74rem; color: var(--tb-muted); }
.ibx-wait.is-late, .ibx-late { color: var(--tb-crit); font-weight: 600; }
.ibx-empty { text-align: center; padding: 2rem 1rem; color: var(--tb-muted); }
.ibx-empty i { font-size: 2rem; color: var(--tb-line-2); display: block; margin-bottom: .4rem; }
.ibx-drawer { padding: 1rem; border-top: 3px solid var(--tb-navy); }
.ibx-drawer:focus { outline: none; }
.ibx-d-head { display: flex; gap: .75rem; align-items: flex-start; margin-bottom: .6rem; }
.ibx-d-subject { font-size: 1.05rem; font-weight: 700; margin: 0 0 .3rem; overflow-wrap: anywhere; }
.ibx-d-pills { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
.ibx-banner { display: flex; gap: .75rem; align-items: center; justify-content: space-between; flex-wrap: wrap;
  background: var(--tb-sunk); border-radius: var(--tb-radius); padding: .5rem .75rem; margin-bottom: .75rem; font-size: .88rem; }
.ibx-actions { display: flex; flex-wrap: wrap; gap: .35rem; align-items: flex-start; padding: .5rem 0 .75rem; border-bottom: 1px solid var(--tb-line); margin-bottom: .75rem; }
.ibx-act > summary { list-style: none; }
.ibx-act > summary::-webkit-details-marker { display: none; }
.ibx-act[open] { flex-basis: 100%; order: 10; }
.ibx-act[open] > summary { background: var(--tb-navy); color: #fff; border-color: var(--tb-navy); }
.ibx-act-form { display: grid; gap: .4rem; margin-top: .45rem; padding: .6rem; background: var(--tb-sunk); border-radius: var(--tb-radius); }
.ibx-snooze-btns { display: flex; flex-wrap: wrap; gap: .35rem; }
.ibx-d-who { margin-bottom: .75rem; }
.ibx-d-who dd { overflow-wrap: anywhere; }
.ibx-d-label { display: block; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--tb-muted); margin-bottom: .3rem; }
.ibx-d-summary { background: var(--tb-hover); border-radius: var(--tb-radius); padding: .6rem .75rem; margin-bottom: .75rem; font-size: .9rem; }
.ibx-d-body { margin-bottom: .75rem; }
.ibx-body { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--tb-ground); border: 1px solid var(--tb-line); border-radius: var(--tb-radius);
  padding: .6rem .75rem; max-height: 22rem; overflow: auto; font: .82rem/1.5 ui-monospace, Menlo, Consolas, monospace; margin: 0; }
.ibx-reply { display: grid; gap: .45rem; padding: .75rem; border: 1px solid var(--tb-line); border-radius: var(--tb-radius); margin-bottom: .75rem; }
.ibx-reply-row { display: flex; gap: .45rem; align-items: center; flex-wrap: wrap; }
.ibx-reply-row > .form-control, .ibx-reply-row > .form-select { flex: 1 1 12rem; }
.ibx-d-sec { border-top: 1px solid var(--tb-line); padding-top: .6rem; margin-top: .6rem; }
.ibx-work-row { display: flex; flex-wrap: wrap; gap: .4rem; }
.ibx-work-row .form-select { width: auto; }
.ibx-note { border-left: 3px solid var(--tb-line-2); padding: .2rem 0 .2rem .6rem; margin: .45rem 0; font-size: .88rem; }
.ibx-note-meta { font-size: .76rem; color: var(--tb-muted); }
.ibx-note-text { overflow-wrap: anywhere; }
.ibx-note-form { display: grid; gap: .35rem; margin-bottom: .5rem; }
.ibx-note-form .btn { justify-self: start; }
.ibx-fu { font-size: .88rem; margin: .2rem 0; }
.ibx-toast { position: fixed; right: 1rem; bottom: 1rem; z-index: 1080; max-width: 26rem; padding: .6rem .9rem; border-radius: var(--tb-radius);
  background: var(--tb-ink); color: #fff; box-shadow: var(--tb-shadow); font-size: .9rem; }
.ibx-toast.is-bad { background: var(--tb-crit); }
.ibx-busy { opacity: .55; pointer-events: none; }
.btn.ibx-strong { --bs-btn-color: var(--tb-navy); --bs-btn-border-color: #b9cdf0; --bs-btn-hover-bg: var(--tb-hover); --bs-btn-hover-color: var(--tb-navy); font-weight: 600; }
.ibx-settings { display: grid; gap: 1rem; }
.ibx-checks { display: grid; gap: .2rem; font-size: .85rem; align-content: end; }
.ibx-sig { border-top: 1px solid var(--tb-line); padding: .5rem 0; }
.ibx-sig-body { white-space: pre-wrap; font-size: .82rem; color: var(--tb-muted); margin: .2rem 0 0; }
.ibx-card-list { list-style: none; margin: 0; padding: 0; }
.ibx-card-list li { display: flex; gap: .6rem; align-items: flex-start; justify-content: space-between; padding: .45rem 0; border-top: 1px solid var(--tb-line); }
.ibx-card-list li.is-open { border-left: 3px solid var(--tb-navy); padding-left: .5rem; }
.ibx-card-main { min-width: 0; }
.ibx-card-acts { display: flex; gap: .3rem; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 767.98px) {
  .ibx-c-who, .ibx-c-cat, .ibx-c-src { display: none; }
  .ibx-mobile-only { display: inline; }
  .ibx-keys { display: none; }
  .ibx-qtable thead { display: none; }
  .ibx-qtable tbody tr { display: grid; grid-template-columns: 1.6rem minmax(0, 1fr) auto; gap: .1rem .5rem; padding: .5rem 0; border-bottom: 1px solid var(--tb-line); }
  .ibx-qtable tbody td { border: 0; padding: 0; }
  .ibx-qtable tbody td.ibx-c-tick { grid-row: span 3; }
  .ibx-qtable tbody td.ibx-c-when { grid-column: 2; width: auto; order: 2; }
  .ibx-qtable tbody td.ibx-c-from { grid-column: 2; order: 1; }
  .ibx-qtable tbody td.ibx-c-sum { grid-column: 2 / -1; order: 3; }
  .ibx-qtable tbody td:nth-last-child(2) { grid-column: 3; grid-row: 1; order: 0; }
  .ibx-qtable tbody td:last-child { grid-column: 3; grid-row: 2; }
  .ibx-card-list li { flex-direction: column; }
  .ibx-d-head { flex-direction: column-reverse; }
}
/* --- end P-INBOX-1 --- */

/* --- LEGACY-EVENTS (1 Oct 2026) --- lead conversation entries the old site wrote as JSON,
   shown as labelled lines under a small tag (Support\LegacyEventBody). Navy tag: it is
   structure (where the entry came from), not a status. */
.legacy-entry { margin-top: .15rem; }
.legacy-tag { display: inline-block; font-size: .68rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--tb-navy); background: var(--tb-hover); border: 1px solid #cfe0ff; border-radius: 3px; padding: 0 .4rem; margin-bottom: .25rem; }
.legacy-empty { display: block; color: var(--tb-muted); font-size: .85rem; }
.legacy-fields { display: grid; grid-template-columns: max-content 1fr; gap: .1rem .75rem; margin: 0; font-size: .9rem; }
.legacy-fields dt { font-weight: 600; color: var(--tb-muted); white-space: nowrap; }
.legacy-fields dd { margin: 0; white-space: pre-line; }
@media (max-width: 575.98px) { .legacy-fields { grid-template-columns: 1fr; } .legacy-fields dd { margin-bottom: .3rem; } }
/* --- end LEGACY-EVENTS --- */

/* --- quote numbers (Carl, 1 Oct 2026) ----------------------------------
   TBQ-<lead id>, derived in QuoteRef. Set like a reference and not like a
   word: tabular figures so two numbers line up in a list, no colour,
   because it is a label and not a state. */
.quote-ref {
  font-variant-numeric: tabular-nums; letter-spacing: .02em; font-weight: 600;
  color: var(--tb-muted); font-size: .8em; white-space: nowrap;
}
.card-heading .quote-ref { margin-left: .5rem; font-weight: 600; }
dd .quote-ref { font-size: .875rem; color: var(--tb-ink); }

/* A staff-stat that is a link (the supplier record's Leads count opens the
   lead list): looks like its siblings, says so on hover. */
a.staff-stat { text-decoration: none; color: inherit; }
a.staff-stat:hover { box-shadow: inset 0 0 0 1px var(--tb-line-2); }

/* --- P-PRICES-SCREEN: one supplier's price-update screen ---------------
   The legacy Bootstrap/supplierpriceupdates/assets/style.css, ported
   onto the tokens: the same layout (sidebar, toolbar, spreadsheet grid,
   pop-ups), the same radii and spacing, the legacy class names (spu-) so
   prices.js is a port and not a rewrite. What the legacy drew in its own
   blue is navy here where it marks structure or a selection and orange
   where it is the action (Upload CSV, Save, Create); red and green stay
   what they were -- a price going up or down, a status. Carl, 2 Oct
   2026: "I really want to maintain the look and feel". The dark theme
   below flips the tokens; nothing in this block names a colour. */
.spu-screen {
  --spu-bg: var(--tb-ground);
  --spu-card-bg: var(--tb-surface);
  --spu-border: var(--tb-line);
  --spu-text: var(--tb-ink);
  --spu-text-muted: var(--tb-muted);
  --spu-primary: var(--tb-navy);
  --spu-primary-soft: var(--tb-hover);
  --spu-up: var(--tb-crit);
  --spu-up-bg: color-mix(in srgb, var(--tb-crit) 12%, var(--tb-surface));
  --spu-down: var(--tb-good);
  --spu-down-bg: color-mix(in srgb, var(--tb-good) 12%, var(--tb-surface));
  --spu-neutral-bg: var(--tb-sunk);
  --spu-variation-bg: color-mix(in srgb, var(--tb-navy) 6%, var(--tb-surface));
  --spu-variation-border: var(--tb-navy);
  --spu-variation-text: var(--tb-navy);
  --spu-shadow: var(--tb-shadow);
  --spu-radius: .75rem;
  --spu-focus: 0 0 0 3px color-mix(in srgb, var(--tb-orange) 25%, transparent);
  color: var(--spu-text);
  margin-inline: -1rem;
  padding-inline: 1rem;
}
@media (min-width: 1200px) { .spu-screen { margin-inline: -1.5rem; padding-inline: 1.5rem; } }

.spu-header {
  border-bottom: 1px solid var(--spu-border);
  background: var(--spu-card-bg);
  margin: -1.25rem -1rem 0;
  padding: 0 1rem;
}
@media (min-width: 1200px) { .spu-header { margin-inline: -1.5rem; padding-inline: 1.5rem; } }
.spu-header-inner {
  max-width: 1460px; margin: 0 auto; padding: 18px 0;
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.spu-header-inner h1 { margin: 0; font-size: 1.375rem; letter-spacing: -0.01em; }
.spu-supplier-name { margin: 2px 0 0; color: var(--spu-text-muted); font-size: 0.95rem; }
.spu-counts { font-size: .85rem; }
.spu-user { color: var(--spu-text-muted); font-size: 0.85rem; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.spu-theme-toggle {
  background: var(--spu-neutral-bg); border: 1px solid var(--spu-border); color: var(--spu-text);
  border-radius: 999px; padding: 5px 12px; font-size: 0.8rem; cursor: pointer; white-space: nowrap;
  text-decoration: none; display: inline-block; line-height: 1.4;
}
.spu-theme-toggle:hover { border-color: var(--spu-primary); color: var(--spu-text); }
.spu-theme-toggle.spu-has-pending { border-color: var(--spu-primary); color: var(--spu-primary); font-weight: 600; }

.spu-main { max-width: 1460px; margin: 0 auto; padding: 24px 0 80px; }

/* --- Category sidebar layout --- */
.spu-layout { display: flex; gap: 20px; align-items: flex-start; }
.spu-content { flex: 1; min-width: 0; }
.spu-sidebar {
  width: 280px; flex-shrink: 0; position: sticky; top: 12px; max-height: calc(100vh - 24px); overflow-y: auto;
  background: var(--spu-card-bg); border: 1px solid var(--spu-border); border-radius: var(--spu-radius);
  box-shadow: var(--spu-shadow); padding: 12px;
}
.spu-sidebar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 0 4px; }
.spu-sidebar-head h2 { margin: 0; font-size: 0.95rem; }

.spu-cat-item, .spu-cat-row {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px; padding-left: calc(8px + var(--depth, 0) * 14px);
  border-radius: 8px; font-size: 0.88rem; cursor: pointer; user-select: none;
}
.spu-cat-item:hover, .spu-cat-row:hover { background: var(--spu-neutral-bg); }
.spu-cat-item.spu-cat-selected, .spu-cat-row.spu-cat-selected { background: var(--spu-primary-soft); color: var(--spu-primary); font-weight: 600; }
.spu-cat-all { font-weight: 600; }
.spu-cat-row.spu-cat-master > .spu-cat-name { color: var(--spu-text-muted); }
.spu-cat-row.spu-cat-selected > .spu-cat-name { color: inherit; }
.spu-cat-name { flex: 1; overflow-wrap: anywhere; }
.spu-cat-toggle {
  background: none; border: none; color: var(--spu-text-muted); cursor: pointer; padding: 0; width: 16px; flex-shrink: 0;
  font-size: 0.8rem; line-height: 1; transition: transform 0.12s ease;
}
.spu-cat-toggle-open { transform: rotate(90deg); }
.spu-cat-toggle-leaf { visibility: hidden; }
.spu-cat-actions { display: none; flex-shrink: 0; gap: 2px; }
.spu-cat-row:hover .spu-cat-actions, .spu-cat-row.spu-cat-selected .spu-cat-actions { display: inline-flex; }
.spu-cat-action { background: none; border: none; color: var(--spu-text-muted); cursor: pointer; padding: 0 3px; font-size: 0.8rem; line-height: 1; border-radius: 4px; }
.spu-cat-action:hover { background: var(--spu-border); color: var(--spu-text); }
.spu-cat-delete:hover { color: var(--spu-up); }
.spu-cat-count { margin-left: auto; flex-shrink: 0; background: var(--spu-neutral-bg); border-radius: 999px; font-size: 0.72rem; padding: 1px 7px; color: var(--spu-text-muted); }
.spu-cat-count-zero { opacity: 0.5; }
.spu-cat-count:empty { display: none; }

.spu-cat-chip {
  display: inline-flex; align-items: center; gap: 6px; background: var(--spu-primary-soft); color: var(--spu-primary);
  border-radius: 999px; padding: 5px 12px; font-size: 0.8rem; font-weight: 600; white-space: nowrap; max-width: 260px; overflow: hidden; text-overflow: ellipsis;
}
.spu-cat-chip[hidden] { display: none; }
.spu-cat-chip-x { background: none; border: none; color: inherit; cursor: pointer; font-size: 1rem; line-height: 1; padding: 0; }

.spu-table tbody tr.spu-cat-banner td { background: var(--spu-neutral-bg); border-top: 2px solid var(--spu-border); padding: 10px 14px; }
.spu-cat-banner-name { font-weight: 700; font-size: 0.95rem; }
.spu-cat-banner-path { font-size: 0.75rem; color: var(--spu-text-muted); margin-top: 2px; }

#spu-sidebar-toggle { display: none; }
@media (max-width: 900px) {
  .spu-layout { flex-direction: column; }
  .spu-sidebar { position: static; width: 100%; max-height: 40vh; display: none; }
  .spu-sidebar.spu-sidebar-open { display: block; }
  #spu-sidebar-toggle { display: inline-block; }
  .spu-content { width: 100%; }
}

.spu-toolbar {
  background: var(--spu-card-bg); border: 1px solid var(--spu-border); border-radius: var(--spu-radius); box-shadow: var(--spu-shadow);
  padding: 16px 20px; margin-bottom: 12px; display: flex; flex-direction: column; gap: 12px;
}
.spu-toolbar-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.spu-toolbar-dates { border-top: 1px solid var(--spu-border); padding-top: 12px; }
#spu-search {
  flex: 1 1 260px; font-size: 1rem; padding: 11px 16px; border-radius: 8px; border: 1px solid var(--spu-border);
  background: var(--spu-bg); color: var(--spu-text); outline: none; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
#spu-search:focus { border-color: var(--tb-orange); box-shadow: var(--spu-focus); }
.spu-checkbox { display: flex; align-items: center; gap: 6px; font-size: 0.88rem; color: var(--spu-text-muted); white-space: nowrap; cursor: pointer; margin: 0; }
.spu-checkbox input { cursor: pointer; }
.spu-toolbar-hint { font-size: 0.82rem; color: var(--spu-text-muted); }
.spu-hint { color: var(--spu-text-muted); font-size: 0.9rem; margin: 8px 4px 24px; }
.spu-hint code { font-size: .8em; background: var(--spu-neutral-bg); padding: 0 .25rem; border-radius: .2rem; color: var(--spu-text); }
.spu-muted { color: var(--spu-text-muted); }

.spu-field { display: flex; flex-direction: column; gap: 4px; }
.spu-field label { font-size: 0.72rem; color: var(--spu-text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.spu-field input, .spu-field select, .spu-batch-acts input {
  border: 1px solid var(--spu-border); border-radius: 8px; padding: 8px 10px; font-size: 0.9rem; background: var(--spu-bg); color: var(--spu-text); max-width: 100%;
}
.spu-field input:focus, .spu-field select:focus, .spu-batch-acts input:focus { outline: none; border-color: var(--tb-orange); box-shadow: var(--spu-focus); }
.spu-modal-small { max-width: 460px; }
.spu-modal-body .spu-field { margin-bottom: 12px; }
#spu-dates-use-global-wrap { margin-bottom: 12px; white-space: normal; }
.spu-modal-actions { margin-top: 16px; display: flex; justify-content: flex-end; }

/* --- Spreadsheet table --- */
.spu-table-wrap { background: var(--spu-card-bg); border: 1px solid var(--spu-border); border-radius: var(--spu-radius); box-shadow: var(--spu-shadow); overflow-x: auto; }
.spu-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.spu-table thead th {
  position: sticky; top: 0; background: var(--spu-card-bg); border-bottom: 1px solid var(--spu-border); text-align: left; padding: 10px 14px;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--spu-text-muted); z-index: 1; font-weight: 600;
}
.spu-table td { padding: 8px 14px; border-bottom: 1px solid var(--spu-border); vertical-align: middle; }
.spu-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--spu-text) 3%, transparent); }
.spu-table tbody tr:hover { background: var(--spu-primary-soft); }
.spu-table tbody tr.spu-group-start td { border-top: 2px solid var(--spu-border); }
.spu-table tbody tr.spu-group-header { background: var(--spu-neutral-bg); border-top: 2px solid var(--spu-border); }
.spu-table tbody tr.spu-group-header td { padding: 7px 14px; font-size: 0.88rem; color: var(--spu-text); }
.spu-table tbody tr.spu-row-variation { background: var(--spu-variation-bg); box-shadow: inset 3px 0 0 var(--spu-variation-border); }
.spu-table tbody tr.spu-row-variation:hover { background: var(--spu-primary-soft); }
.spu-row-variation .spu-col-sku { color: var(--spu-variation-text); }
.spu-row-variation .spu-name-text { color: var(--spu-variation-text); padding-left: 18px; position: relative; }
.spu-row-variation .spu-name-text::before { content: "↳"; position: absolute; left: 0; opacity: 0.7; }
.spu-sortable { cursor: pointer; user-select: none; }
.spu-sortable:hover { color: var(--spu-text); }
.spu-sort-active { color: var(--spu-primary); }
.spu-sort-arrow { font-size: 0.7rem; }
.spu-col-select { width: 34px; text-align: center; }
.spu-col-select input { cursor: pointer; }
.spu-col-sku { white-space: nowrap; color: var(--spu-text-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.spu-col-name { min-width: 220px; }
.spu-name-text { font-weight: 500; }
.spu-type { display: inline-block; margin-left: .35rem; font-size: .7rem; color: var(--spu-text-muted); border: 1px solid var(--spu-border); border-radius: 1rem; padding: 0 .4rem; font-weight: 400; }
.spu-col-price { white-space: nowrap; min-width: 120px; font-variant-numeric: tabular-nums; }
.spu-col-diff { white-space: nowrap; width: 90px; }
.spu-col-action { white-space: nowrap; min-width: 170px; }
.spu-unit { color: var(--spu-text-muted); font-size: 0.82rem; }
.spu-valid-until { color: var(--spu-text-muted); font-size: 0.75rem; }
.spu-ends-soon { color: var(--tb-warn); font-weight: 600; }
.spu-no-price { color: var(--spu-text-muted); font-style: italic; }
.spu-price-expired { color: var(--spu-up); font-weight: 600; }
.spu-price-expired-label { color: var(--spu-up); }
.spu-new-price { width: 110px; border: 1px solid var(--spu-border); border-radius: 8px; padding: 6px 8px; font-size: 0.9rem; background: var(--spu-bg); color: var(--spu-text); }
.spu-new-price:focus { outline: none; border-color: var(--tb-orange); box-shadow: var(--spu-focus); }
.spu-new-price:disabled { opacity: 0.5; }
.spu-diff { font-weight: 700; font-size: 0.88rem; padding: 5px 8px; border-radius: 6px; text-align: center; background: var(--spu-neutral-bg); color: var(--spu-text-muted); }
.spu-diff-up { background: var(--spu-up-bg); color: var(--spu-up); }
.spu-diff-down { background: var(--spu-down-bg); color: var(--spu-down); }
.spu-diff-neutral { background: var(--spu-neutral-bg); color: var(--spu-text-muted); }
.spu-pending { font-size: 0.82rem; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.spu-edit-dates { background: none; border: none; color: var(--spu-text-muted); cursor: pointer; padding: 0 3px; font-size: 0.8rem; line-height: 1; border-radius: 4px; }
.spu-edit-dates:hover { background: var(--spu-border); color: var(--spu-primary); }
.spu-future { font-size: 0.8rem; color: var(--spu-text-muted); display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 3px; }
.spu-future-badge { background: var(--spu-down-bg); color: var(--spu-down); font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; padding: 2px 7px; border-radius: 999px; }
.spu-pending-badge { background: var(--spu-primary); color: #fff; font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; padding: 2px 7px; border-radius: 999px; }
.spu-save-status { font-size: 0.8rem; font-weight: 600; padding: 4px 10px; border-radius: 999px; display: inline-block; }
.spu-save-status-saving { background: var(--spu-neutral-bg); color: var(--spu-text-muted); }
.spu-save-status-saved { background: var(--spu-down-bg); color: var(--spu-down); }
.spu-save-status-error { background: var(--spu-up-bg); color: var(--spu-up); }

.spu-btn { border: none; border-radius: 8px; padding: 7px 14px; font-size: 0.85rem; font-weight: 600; cursor: pointer; text-decoration: none; display: inline-block; transition: background-color 0.15s ease; line-height: 1.4; }
.spu-btn-primary { background: var(--tb-orange); color: #1b1b1b; }
.spu-btn-primary:hover { background: #e56f00; color: #1b1b1b; }
.spu-btn-primary:disabled { background: #f9b77a; color: #5a3a1a; cursor: default; }
.spu-btn-secondary { background: var(--spu-neutral-bg); color: var(--spu-text); white-space: nowrap; }
.spu-btn-secondary:hover { background: var(--spu-border); }
.spu-btn-cancel { background: transparent; color: var(--spu-text-muted); border: 1px solid var(--spu-border); padding: 3px 9px; font-size: 0.78rem; }
.spu-btn-cancel:hover { color: var(--spu-up); border-color: var(--spu-up); }
.spu-btn:focus-visible, .spu-theme-toggle:focus-visible { outline: 2px solid var(--tb-orange); outline-offset: 2px; }
.spu-line-msg { margin-top: 4px; font-size: 0.78rem; }
.spu-msg-ok { color: var(--spu-down); }
.spu-msg-error { color: var(--spu-up); }

/* --- Pop-ups --- */
.spu-modal-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); display: flex; align-items: flex-start; justify-content: center; padding: 5vh 20px; z-index: 1050; }
.spu-modal-backdrop[hidden] { display: none; }
.spu-modal { background: var(--spu-card-bg); border: 1px solid var(--spu-border); border-radius: var(--spu-radius); box-shadow: var(--spu-shadow); width: 100%; max-width: 900px; max-height: 90vh; display: flex; flex-direction: column; overflow: hidden; color: var(--spu-text); }
.spu-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--spu-border); }
.spu-modal-header h2 { margin: 0; font-size: 1.1rem; }
.spu-modal-body { padding: 16px 20px 20px; overflow-y: auto; }
.spu-history-action { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; padding: 2px 8px; border-radius: 999px; display: inline-block; }
.spu-history-action-save { background: var(--spu-neutral-bg); color: var(--spu-primary); }
.spu-history-action-cancel { background: var(--spu-up-bg); color: var(--spu-up); }
.spu-history-action-activate { background: var(--spu-down-bg); color: var(--spu-down); }
.spu-history-staff-tag { font-size: 0.7rem; color: var(--spu-text-muted); margin-left: 4px; }
.spu-history-note { font-size: .75rem; color: var(--spu-text-muted); margin-top: 2px; }
.spu-recent-head { font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--spu-text-muted); margin: 18px 0 6px; }
.spu-recent { list-style: none; padding: 0; margin: 0; font-size: .85rem; }
.spu-recent li { padding: 2px 0; }

.spu-batch { border: 1px solid var(--spu-border); border-left: 3px solid var(--spu-primary); border-radius: 8px; padding: 8px 12px; margin-bottom: 10px; }
.spu-batch > summary { cursor: pointer; font-size: .9rem; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.spu-batch[open] > summary { margin-bottom: 8px; }
.spu-batch-table td, .spu-batch-table th { padding: 6px 10px; }
.spu-batch-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 10px; }
.spu-inline-form { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
.spu-batch-acts input { padding: 5px 8px; font-size: .85rem; }

/* --- Product add/edit modal --- */
.spu-modal-wide { max-width: 860px; }
.spu-edit-product { display: inline-block; background: var(--spu-primary); color: #fff; border: none; border-radius: 6px; padding: 1px 8px; font-size: 0.72rem; font-weight: 600; cursor: pointer; vertical-align: middle; margin-left: 4px; }
.spu-edit-product:hover { background: var(--tb-navy-2); }
.spu-prod-cols { display: flex; gap: 20px; flex-wrap: wrap; }
.spu-prod-cols > .spu-prod-section { flex: 1 1 320px; }
.spu-prod-section { border-top: 1px solid var(--spu-border); padding-top: 14px; margin-top: 14px; }
.spu-prod-cols, .spu-prod-cols > .spu-prod-section { border-top: none; margin-top: 0; padding-top: 0; }
.spu-prod-section h3 { margin: 0 0 10px; font-size: 0.95rem; }
.spu-prod-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.spu-prod-section-head h3 { margin: 0; }
.spu-prod-note { margin-top: 10px; background: var(--spu-neutral-bg); border-radius: 8px; padding: 8px 12px; font-size: 0.82rem; color: var(--spu-text-muted); }
.spu-prod-image { display: flex; flex-direction: column; gap: 8px; }
#spu-prod-image-preview { max-width: 160px; max-height: 160px; border: 1px solid var(--spu-border); border-radius: 8px; object-fit: contain; background: var(--spu-bg); }
.spu-prod-price-row { display: flex; gap: 12px; flex-wrap: wrap; }
.spu-prod-price-row .spu-field { flex: 1 1 130px; }
.spu-prod-price-row input:disabled, .spu-prod-price-row input:read-only { opacity: 0.6; }
.spu-prod-cats-hint { margin: 8px 0 0; }
.spu-modal-body textarea, .spu-modal-body input[type="text"], .spu-modal-body input[type="number"], .spu-modal-body input[type="file"] {
  border: 1px solid var(--spu-border); border-radius: 8px; padding: 8px 10px; font-size: 0.9rem; background: var(--spu-bg); color: var(--spu-text); width: 100%; font-family: inherit;
}
.spu-modal-body textarea { resize: vertical; }
.spu-modal-body textarea:focus, .spu-modal-body input[type="text"]:focus, .spu-modal-body input[type="number"]:focus { outline: none; border-color: var(--tb-orange); box-shadow: var(--spu-focus); }
.spu-variation { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.spu-variation .spu-var-code { flex: 1 1 120px; }
.spu-variation .spu-var-name { flex: 2 1 180px; }
.spu-variation .spu-var-unit { flex: 1 1 90px; }
.spu-variation .spu-var-price { flex: 1 1 90px; }
.spu-variation .spu-checkbox { flex: 0 0 auto; }
.spu-spec-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px 16px; }
.spu-spec-field { display: flex; flex-direction: column; gap: 3px; }
.spu-spec-field[hidden] { display: none; }
.spu-spec-field label { font-size: 0.72rem; color: var(--spu-text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.spu-catcheck-row { display: flex; align-items: center; gap: 6px; padding: 3px 0; padding-left: calc(var(--depth, 0) * 18px); font-size: 0.88rem; cursor: pointer; margin: 0; }
.spu-catcheck-row input { cursor: pointer; }
.spu-catcheck-row .spu-cat-master { color: var(--spu-text-muted); }
/* --- end P-PRICES-SCREEN --- */

/* --- DARK THEME (the staff area) -----------------------------------------
   Carl, 2 Oct 2026: "can we have the light dark theme here too - backend
   wide?" The staff layout sets html[data-theme] from the browser's saved
   choice (the 🌓 button in the bar) or, with none saved, from the
   operating system's preference; the member and supplier layouts never
   set it, so this block cannot reach a customer's screen. It flips the
   tokens, and with them everything that reads a token -- which is almost
   everything. Where a screen names a light colour outright (a pill, a
   note box) the rules below re-tint the common cases; a patch of white on
   a dark screen is one such rule still to add, not a different theme. */
html[data-theme="dark"] {
  --tb-ground:   #0f141b;
  --tb-surface:  #171d26;
  --tb-sunk:     #1f2733;
  --tb-ink:      #e6edf3;
  --tb-muted:    #9aa6b4;
  --tb-faint:    #6f7b89;
  --tb-line:     #2a3440;
  --tb-line-2:   #3a4654;
  --tb-hover:    #1d2a3d;
  --tb-navy:     #4f8cff;
  --tb-navy-2:   #6ea1ff;
  --tb-orange-ink: #ffa050;
  --tb-orange-soft: #3a2a18;
  --tb-crit:     #ff8a80;
  --tb-warn:     #e2b654;
  --tb-good:     #4ade80;
  --tb-shadow:   0 1px 2px rgba(0,0,0,.4), 0 1px 3px rgba(0,0,0,.5);
  --bs-body-bg: var(--tb-ground);
  --bs-body-color: var(--tb-ink);
  --bs-border-color: var(--tb-line);
  --bs-tertiary-bg: var(--tb-sunk);
  --bs-secondary-bg: var(--tb-sunk);
  --bs-secondary-color: var(--tb-muted);
  --bs-emphasis-color: var(--tb-ink);
  --bs-link-color: var(--tb-navy);
  --bs-link-hover-color: var(--tb-navy-2);
  color-scheme: dark;
}
html[data-theme="dark"] body { background: var(--tb-ground); color: var(--tb-ink); }
html[data-theme="dark"] .app-navbar, html[data-theme="dark"] .app-navbar--staff,
html[data-theme="dark"] body:has(.app-navbar--staff) .entity-bar, html[data-theme="dark"] .entity-bar { background: #0b2b63; }
html[data-theme="dark"] .entity-bar a { color: #cfe0ff; }
html[data-theme="dark"] .text-muted, html[data-theme="dark"] .form-text { color: var(--tb-muted) !important; }
html[data-theme="dark"] .form-control, html[data-theme="dark"] .form-select, html[data-theme="dark"] .form-check-input:not(:checked), html[data-theme="dark"] textarea {
  background-color: var(--tb-ground); color: var(--tb-ink); border-color: var(--tb-line-2);
}
html[data-theme="dark"] .form-control::placeholder { color: var(--tb-faint); }
html[data-theme="dark"] .form-control:disabled, html[data-theme="dark"] .form-select:disabled { background-color: var(--tb-sunk); }
html[data-theme="dark"] .form-select { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239aa6b4' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); }
html[data-theme="dark"] .table { --bs-table-bg: transparent; --bs-table-color: var(--tb-ink); --bs-table-border-color: var(--tb-line); --bs-table-striped-bg: rgba(255,255,255,.03); --bs-table-hover-bg: var(--tb-hover); --bs-table-hover-color: var(--tb-ink); color: var(--tb-ink); }
html[data-theme="dark"] .dropdown-menu { --bs-dropdown-bg: var(--tb-surface); --bs-dropdown-color: var(--tb-ink); --bs-dropdown-link-color: var(--tb-ink); --bs-dropdown-link-hover-bg: var(--tb-hover); --bs-dropdown-link-hover-color: var(--tb-ink); --bs-dropdown-border-color: var(--tb-line); --bs-dropdown-header-color: var(--tb-muted); --bs-dropdown-divider-bg: var(--tb-line); }
html[data-theme="dark"] .dropdown-item.active, html[data-theme="dark"] .dropdown-item:active { background: var(--tb-hover); color: var(--tb-ink); }
html[data-theme="dark"] .modal-content, html[data-theme="dark"] .card, html[data-theme="dark"] .list-group-item, html[data-theme="dark"] .offcanvas { background: var(--tb-surface); color: var(--tb-ink); border-color: var(--tb-line); }
html[data-theme="dark"] .alert { --bs-alert-bg: var(--tb-sunk); --bs-alert-color: var(--tb-ink); --bs-alert-border-color: var(--tb-line-2); }
html[data-theme="dark"] .alert-success { --bs-alert-color: var(--tb-good); --bs-alert-border-color: var(--tb-good); }
html[data-theme="dark"] .alert-danger  { --bs-alert-color: var(--tb-crit); --bs-alert-border-color: var(--tb-crit); }
html[data-theme="dark"] .alert-warning { --bs-alert-color: var(--tb-warn); --bs-alert-border-color: var(--tb-warn); }
html[data-theme="dark"] .alert-info    { --bs-alert-color: var(--tb-navy); --bs-alert-border-color: var(--tb-navy); }
html[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%); }
html[data-theme="dark"] .btn-outline-secondary, html[data-theme="dark"] .btn-outline-primary, html[data-theme="dark"] .btn-light { --bs-btn-bg: var(--tb-surface); --bs-btn-color: var(--tb-ink); --bs-btn-hover-bg: var(--tb-hover); --bs-btn-hover-color: var(--tb-ink); --bs-btn-border-color: var(--tb-line-2); }
html[data-theme="dark"] .btn-outline-primary { --bs-btn-color: var(--tb-orange-ink); --bs-btn-hover-color: var(--tb-orange-ink); }
html[data-theme="dark"] .bg-white, html[data-theme="dark"] .bg-light { background-color: var(--tb-surface) !important; }
html[data-theme="dark"] .text-dark, html[data-theme="dark"] .text-body { color: var(--tb-ink) !important; }
html[data-theme="dark"] .border, html[data-theme="dark"] .border-top, html[data-theme="dark"] .border-bottom, html[data-theme="dark"] hr { border-color: var(--tb-line) !important; }
html[data-theme="dark"] .chip:hover { border-color: var(--tb-line-2); }
html[data-theme="dark"] .chip.is-on { color: #0f141b; }
html[data-theme="dark"] .chip.is-on .chip-n { color: #0f141b; }
/* Status pills and soft boxes name their light tints; re-tint them from the status colour. */
html[data-theme="dark"] .pill:not(.chip-orange)[class*="pill-"], html[data-theme="dark"] .pill[class*="pill--"] { background: color-mix(in srgb, currentColor 16%, transparent); border-color: color-mix(in srgb, currentColor 35%, transparent); }
html[data-theme="dark"] .msg-tb, html[data-theme="dark"] .pp-problems { background: color-mix(in srgb, var(--tb-warn) 14%, var(--tb-surface)); border-color: color-mix(in srgb, var(--tb-warn) 40%, transparent); }
html[data-theme="dark"] .msg-me { border-color: var(--tb-line-2); }
html[data-theme="dark"] .pwned-banner { background: color-mix(in srgb, var(--tb-crit) 14%, var(--tb-surface)); }
html[data-theme="dark"] img.spu-img, html[data-theme="dark"] .section-card img:not(.keep-bright) { opacity: .92; }
html[data-theme="dark"] .spu-modal-backdrop { background: rgba(0,0,0,.65); }
html[data-theme="dark"] .spu-btn-primary:disabled { background: #6b4a2a; color: #cbb39a; }
/* The 🌓 button in the staff bar. */
.theme-toggle { --bs-btn-border-color: rgba(255,255,255,.35); font-size: .82rem; }
/* --- end DARK THEME --- */

/* --- NM shop: the category tree and the photo pop-up (Carl, 2 Oct 2026) --- */
.nms-shelf-grid { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: 1rem; align-items: start; }
.nms-cats { font-size: .86rem; border: 1px solid var(--tb-line); border-radius: var(--tb-radius); padding: .5rem .4rem; max-height: 70vh; overflow-y: auto; position: sticky; top: .75rem; }
.nms-cats-head { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tb-muted); padding: .1rem .5rem .4rem; }
.nms-cat-branch > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .3rem; }
.nms-cat-branch > summary::-webkit-details-marker { display: none; }
.nms-cat-branch > summary::before { content: "\25B8"; color: var(--tb-muted); font-size: .75rem; width: .8rem; flex: 0 0 auto; transition: transform .12s ease; }
.nms-cat-branch[open] > summary::before { transform: rotate(90deg); }
.nms-cat-leaf { padding-left: 1.1rem; }
.nms-cat-kids { padding-left: .9rem; border-left: 1px solid var(--tb-line); margin-left: .35rem; }
.nms-cat-link { display: flex; align-items: center; gap: .4rem; padding: .25rem .5rem; border-radius: .4rem; color: var(--tb-ink); text-decoration: none; flex: 1 1 auto; min-width: 0; }
.nms-cat-link:hover { background: var(--tb-hover); color: var(--tb-navy); }
.nms-cat-link.is-on { background: var(--tb-hover); color: var(--tb-navy); font-weight: 600; }
.nms-cat-n { margin-left: auto; font-size: .7rem; color: var(--tb-muted); background: var(--tb-sunk); border-radius: 1rem; padding: 0 .4rem; font-variant-numeric: tabular-nums; }
@media (max-width: 767.98px) { .nms-shelf-grid { grid-template-columns: minmax(0, 1fr); } .nms-cats { position: static; max-height: 14rem; } }
.nms-img-btn { background: none; border: 0; padding: 0; cursor: zoom-in; display: block; border-radius: .3rem; }
.nms-img-btn:focus-visible { outline: 2px solid var(--tb-orange); outline-offset: 2px; }
.lb-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: flex; align-items: center; justify-content: center; padding: 1rem; z-index: 1060; }
.lb-backdrop[hidden] { display: none; }
.lb-card { background: var(--tb-surface); color: var(--tb-ink); border-radius: var(--tb-radius); box-shadow: 0 8px 30px rgba(0,0,0,.4); max-width: min(92vw, 60rem); max-height: 92vh; display: flex; flex-direction: column; overflow: hidden; }
.lb-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .6rem .9rem; border-bottom: 1px solid var(--tb-line); font-weight: 600; font-size: .9rem; }
.lb-close { background: var(--tb-sunk); color: var(--tb-ink); border: 1px solid var(--tb-line); border-radius: .4rem; padding: .25rem .7rem; font-size: .85rem; cursor: pointer; }
.lb-img { display: block; max-width: 100%; max-height: calc(92vh - 3.5rem); object-fit: contain; margin: 0 auto; background: var(--tb-sunk); }
/* --- end NM shop tree / pop-up --- */

/* --- P-NEWS: the Newsletters tool (Carl, 2 Oct 2026) -------------------
   The legacy Bootstrap/Newsletters pages' own CSS -- the white cards with
   a 12px radius and soft shadow, the uppercase table headings, the
   status and source tags, the stat tiles, the two-tab recipient picker,
   the attachment list -- ported onto the tokens so the dark theme flips
   it. The legacy's navy buttons are orange here where they are THE
   action (Compose, Save & Preview, Upload & Continue, Continue to
   Recipients, Review & Confirm, Confirm) and quiet where they are not;
   its red Confirm and Cancel are orange and outline, because red is a
   status in this system. The status tags keep their meaning: Draft
   amber, Sent green, Scheduled and Sending navy. Carl: "maintain the
   exact same look and feel". */
.nl-bar { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.nl-bar .page-head { margin-bottom: 0; }
.nl-bar-who { font-weight: 400; color: var(--tb-muted); font-size: 1.05rem; }
.nl-bar-actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; padding-top: .15rem; }
.nl-quiet { font-size: .8rem; color: var(--tb-muted); text-decoration: none; }
.nl-quiet:hover { color: var(--tb-ink); text-decoration: underline; }
.nl-btn { font-weight: 600; border-radius: 6px; padding: .6rem 1.15rem; }
.nl-btn--sm { padding: .5rem .9rem; font-size: .85rem; }
.nl-btn--review { margin-top: 1rem; padding: .7rem 1.4rem; }
.nl-btn--confirm { width: 100%; margin-top: 1.25rem; padding: .75rem 1.5rem; font-weight: 700; }
.nl-btn-sm { padding: 5px 12px; font-size: .78rem; border: 1.5px solid var(--tb-line-2); border-radius: 5px; background: var(--tb-sunk); color: var(--tb-ink); cursor: pointer; font-weight: 600; }
.nl-btn-sm:hover { background: var(--tb-hover); }
.nl-btn-xs { font-size: .78rem; border: 1px solid var(--tb-line-2); background: var(--tb-surface); color: var(--tb-ink); border-radius: 6px; padding: 5px 10px; cursor: pointer; margin-left: 6px; }
.nl-btn-xs:hover { background: var(--tb-hover); }

.nl-card { background: var(--tb-surface); border-radius: 12px; box-shadow: 0 2px 16px rgba(0,0,0,.10); overflow: hidden; color: var(--tb-ink); }
html[data-theme="dark"] .nl-card { box-shadow: var(--tb-shadow); border: 1px solid var(--tb-line); }
.nl-card--form { padding: 2rem; margin: 0 auto; overflow: visible; }
.nl-card--720 { max-width: 720px; }
.nl-card--560 { max-width: 560px; }
.nl-card--480 { max-width: 480px; }
.nl-card--460 { max-width: 460px; }
.nl-h1 { font-size: 1.25rem; font-weight: 700; margin: 0 0 1.25rem; color: var(--tb-ink); }
.nl-h1--sm { font-size: 1.125rem; margin-bottom: .25rem; }
.nl-h2 { font-size: .95rem; font-weight: 700; margin: 1.5rem 0 .6rem; color: var(--tb-ink); }
.nl-sub { font-size: .78rem; color: var(--tb-muted); margin-bottom: 1.25rem; }
.nl-sub--tight { margin-bottom: 0; }
.nl-back { color: var(--tb-navy); font-size: .82rem; text-decoration: none; }
.nl-back:hover { text-decoration: underline; }
.nl-back--bold { font-weight: 600; }
.nl-muted { color: var(--tb-muted); }
.nl-faint { color: var(--tb-faint); }
.nl-empty { padding: 2.5rem; text-align: center; color: var(--tb-muted); }
.nl-empty-row { color: var(--tb-faint); padding: 1rem; }

.nl-table { width: 100%; border-collapse: collapse; }
.nl-table th, .nl-table td { text-align: left; padding: 12px 16px; font-size: .82rem; border-bottom: 1px solid var(--tb-line); vertical-align: middle; }
.nl-table th { color: var(--tb-muted); font-weight: 600; font-size: .74rem; text-transform: uppercase; letter-spacing: .02em; }
.nl-table tr:last-child td { border-bottom: none; }
.nl-table--tight th, .nl-table--tight td { padding: 8px 16px; }
.nl-table .nl-cb { width: 24px; }
.nl-no { color: var(--tb-faint); font-size: .74rem; font-variant-numeric: tabular-nums; }
.nl-links { white-space: nowrap; }
.nl-link { color: var(--tb-navy); text-decoration: none; font-weight: 600; }
.nl-link:hover { text-decoration: underline; }

.nl-status { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: .76rem; font-weight: 600; }
.nl-status--draft     { background: color-mix(in srgb, var(--tb-warn) 16%, var(--tb-surface)); color: var(--tb-warn); }
.nl-status--sent      { background: color-mix(in srgb, var(--tb-good) 16%, var(--tb-surface)); color: var(--tb-good); }
.nl-status--scheduled { background: color-mix(in srgb, var(--tb-navy) 14%, var(--tb-surface)); color: var(--tb-navy); }
.nl-status--sending   { background: color-mix(in srgb, var(--tb-navy) 14%, var(--tb-surface)); color: var(--tb-navy); outline: 1px dashed var(--tb-navy); outline-offset: -1px; }
.nl-sched-time { font-size: .7rem; color: var(--tb-muted); display: block; margin-top: 2px; }

.nl-tag { font-size: .7rem; padding: 2px 6px; border-radius: 4px; font-weight: 600; white-space: nowrap; }
.nl-tag--yes       { background: color-mix(in srgb, var(--tb-good) 16%, var(--tb-surface)); color: var(--tb-good); }
.nl-tag--no        { background: var(--tb-sunk); color: var(--tb-muted); }
.nl-tag--err       { background: color-mix(in srgb, var(--tb-crit) 14%, var(--tb-surface)); color: var(--tb-crit); }
.nl-tag--date      { background: color-mix(in srgb, var(--tb-warn) 16%, var(--tb-surface)); color: var(--tb-warn); }
.nl-tag--click     { background: color-mix(in srgb, var(--tb-navy) 14%, var(--tb-surface)); color: var(--tb-navy); }
.nl-tag--bounce    { background: color-mix(in srgb, var(--tb-crit) 14%, var(--tb-surface)); color: var(--tb-crit); }
.nl-tag--complaint { background: color-mix(in srgb, var(--tb-crit) 22%, var(--tb-surface)); color: var(--tb-crit); }
.nl-tag--unknown   { background: var(--tb-sunk); color: var(--tb-muted); }

.nl-alert { border-radius: 6px; padding: 10px 14px; font-size: .82rem; margin-bottom: 1.1rem; }
.nl-alert--err  { background: color-mix(in srgb, var(--tb-crit) 10%, var(--tb-surface)); color: var(--tb-crit); border: 1px solid color-mix(in srgb, var(--tb-crit) 40%, var(--tb-surface)); }
.nl-alert--warn { background: color-mix(in srgb, var(--tb-warn) 12%, var(--tb-surface)); color: var(--tb-warn); border: 1px solid color-mix(in srgb, var(--tb-warn) 45%, var(--tb-surface)); }
.nl-alert--ok   { background: color-mix(in srgb, var(--tb-good) 12%, var(--tb-surface)); color: var(--tb-good); border: 1px solid color-mix(in srgb, var(--tb-good) 40%, var(--tb-surface)); }
.nl-alert a { color: inherit; font-weight: 600; }
.nl-result { font-size: .82rem; margin: .5rem 0 1rem; color: var(--tb-good); }
.nl-result--err { color: var(--tb-crit); }

.nl-field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 1rem; }
.nl-field > label { font-size: .76rem; font-weight: 600; color: var(--tb-ink); }
.nl-field input[type=text], .nl-field input[type=email], .nl-field input[type=file] {
  border: 1.5px solid var(--tb-line-2); border-radius: 6px; padding: 9px 12px; font-size: .9rem;
  background: var(--tb-surface); color: var(--tb-ink); width: 100%;
}
.nl-field input:focus { outline: none; border-color: var(--tb-orange); }
.nl-hint { font-size: .76rem; color: var(--tb-muted); margin-top: 4px; }
.nl-hint--indent { margin-left: 26px; }
.nl-radio-row { display: flex; gap: 18px; flex-wrap: wrap; }
.nl-radio-row label { display: flex; align-items: center; gap: 6px; font-size: .84rem; font-weight: 400; cursor: pointer; }

/* The editor: Quill's snow theme inside the legacy frame, on the tokens. */
.nl-editor { border: 1.5px solid var(--tb-line-2); border-radius: 6px; overflow: hidden; background: var(--tb-surface); }
.nl-editor .ql-toolbar { border: none; border-bottom: 1.5px solid var(--tb-line-2); background: var(--tb-sunk); }
.nl-editor .ql-container { border: none; font-size: .95rem; min-height: 280px; color: var(--tb-ink); }
.nl-editor .ql-editor { min-height: 280px; }
.nl-editor:focus-within { border-color: var(--tb-orange); }
html[data-theme="dark"] .nl-editor .ql-snow .ql-stroke { stroke: var(--tb-ink); }
html[data-theme="dark"] .nl-editor .ql-snow .ql-fill { fill: var(--tb-ink); }
html[data-theme="dark"] .nl-editor .ql-snow .ql-picker { color: var(--tb-ink); }
html[data-theme="dark"] .nl-editor .ql-snow .ql-picker-options { background: var(--tb-surface); border-color: var(--tb-line); }
html[data-theme="dark"] .nl-editor .ql-editor.ql-blank::before { color: var(--tb-faint); }
.nl-source { display: none; width: 100%; min-height: 280px; border: 1.5px solid var(--tb-line-2); border-radius: 6px; padding: 12px;
  font-family: 'Courier New', monospace; font-size: .82rem; box-sizing: border-box; resize: vertical; color: var(--tb-ink); background: var(--tb-surface); }
.nl-source:focus { outline: none; border-color: var(--tb-orange); }
.nl-editor-toolbar-row { display: flex; justify-content: flex-end; gap: 6px; margin-top: 6px; }

/* The attachments list on Edit. */
.nl-file-input-wrap { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
.nl-file-input-wrap input[type=file] { flex: 1; }
.nl-file-list { border: 1px solid var(--tb-line); border-radius: 6px; overflow: hidden; }
.nl-file-none { padding: 12px; text-align: center; color: var(--tb-faint); font-size: .82rem; }
.nl-file-item { display: flex; align-items: center; gap: 12px; padding: 12px; border-bottom: 1px solid var(--tb-line); font-size: .82rem; flex-wrap: wrap; }
.nl-file-item:last-child { border-bottom: none; }
.nl-file-thumb { width: 40px; height: 40px; flex-shrink: 0; background: var(--tb-sunk); border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: .7rem; color: var(--tb-muted); overflow: hidden; }
.nl-file-thumb img { width: 100%; height: 100%; object-fit: cover; }
.nl-file-icon { font-size: .95rem; }
.nl-file-info { flex: 1; min-width: 10rem; }
.nl-file-name { font-weight: 600; color: var(--tb-ink); display: block; word-break: break-word; }
.nl-file-size { font-size: .76rem; color: var(--tb-muted); }
.nl-file-actions { display: flex; gap: 6px; flex-shrink: 0; align-items: center; flex-wrap: wrap; }
.nl-file-btn { padding: 5px 10px; font-size: .76rem; border: 1.5px solid var(--tb-line-2); border-radius: 5px; background: var(--tb-sunk); color: var(--tb-ink); cursor: pointer; font-weight: 600; white-space: nowrap; }
.nl-file-btn:hover { background: var(--tb-hover); }
.nl-file-btn.danger { color: var(--tb-crit); border-color: color-mix(in srgb, var(--tb-crit) 45%, var(--tb-surface)); }
.nl-file-btn.danger:hover { background: color-mix(in srgb, var(--tb-crit) 10%, var(--tb-surface)); }
.nl-file-ckbox { display: flex; align-items: center; gap: 6px; font-size: .76rem; cursor: pointer; margin: 0; }
.nl-upload-loading { display: none; font-size: .76rem; color: var(--tb-muted); margin-bottom: 8px; }
.nl-upload-errors { display: none; background: color-mix(in srgb, var(--tb-crit) 10%, var(--tb-surface)); color: var(--tb-crit); border: 1px solid color-mix(in srgb, var(--tb-crit) 40%, var(--tb-surface)); border-radius: 6px; padding: 10px 12px; font-size: .76rem; margin-bottom: 8px; }
.nl-upload-errors ul { margin: 0; padding-left: 1.1rem; }

/* Preview. */
.nl-preview-bar { padding: 1.25rem 1.5rem; margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; overflow: visible; }
.nl-testform { display: flex; gap: 8px; align-items: center; }
.nl-testform input { border: 1.5px solid var(--tb-line-2); border-radius: 6px; padding: 8px 10px; font-size: .82rem; background: var(--tb-surface); color: var(--tb-ink); min-width: 14rem; }
.nl-testform input:focus { outline: none; border-color: var(--tb-orange); }
.nl-frame-wrap { padding: 12px; }
.nl-frame { width: 100%; height: 70vh; border: 1px solid var(--tb-line); border-radius: 8px; background: #fff; }

/* Recipients. */
.nl-tabs { display: flex; gap: 8px; margin: 1rem 0 0; }
.nl-tab-btn { padding: 9px 18px; border: none; border-radius: 6px 6px 0 0; background: var(--tb-sunk); color: var(--tb-muted); font-size: .84rem; font-weight: 600; cursor: pointer; }
.nl-tab-btn.active { background: var(--tb-surface); color: var(--tb-ink); box-shadow: inset 0 2px 0 var(--tb-orange); }
.nl-panel { display: none; background: var(--tb-surface); border-radius: 0 12px 12px 12px; box-shadow: 0 2px 16px rgba(0,0,0,.10); }
html[data-theme="dark"] .nl-panel { box-shadow: var(--tb-shadow); border: 1px solid var(--tb-line); }
.nl-panel.active { display: block; }
.nl-panel-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 12px 16px; border-bottom: 1px solid var(--tb-line); flex-wrap: wrap; }
.nl-count { font-size: .78rem; color: var(--tb-muted); }
.nl-toggle { cursor: pointer; font-weight: 600; color: var(--tb-ink); }
.nl-toggle input { margin-right: 4px; }
.nl-scroll { max-height: 50vh; overflow-y: auto; }

/* Confirm. */
.nl-row { display: flex; justify-content: space-between; gap: 1rem; padding: 10px 0; border-bottom: 1px solid var(--tb-line); font-size: .9rem; }
.nl-row strong { text-align: right; }
.nl-mode-row { padding: 16px 0; border-bottom: 1px solid var(--tb-line); }
.nl-mode-row label { display: flex; align-items: center; gap: 8px; font-size: .9rem; margin-bottom: 8px; cursor: pointer; flex-wrap: wrap; }
.nl-mode-row input[type=datetime-local] { margin-left: 26px; border: 1.5px solid var(--tb-line-2); border-radius: 6px; padding: 8px 10px; font-size: .82rem; background: var(--tb-surface); color: var(--tb-ink); }
.nl-mode-row input[type=datetime-local]:disabled { opacity: .55; }

/* Report and Unsubscribes. */
.nl-stats { display: flex; gap: 16px; margin-bottom: 1.25rem; flex-wrap: wrap; }
.nl-stat { background: var(--tb-surface); border-radius: 12px; box-shadow: 0 2px 16px rgba(0,0,0,.10); padding: 16px 24px; text-align: center; flex: 1; min-width: 7rem; }
html[data-theme="dark"] .nl-stat { box-shadow: var(--tb-shadow); border: 1px solid var(--tb-line); }
.nl-stats--fit .nl-stat { flex: 0 1 auto; }
.nl-num { font-size: 1.5rem; font-weight: 700; color: var(--tb-ink); font-variant-numeric: tabular-nums; }
.nl-num--crit { color: var(--tb-crit); }
.nl-label { font-size: .76rem; color: var(--tb-muted); }
.nl-sched-card { margin-bottom: 1.25rem; }
.nl-sched-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 14px 16px; border-bottom: 1px solid var(--tb-line); }
.nl-sched-header h2 { font-size: .9rem; font-weight: 700; margin: 0; color: var(--tb-ink); }
.nl-details { margin-top: 1.25rem; }
.nl-details summary { cursor: pointer; font-size: .84rem; font-weight: 600; color: var(--tb-navy); padding: 10px 16px; user-select: none; }
.nl-details summary:hover { text-decoration: underline; }
.nl-clear-btn { background: none; border: none; padding: 0; cursor: pointer; color: var(--tb-crit); font-size: .82rem; font-weight: 600; }
.nl-clear-btn:hover { text-decoration: underline; }

/* The "Sending…" page, written straight to the browser as it goes. */
body.nl-stream { padding: 24px; background: var(--tb-ground); color: var(--tb-ink); font-size: .84rem; }
.nl-stream-wrap { max-width: 720px; margin: 0 auto; }
.nl-stream h2 { font-size: 1.1rem; font-weight: 700; }
.nl-stream-n { color: var(--tb-muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.nl-stream-note { color: var(--tb-warn); font-weight: 600; }
.nl-stream-log { background: var(--tb-surface); border: 1px solid var(--tb-line); border-radius: 8px; padding: .75rem 1rem; margin: .75rem 0 1rem; font-family: ui-monospace, monospace; font-size: .8rem; line-height: 1.6; }
.nl-stream-log .ok { color: var(--tb-good); }
.nl-stream-log .err { color: var(--tb-crit); }
/* --- end P-NEWS --- */

/* --- the one search (/staff/search), Carl 2 Oct 2026 --- */
.staff-search-list { display: flex; flex-direction: column; gap: .15rem; }
.staff-search-row { display: flex; align-items: baseline; gap: .5rem; padding: .35rem .25rem; border-bottom: 1px solid var(--tb-line); flex-wrap: wrap; }
.staff-search-row:last-child { border-bottom: 0; }
.staff-search-row.is-inactive .staff-search-name { color: var(--tb-muted); }
.staff-search-name { font-weight: 600; text-decoration: none; color: var(--tb-ink); }
.staff-search-name:hover { color: var(--tb-navy); }
.staff-search-row .staff-quick-no { margin-left: auto; }
/* --- end the one search --- */

/* --- P-TAKEOFF-1 --- the plan on screen (templates/takeoff/show.php) */
.tk { position: fixed; inset: 0; display: flex; flex-direction: column; background: #e9ecf1; color: #171b22; }
.tk-bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: #0d47a1; color: #fff; flex-wrap: wrap; }
.tk-back { color: #fff; font-size: 20px; text-decoration: none; padding: 2px 6px; }
.tk-title { flex: 1; min-width: 200px; line-height: 1.2; }
.tk-title b { display: block; font-size: 15px; } .tk-title span { font-size: 12px; color: #c8d3ea; }
.tk-tools { display: flex; align-items: center; gap: 4px; }
.tk-tool { background: transparent; border: 1px solid rgba(255,255,255,.35); color: #fff; border-radius: 6px; padding: 5px 10px; font-size: 13px; font-weight: 600; }
.tk-tool.is-primary { background: #F87800; border-color: #F87800; }
.tk-tool.is-active { background: #fff; color: #F87800; border-color: #fff; }
.tk-sep { width: 1px; height: 22px; background: rgba(255,255,255,.3); margin: 0 4px; }
.tk-zoom { font-size: 12px; min-width: 42px; text-align: center; font-variant-numeric: tabular-nums; }
.tk-scale { width: 100%; font-size: 12px; color: #ffd9b8; padding-top: 2px; } .tk-scale.is-set { color: #c7f0cf; }
.tk-body { flex: 1; display: flex; min-height: 0; }
.tk-thumbs { width: 150px; overflow-y: auto; background: #fff; border-right: 1px solid #d9dee5; padding: 8px; display: flex; flex-direction: column; gap: 8px; }
.tk-thumb { border: 2px solid transparent; border-radius: 6px; background: #f4f6f9; padding: 4px; text-align: center; font-size: 11px; color: #5f6873; cursor: pointer; }
.tk-thumb canvas { width: 100%; height: auto; display: block; border: 1px solid #d9dee5; background: #fff; }
.tk-thumb.is-active { border-color: #F87800; color: #F87800; font-weight: 700; }
.tk-stage { flex: 1; overflow: auto; position: relative; padding: 16px; }
.tk-canvas-wrap { position: relative; margin: 0 auto; box-shadow: 0 2px 12px rgba(0,0,0,.18); background: #fff; }
.tk-canvas-wrap canvas { position: absolute; left: 0; top: 0; }
.tk-canvas-wrap canvas[data-overlay] { z-index: 2; }
.tk-hint { position: sticky; bottom: 8px; margin: 8px auto 0; width: max-content; max-width: 90%; background: #171b22; color: #fff; font-size: 13px; padding: 6px 12px; border-radius: 999px; }
.tk-hint[hidden] { display: none; }
.tk-modal { position: fixed; inset: 0; background: rgba(23,27,34,.55); display: flex; align-items: center; justify-content: center; z-index: 20; }
.tk-modal[hidden] { display: none; }
.tk-modal-box { background: #fff; border-radius: 12px; padding: 18px 20px; width: min(420px, 92vw); }
@media (max-width: 720px) { .tk-thumbs { display: none; } .tk-title span { display: none; } }
/* --- end P-TAKEOFF-1 --- */

/* --- P-TAKEOFF-2/3 --- the measurements panel and the line picker */
.tk-tool.is-ai { background: #fff; color: #0d47a1; border-color: #fff; }
.tk-panel { width: 300px; background: #fff; border-left: 1px solid #d9dee5; display: flex; flex-direction: column; min-height: 0; }
.tk-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-bottom: 1px solid #d9dee5; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: #5f6873; font-weight: 700; }
.tk-new button { background: #fff; border: 1px solid #d9dee5; border-radius: 6px; font-size: 12px; font-weight: 600; color: #0d47a1; padding: 3px 7px; margin-left: 3px; }
.tk-new button:hover { border-color: #F87800; color: #F87800; }
.tk-groups { overflow-y: auto; padding: 8px; flex: 1; }
.tk-empty { color: #5f6873; font-size: 13px; padding: 10px; text-align: center; }
.tk-g { border: 1px solid #d9dee5; border-radius: 8px; margin-bottom: 8px; background: #fff; }
.tk-g.is-active { border-color: #F87800; box-shadow: 0 0 0 2px rgba(248,120,0,.15); }
.tk-g.is-draft { border-style: dashed; }
.tk-g-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; cursor: pointer; }
.tk-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none; cursor: pointer; }
.tk-g-name { flex: 1; font-weight: 600; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tk-g-type { font-size: 11px; color: #5f6873; }
.tk-g-total { font-variant-numeric: tabular-nums; font-size: 14px; white-space: nowrap; }
.tk-g-flag { font-size: 12px; padding: 4px 10px; background: #fff4dc; color: #5a3d00; }
.tk-g-flag.is-ok { background: #e3f3e6; color: #1b5e20; }
.tk-g-flag button { background: none; border: 0; color: #0d47a1; font-weight: 700; font-size: 12px; padding: 0; text-decoration: underline; }
.tk-s { display: flex; align-items: center; gap: 6px; padding: 4px 10px 4px 32px; font-size: 13px; border-top: 1px solid #f1f3f6; }
.tk-s > span:first-child { flex: 1; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tk-s.is-editing > span:first-child { color: #F87800; font-weight: 600; }
.tk-s-v { color: #5f6873; font-variant-numeric: tabular-nums; }
.tk-s button { background: none; border: 0; color: #9aa3ae; font-size: 16px; line-height: 1; padding: 0 2px; }
.tk-s button:hover { color: #c62828; }
.tk-g-acts { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 10px 8px; border-top: 1px solid #f1f3f6; }
.tk-g-acts button { background: #fff; border: 1px solid #d9dee5; border-radius: 6px; font-size: 12px; padding: 3px 8px; color: #0d47a1; }
.tk-g-acts button:first-child { color: #F87800; border-color: #F87800; font-weight: 700; }
.tk-lines { max-height: 50vh; overflow-y: auto; border: 1px solid #d9dee5; border-radius: 8px; }
.tk-line { display: block; width: 100%; text-align: left; background: #fff; border: 0; border-bottom: 1px solid #f1f3f6; padding: 8px 10px; }
.tk-line:hover { background: #fff6ee; } .tk-line.is-hint { background: #fff6ee; border-left: 3px solid #F87800; }
.tk-line span { display: block; font-weight: 600; font-size: 14px; } .tk-line small { color: #5f6873; } .tk-line em { color: #b26a00; font-style: normal; }
@media (max-width: 980px) { .tk-panel { width: 240px; } }
.tk-chip { display: inline-block; font-size: 11px; font-weight: 700; color: #1b5e20; background: #e3f3e6; border-radius: 999px; padding: 1px 7px; margin-left: 4px; text-decoration: none; white-space: nowrap; vertical-align: middle; }
.tk-chip.is-draft { background: #fff4dc; color: #8a5a00; }
.tk-chip.is-quiet { background: transparent; color: #9aa3ae; padding: 1px 4px; } .tk-chip.is-quiet:hover { color: #F87800; }
/* --- end P-TAKEOFF-2/3 --- */

/* --- R1 --- the recipe list under the line picker: the modal stays on one screen */
.tk-modal-box { max-height: 92vh; overflow-y: auto; }
.tk-recipes .tk-lines { max-height: 26vh; }
[data-line-modal] .tk-lines:not(.tk-recipes .tk-lines) { max-height: 34vh; }
.tk-recipe span small { color: #5f6873; font-weight: 400; }
/* --- end R1 --- */

/* --- B3 --- the estimate in three passes: the strip, Trim, Price by trade */
.b3-passes { display: flex; gap: 6px; margin: 0 0 .75rem; flex-wrap: wrap; }
.b3-pass { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px 4px 6px; border: 1px solid var(--tb-line); border-radius: 999px; background: var(--tb-surface); color: var(--tb-navy, #0d47a1); font-weight: 600; font-size: 13px; text-decoration: none; }
.b3-pass small { font-weight: 400; color: #5f6873; }
.b3-pass-n { width: 20px; height: 20px; border-radius: 50%; background: #e8edf3; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; }
.b3-pass.is-on { border-color: #F87800; } .b3-pass.is-on .b3-pass-n { background: #F87800; color: #fff; }
.b3-bar { display: flex; align-items: center; gap: 12px; padding: 4px 0 8px; }
.b3-sec { border-top: 1px solid var(--tb-line); padding: 8px 0; }
.b3-sec-h { margin-bottom: 4px; } .b3-sec-h label { cursor: pointer; }
.b3-head { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: #5f6873; margin: 6px 0 2px 22px; }
.b3-line { display: flex; align-items: baseline; gap: 8px; padding: 3px 4px; border-radius: 6px; cursor: pointer; }
.b3-line:hover { background: #f6f8fb; } .b3-line.is-in { margin-left: 22px; } .b3-line.is-off .b3-desc { text-decoration: line-through; color: #5f6873; }
.b3-desc { flex: 1; } .b3-meta { font-size: 12px; color: #5f6873; white-space: nowrap; }
.b3-progress { height: 6px; background: #e8edf3; border-radius: 3px; margin: -.5rem 0 1rem; overflow: hidden; } .b3-progress span { display: block; height: 100%; background: #2e7d32; }
.b3-bench { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; background: #fff6ee; border: 1px solid #f3d3b3; border-radius: var(--tb-radius); padding: 10px 12px; margin-bottom: 1rem; }
.b3-trade { padding: 10px 12px; margin-bottom: 10px; } .b3-trade.is-done { border-color: #a5d6a7; background: #f4faf4; }
.b3-trade-h { display: flex; align-items: flex-start; gap: 10px; } .b3-state { font-size: 12px; font-weight: 700; color: #b26a00; white-space: nowrap; } .is-done .b3-state { color: #2e7d32; }
.b3-lines summary { cursor: pointer; color: #5f6873; margin: 4px 0; } .b3-lines ul { padding-left: 1.2rem; }
.b3-ask { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.b3-party { background: #fff; border: 1px solid var(--tb-line); border-radius: 999px; padding: 4px 10px; font-size: 13px; font-weight: 600; color: var(--tb-navy, #0d47a1); }
.b3-party small { font-weight: 400; color: #5f6873; } .b3-party:hover { border-color: #F87800; color: #F87800; }
.b3-party-tb { border-color: #0d47a1; } .b3-party-chosen { border-color: #2e7d32; }
.tpl-wyg { margin-top: 4px; max-width: 70ch; }
/* --- end B3 --- */

/* --- B1 --- Today in four blocks, nav of five + More, one info banner.
   The tiles above Today are the four questions, in order; four columns
   on a desktop and two on a phone. */
.today-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 575.98px) { .today-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.today-tiles a.tile { color: inherit; text-decoration: none; }
.today-block { scroll-margin-top: 4.5rem; }
.today-empty { margin: 0; padding: .6rem 1rem 1rem; color: var(--tb-muted); font-size: .88rem; }
.today-note  { margin: 0; padding: .6rem 1rem .9rem; color: var(--tb-muted); font-size: .82rem; border-top: 1px solid var(--tb-line); }
/* S2: a Today row with its own buttons (nudge / close / keep open). */
.queue-ask { border-top: 1px solid var(--tb-line); }
.queue-ask:first-child { border-top: none; }
.queue-ask > .queue-row { border-top: none; }
.queue-ask-actions { display: flex; gap: .5rem; flex-wrap: wrap; padding: 0 1rem .7rem 1.4rem; }
.queue-ask-actions form { margin: 0; }
.today-sub   { padding: .7rem 1rem .2rem; font-size: .7rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--tb-muted); border-top: 1px solid var(--tb-line); }
.today-amount { font-weight: 700; font-variant-numeric: tabular-nums; font-size: .9rem; }
.today-tidy { font-size: .85rem; }
.today-tidy a { color: var(--tb-muted); }
.today-tidy a:hover { color: var(--tb-ink); }
.chip.chip-late { border-color: #f3e2b8; }
.chip.chip-late .chip-n { color: var(--tb-warn); }

/* More: in the navy bar and in the phone tab bar. The tab bar's own
   `.tabbar a` rule would otherwise turn every menu item into a tab. */
.app-navbar .dropdown-menu { font-size: .9rem; }
.app-navbar .dropdown-item.active,
.app-navbar .dropdown-item:active,
.tabbar .dropdown-item.active,
.tabbar .dropdown-item:active { background: var(--tb-orange-soft); color: var(--tb-orange-ink); font-weight: 600; }
.tabbar-more { display: flex; justify-content: center; }
.tabbar-more > a { width: 100%; }
.tabbar-more > a::after { display: none; }
.tabbar .dropdown-menu a.dropdown-item {
  display: block; flex-direction: row; font-size: .92rem; font-weight: 500;
  color: var(--tb-ink); padding: .55rem 1rem;
}
.tabbar .dropdown-menu a.dropdown-item i { font-size: 1rem; }

/* One instruction banner: neutral blue. "Tick the lines first" is an
   instruction, not an error, so it is an alert-info flash, and red stays
   for things that went wrong. */
.alert-info {
  --bs-alert-bg: var(--tb-hover); --bs-alert-color: var(--tb-ink);
  --bs-alert-border-color: #cfe0ff; --bs-alert-link-color: var(--tb-navy);
  border-left: 3px solid var(--tb-navy);
}
/* --- end B1 --- */

/* B5: the Files tab -- current plans first, then the groups. */
.files-current-open { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.files-jump { display: flex; flex-wrap: wrap; gap: .4rem; }
.files-group th { background: var(--tb-hover); font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--tb-navy, inherit); scroll-margin-top: 4.5rem; }

/* B7: this week as days on the Schedule tab. */
.sch-week-day { padding: .4rem 0; border-top: 1px solid var(--tb-line); }
.sch-week-day:first-of-type { border-top: none; }
.sch-week-day.is-today .sch-week-date { color: var(--tb-orange-ink, #b85a00); }
.sch-week-date { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--tb-muted); }
.sch-week-row { display: flex; justify-content: space-between; gap: .5rem; align-items: baseline; padding: .15rem 0; }

/* The renewal page's two options, as the renewal email sets them out. */
.renew-option { border: 1px solid var(--tb-line); border-radius: .5rem; padding: 1rem 1.15rem; margin: 1rem 0; scroll-margin-top: 1rem; }
.renew-option:target { border-color: var(--tb-orange); box-shadow: 0 0 0 3px rgba(248,120,0,.15); }
.renew-option-h { font-size: 1rem; font-weight: 700; color: var(--tb-orange-ink, #b85a00); margin: 0 0 .5rem; }
.renew-option .btn { margin: .4rem 0 .8rem; }
.renew-pill { display: inline-block; background: var(--tb-orange); color: #fff; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: .1rem .55rem; border-radius: 1rem; vertical-align: middle; margin-left: .3rem; }
.renew-option .btn-outline-primary { border: 2px solid var(--tb-orange); color: var(--tb-orange-ink, #b85a00); background: #fff; }
.renew-option .btn-outline-primary:hover { background: var(--tb-orange); color: #fff; }

/* P-SITE-3: the desk at phone width points at TB Site. */
.desk-phone-note { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; background: var(--tb-navy); color: #fff; border-radius: .5rem; padding: .6rem .8rem; margin: 0 0 .8rem; font-size: .85rem; }
.desk-phone-note[hidden] { display: none; }
.desk-phone-note span { flex: 1 1 12rem; }
@media (min-width: 576px) { .desk-phone-note { display: none !important; } }
/* P-SITE-3: install prompts. */
.login-site { display: none; }
@media (max-width: 575.98px) { .login-site { display: block; } }
.mcard-install { background: #fff7ee; border: 1px solid #ffd9b0; border-radius: .5rem; padding: .6rem .8rem; }
.site-install { display: flex; align-items: center; gap: .6rem; background: #fff; border: 1px solid var(--tb-line); border-radius: .75rem; padding: .6rem .7rem; margin: 0 0 .8rem; box-shadow: 0 2px 8px rgba(13,43,94,.08); }
.site-install[hidden] { display: none; }
.site-install img { border-radius: .55rem; flex: 0 0 40px; }
.site-install-text { flex: 1; font-size: .85rem; line-height: 1.35; display: flex; flex-direction: column; }
