/* PM UK — practice management. Hand-written CSS: no build step, so what is in
   the repository is exactly what the browser receives. */

:root {
  /* Taken from the CBM Accounting logo: the slate grey of "cbm" and the cloud,
     and the azure of "accounting" and the mark.

     The azure is too light to carry white text, so it does the decorative work
     — brand mark, active states, focus rings — while the slate carries buttons
     and anything that needs contrast. --azure-ink is a darkened azure used
     wherever the colour has to be legible as text. */
  --paper:#F7F9FA; --card:#FFFFFF; --sidebar:#F0F4F6;
  --ink:#2C3A43; --ink-soft:#5A6B76; --ink-faint:#8494A0;
  --rule:#DCE4E9; --rule-soft:#EAF0F3; --wash:#F0F4F6;

  --slate:#44555F; --slate-deep:#36444D;
  --azure:#29ABE2; --azure-ink:#15769F; --azure-wash:#E4F4FC;

  --brand:#44555F; --brand-ink:#15769F; --brand-wash:#E4F4FC; --on-brand:#FFFFFF;

  --danger:#B3392A; --danger-wash:#FBEDEB; --danger-rule:#EBC1BA;
  --warn:#8A5B00; --warn-wash:#FBF2DF;
  --ok:#1C7A55; --ok-wash:#E4F4EE;
  --info:#15769F; --info-wash:#E4F4FC;
  --radius:10px;
  --shadow:0 1px 2px rgba(44,58,67,.06), 0 10px 28px -14px rgba(44,58,67,.22);
  --sans:"IBM Plex Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --serif:"IBM Plex Serif",Georgia,"Times New Roman",serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:#101619; --card:#182126; --sidebar:#141C20;
    --ink:#E6EDF1; --ink-soft:#A6B5BE; --ink-faint:#788994;
    --rule:#26333A; --rule-soft:#1D272C; --wash:#1A2429;

    --slate:#8FA3AF; --slate-deep:#7A8E9A;
    /* On a dark ground the azure is the readable one, so the roles swap. */
    --azure:#29ABE2; --azure-ink:#6FC9F0; --azure-wash:#12303F;

    --brand:#29ABE2; --brand-ink:#6FC9F0; --brand-wash:#12303F; --on-brand:#0B1B23;

    --danger:#E58C7C; --danger-wash:#2E1B18; --danger-rule:#4E2D26;
    --warn:#DCA94E; --warn-wash:#2A2317;
    --ok:#6BC69F; --ok-wash:#15271F;
    --info:#6FC9F0; --info-wash:#12303F;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 28px -14px rgba(0,0,0,.75);
  }
}
*,*::before,*::after{box-sizing:border-box}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--sans);font-size:15px;line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
.boot{display:grid;place-items:center;min-height:100vh;color:var(--ink-faint)}
h1,h2,h3{font-family:var(--serif);margin:0;line-height:1.25;text-wrap:balance}
h1{font-size:1.5rem;font-weight:600;letter-spacing:-.01em}
h2{font-size:1.15rem;font-weight:600}
h3{font-size:1rem;font-weight:600}
p{margin:0}
a{color:var(--brand-ink)}
/* ------------------------------------------------------------- layout */
.app{display:flex;min-height:100vh}
.sidebar{
  width:250px;flex:none;background:var(--sidebar);
  border-right:1px solid var(--rule);
  display:flex;flex-direction:column;gap:1.5rem;padding:1rem;
  overflow-y:auto;
}
.brand{display:flex;align-items:center;gap:.6rem;padding:.4rem .5rem;text-decoration:none;color:inherit}
.brand-mark{
  width:34px;height:34px;flex:none;border-radius:8px;background:var(--azure);
  color:#fff;display:grid;place-items:center;font-weight:700;font-size:.8rem;
}
/* An uploaded logo replaces the lettermark entirely. */
.brand-logo{height:38px;width:auto;max-width:178px;object-fit:contain;object-position:left center;display:block}
.brand-logo-login{height:52px;width:auto;max-width:240px;object-fit:contain;margin:0 auto 1.25rem;display:block}
.brand-name{font-weight:600;font-size:.95rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-group{display:flex;flex-direction:column;gap:2px}
.nav-title{
  font-size:.68rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-faint);padding:0 .65rem .3rem;
}
.nav-item{
  display:flex;align-items:center;gap:.6rem;padding:.5rem .65rem;border-radius:8px;
  color:var(--ink-soft);text-decoration:none;font-size:.9rem;cursor:pointer;
  border:0;background:none;font-family:inherit;width:100%;text-align:left;
}
.nav-item:hover{background:var(--rule-soft);color:var(--ink)}
.nav-item.active{background:var(--azure-wash);color:var(--azure-ink);font-weight:600}
.nav-empty{padding:.35rem .65rem;font-size:.8rem;color:var(--ink-faint)}
/* Pinned to the bottom of the sidebar: who you are, sign out, Administration. */
.sidebar-foot{
  margin-top:auto;padding-top:.6rem;border-top:1px solid var(--rule);
  display:flex;flex-direction:column;gap:2px;
}
.user-row{
  display:flex;align-items:center;gap:.6rem;padding:.5rem .65rem;
  border-radius:8px;text-decoration:none;color:inherit;
}
.user-row:hover{background:var(--rule-soft)}
.user-row.active{background:var(--azure-wash)}
.user-name{
  display:block;font-size:.875rem;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.user-role{display:block;font-size:.75rem;color:var(--ink-faint)}
.nav-admin{font-weight:600;color:var(--ink)}
.swatch{width:9px;height:9px;border-radius:50%;flex:none}
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.content{flex:1;padding:1.75rem 1.5rem 4rem;max-width:1100px;width:100%}
/* The board needs every pixel: six columns squeezed into a reading-width
   column would leave the last one off the side of the screen. */
.content.wide{max-width:none}
/* Specificity matched to .btn, which is declared later. */
.btn.burger{display:none}
.btn.burger{position:fixed;top:.75rem;left:.75rem;z-index:30}
/* ------------------------------------------------------------ heading */
.page-head{display:flex;flex-wrap:wrap;gap:1rem;justify-content:space-between;align-items:flex-start;margin-bottom:1.5rem}
.page-head p{color:var(--ink-soft);font-size:.9rem;margin-top:.3rem;max-width:52ch}
/* -------------------------------------------------------------- cards */
.card{background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);box-shadow:var(--shadow)}
.card-head{padding:1.1rem 1.25rem;border-bottom:1px solid var(--rule-soft)}
.card-head p{color:var(--ink-soft);font-size:.85rem;margin-top:.25rem}
.card-body{padding:1.25rem}
.card-foot{padding:1rem 1.25rem;border-top:1px solid var(--rule-soft);display:flex;justify-content:flex-end;gap:.6rem;align-items:center}
.grid{display:grid;gap:1rem}
.grid-2{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.stack{display:flex;flex-direction:column;gap:1rem}
.row{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap}
.spacer{flex:1}
.stat .n{font-family:var(--serif);font-size:1.9rem;font-weight:600;font-variant-numeric:tabular-nums}
.stat .l{font-size:.8rem;color:var(--ink-faint);text-transform:uppercase;letter-spacing:.06em;font-weight:600}
/* ------------------------------------------------------------ controls */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  font-family:inherit;font-size:.875rem;font-weight:500;line-height:1;
  padding:.55rem .9rem;border-radius:8px;border:1px solid transparent;
  cursor:pointer;white-space:nowrap;text-decoration:none;
}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn:focus-visible{outline:2px solid var(--azure);outline-offset:2px}
.btn-primary{background:var(--brand);color:var(--on-brand)}
.btn-primary:hover:not(:disabled){filter:brightness(1.08)}
.btn-outline{background:var(--card);border-color:var(--rule);color:var(--ink)}
.btn-outline:hover:not(:disabled){background:var(--wash)}
.btn-ghost{background:none;color:var(--ink-soft)}
.btn-ghost:hover:not(:disabled){background:var(--wash);color:var(--ink)}
.btn-danger{background:var(--danger);color:#fff}
.btn-danger:hover:not(:disabled){filter:brightness(1.08)}
.btn-sm{padding:.35rem .6rem;font-size:.8rem}
label.field{display:flex;flex-direction:column;gap:.35rem}
label.field > span{font-size:.82rem;font-weight:600;color:var(--ink)}
label.field .hint{font-size:.78rem;font-weight:400;color:var(--ink-faint)}
input[type=text],input[type=email],input[type=password],input[type=date],
input[type=number],input[type=tel],input[type=url],select,textarea{
  font-family:inherit;font-size:.875rem;color:var(--ink);background:var(--card);
  border:1px solid var(--rule);border-radius:8px;padding:.5rem .65rem;width:100%;
}
textarea{min-height:80px;resize:vertical}
input:focus,select:focus,textarea:focus{outline:2px solid var(--azure);outline-offset:-1px;border-color:var(--azure)}
input:disabled,select:disabled{background:var(--wash);color:var(--ink-faint)}
.checkline{display:flex;align-items:center;gap:.6rem;padding:.4rem .5rem;border-radius:6px;cursor:pointer}
.checkline:hover{background:var(--wash)}
.checkline input{accent-color:var(--azure);width:1rem;height:1rem;flex:none;cursor:pointer}
.checkline .name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem}
.scrollbox{max-height:190px;overflow-y:auto;border:1px solid var(--rule);border-radius:8px;padding:.25rem}
.pill{
  display:inline-flex;align-items:center;gap:.35rem;font-size:.75rem;font-weight:600;
  padding:.2rem .55rem;border-radius:999px;white-space:nowrap;
}
.pill-outline{border:1px solid var(--rule);color:var(--ink-soft);font-weight:500}
.pill-admin{background:var(--azure-wash);color:var(--azure-ink)}
.pill-azure{background:var(--azure-wash);color:var(--azure-ink);font-weight:500}
.pill-manager{background:var(--info-wash);color:var(--info)}
.pill-supervisor{background:var(--warn-wash);color:var(--warn)}
.pill-team_leader{background:var(--ok-wash);color:var(--ok)}
.pill-staff{background:var(--wash);color:var(--ink-soft)}
.pill-active{background:var(--ok-wash);color:var(--ok)}
.pill-invited{background:var(--info-wash);color:var(--info)}
.pill-suspended{background:var(--danger-wash);color:var(--danger)}
/* -------------------------------------------------------------- table */
.table-wrap{overflow-x:auto;border:1px solid var(--rule);border-radius:var(--radius);background:var(--card)}
table{width:100%;border-collapse:collapse;font-size:.875rem}
th{
  text-align:left;padding:.6rem .85rem;font-size:.7rem;font-weight:600;
  letter-spacing:.07em;text-transform:uppercase;color:var(--ink-faint);
  border-bottom:1px solid var(--rule);white-space:nowrap;
}
td{padding:.7rem .85rem;border-bottom:1px solid var(--rule-soft);vertical-align:middle}
tr:last-child td{border-bottom:0}
tbody tr:hover{background:var(--wash)}
td.num{text-align:right;font-variant-numeric:tabular-nums}
.empty-row{text-align:center;color:var(--ink-faint);padding:2.5rem 1rem}
.avatar{
  width:32px;height:32px;flex:none;border-radius:50%;background:var(--wash);
  color:var(--ink-soft);display:grid;place-items:center;font-size:.75rem;font-weight:600;
}
.who{display:flex;align-items:center;gap:.65rem;min-width:0}
.who .nm{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who .sub{font-size:.78rem;color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dept-chip{display:inline-flex;align-items:center;gap:.35rem;border:1px solid var(--rule);border-radius:999px;padding:.15rem .5rem;font-size:.75rem}
/* -------------------------------------------------------------- modal */
.backdrop{
  position:fixed;inset:0;background:rgba(10,16,14,.55);z-index:50;
  display:flex;align-items:flex-start;justify-content:center;padding:2rem 1rem;overflow-y:auto;
}
.modal{
  background:var(--card);border:1px solid var(--rule);border-radius:12px;
  box-shadow:var(--shadow);width:100%;max-width:640px;margin:auto;
}
.modal-sm{max-width:440px}
.modal-head{padding:1.15rem 1.25rem;border-bottom:1px solid var(--rule-soft)}
.modal-head p{font-size:.85rem;color:var(--ink-soft);margin-top:.25rem}
.modal-body{padding:1.25rem;display:flex;flex-direction:column;gap:1rem}
.modal-foot{padding:1rem 1.25rem;border-top:1px solid var(--rule-soft);display:flex;justify-content:flex-end;gap:.6rem}
/* Tab bar for a workspace with several sections. Distinct from .tabs, which is
   the compact segmented control used inside dialogs. */
.tabbar{
  display:flex;gap:.25rem;border-bottom:1px solid var(--rule);
  margin:0 0 1.5rem;overflow-x:auto;
}
.tabbar-item{
  padding:.6rem .9rem;font-size:.9rem;font-weight:500;color:var(--ink-soft);
  text-decoration:none;white-space:nowrap;border-bottom:2px solid transparent;
  margin-bottom:-1px;
}
.tabbar-item:hover{color:var(--ink)}
.tabbar-item.on{color:var(--azure-ink);border-bottom-color:var(--azure);font-weight:600}
.steplist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.steprow{
  display:flex;align-items:center;gap:.7rem;padding:.55rem .6rem;border-radius:8px;
}
.steprow:hover{background:var(--wash)}
.steprow.is-review{background:var(--azure-wash)}
.steprow.is-review:hover{background:var(--azure-wash)}
.stepname{font-size:.9rem;font-weight:500}
.stepnum{
  width:22px;height:22px;flex:none;border-radius:50%;background:var(--wash);
  color:var(--ink-soft);display:grid;place-items:center;font-size:.72rem;font-weight:700;
  border:1px solid var(--rule);
}
.steprow.is-review .stepnum{background:var(--azure);color:#fff;border-color:var(--azure)}
.stepedit{border:1px solid var(--rule);border-radius:8px;padding:.5rem}
/* A group heading inside the staff table, e.g. the unassigned block. */
.grouprow td{
  background:var(--wash);font-size:.72rem;font-weight:600;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-faint);padding:.5rem .85rem;
}
/* A short elbow marking an indented row as reporting to the one above. */
.treeline{
  width:12px;height:1px;flex:none;background:var(--rule);margin-right:-.2rem;
}
.rolelabel{
  font-size:.72rem;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:600;
}
/* --------------------------------------------------- client records */
/* A whole table row that opens a record. The name inside is still a real
   link, so the keyboard and the middle mouse button both work. */
.rowlink{cursor:pointer}
.cellname{font-weight:600;color:inherit;text-decoration:none;display:block}
.cellname:hover{color:var(--azure-ink);text-decoration:underline}
.cellsub{display:block;font-size:.78rem;color:var(--ink-faint)}
/* A filing date reads as ordinary text until it is close, then late. */
.due-soon{color:var(--warn);font-weight:600}
.due-overdue{color:var(--danger);font-weight:600}
.backlink{
  display:inline-block;font-size:.82rem;color:var(--ink-soft);text-decoration:none;
}
.backlink:hover{color:var(--azure-ink)}
/* Label above value rather than beside it: these panels sit three to a row, and
   a fixed label column leaves the value too narrow to read. */
.deflist{display:flex;flex-direction:column;gap:.7rem;margin:0}
.defrow dt{font-size:.75rem;color:var(--ink-faint);margin-bottom:.1rem}
.defrow dd{margin:0;font-size:.875rem;overflow-wrap:break-word;white-space:pre-wrap}
/* One Companies House search result, sitting in a scrollbox. */
.chresult{
  display:block;width:100%;text-align:left;border:0;background:none;cursor:pointer;
  font-family:inherit;padding:.45rem .55rem;border-radius:6px;color:inherit;
}
.chresult:hover:not(:disabled){background:var(--wash)}
.chresult:disabled{opacity:.6;cursor:not-allowed}
.chresult .nm{display:block;font-size:.875rem;font-weight:600}
.chresult .sub{display:block;font-size:.78rem;color:var(--ink-faint)}
/* The department line on a template card. */
.dept-strip{
  padding:.55rem 1.25rem;border-top:1px solid var(--rule-soft);
  display:flex;gap:.75rem;align-items:center;
}
.dept-strip select{max-width:20rem}
.jobline{
  display:grid;grid-template-columns:1fr 8rem 7rem 6rem 8rem;gap:.6rem;
  align-items:center;padding:.45rem .55rem;border-radius:6px;
  text-decoration:none;color:inherit;
}
.jobline:hover{background:var(--wash)}
.jobline .nm{font-weight:600;font-size:.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jobline .sub{font-size:.8rem;color:var(--ink-faint)}
/* ---------------------------------------------------------- the pipeline */
/* A tab per person; inside a tab, a section per service. Every section shares
   one grid, so there is one row of stage headings and one sideways scrollbar
   however many services are on screen. Six columns cannot fit a reading-width
   page, so the grid scrolls sideways as one piece and the service heading
   travels with its rows. */
.tabstrip{
  display:flex;gap:.25rem;padding:.25rem;background:var(--wash);border-radius:8px;
  margin-bottom:1rem;overflow-x:auto;
  /* Groups carrying a team name are taller than the bare buttons beside them.
     Aligning on the bottom puts every tab on one line whether or not it has a
     caption above it. */
  align-items:flex-end;
}
.tabstrip .tab{flex:none;white-space:nowrap}
.tabstrip .tab .n{
  margin-left:.4rem;font-weight:500;font-size:.72rem;color:var(--ink-faint);
}
.tabstrip .tab.on .n{color:inherit;opacity:.7}
/* A team's people sit under the team's name, so the strip reads down the
   practice's chain rather than down the alphabet. The name is a caption, not a
   control — nothing about it is clickable. */
.tabgroup{flex:none;display:flex;flex-direction:column;gap:.1rem;padding-left:.4rem;
  border-left:1px solid var(--line)}
.tabgroup:first-of-type{border-left:0;padding-left:0}
.tabgroup-name{
  font-size:.66rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-faint);padding:0 .5rem;
}
.tabgroup-people{display:flex;gap:.25rem}
.pipe{
  display:grid;grid-template-columns:repeat(6, minmax(176px, 1fr));
  gap:.4rem .5rem;overflow-x:auto;align-items:start;padding-bottom:.75rem;
}
.pipe-head{
  font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:600;padding:.2rem .3rem;
}
/* Each service now carries its own workflow, so each gets its own grid and its
   own row of headings — Bookkeeping's columns and VAT's are different lists and
   cannot share one. The heading sits outside the grid, above it. */
.pipe-block{margin-bottom:1.4rem}
.pipe-block + .pipe-block{border-top:1px solid var(--rule);padding-top:1rem}
.pipe-service{
  margin:0 0 .5rem;font-size:1.05rem;
}
.pipe-service .lbl{
  display:inline-flex;align-items:center;gap:.45rem;
  border-left:3px solid var(--azure);padding-left:.5rem;
}
.pipe-service .n{
  font-weight:500;color:var(--ink-faint);background:var(--wash);
  border-radius:999px;padding:.05rem .45rem;font-size:.72rem;
}
.pipe-cell{
  background:var(--wash);border-radius:8px;min-height:3.2rem;padding:.35rem;
  display:flex;flex-direction:column;gap:.4rem;border:1px dashed transparent;
}
.pipe-cell.droppable{border-color:var(--azure)}
.jobcard{
  display:block;background:var(--card);border:1px solid var(--rule);
  border-radius:8px;padding:.55rem .65rem;text-decoration:none;color:inherit;
  box-shadow:var(--shadow);cursor:pointer;
}
.jobcard:hover{border-color:var(--azure)}
.jobcard:focus-visible{outline:2px solid var(--azure);outline-offset:2px}
.jobcard .client{display:block;font-weight:600;font-size:.85rem;line-height:1.3}
.jobcard .period{display:block;font-size:.75rem;color:var(--ink-faint)}
.jobcard .who{
  display:block;margin-top:.4rem;padding-top:.35rem;border-top:1px solid var(--rule-soft);
  font-size:.72rem;color:var(--ink-soft);font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* A job that has climbed to a reviewer with nobody to receive it. It is not an
   error — the work is safe with whoever did it — but the board must not go on
   implying somebody is checking it, so the line wraps rather than clipping. */
.jobcard .who.gap{
  color:var(--warn);font-weight:600;white-space:normal;
}
.jobcard .who.gap + .who{border-top:0;margin-top:.15rem;padding-top:0}
.jobmeta{
  display:flex;gap:.4rem;align-items:center;flex-wrap:wrap;margin-top:.4rem;
  font-size:.72rem;
}
.jobmeta .steps{
  background:var(--wash);border-radius:999px;padding:.05rem .4rem;color:var(--ink-soft);
}
/* Whether this job comes round again, and how often — the one thing a card
   could not say before. A one-off is spelt out rather than left blank, so an
   absent chip never has to be interpreted. */
.jobmeta .repeat{
  background:var(--azure-wash);color:var(--azure-ink);
  border-radius:999px;padding:.05rem .45rem;font-weight:600;
}
.jobmeta .repeat.once{background:var(--wash);color:var(--ink-faint);font-weight:500}
.pill-to_do{background:var(--wash);color:var(--ink-soft)}
/* The seeded columns. One the practice adds gets the plain pill — the system
   has no opinion about a colour it was never given — so the fallback goes
   first and the named ones override it. */
[class*="pill-status-"]{background:var(--wash);color:var(--ink-soft)}
.pill-status-to_do{background:var(--wash);color:var(--ink-soft)}
.pill-status-in_progress{background:var(--info-wash);color:var(--info)}
.pill-status-waiting_on_client{background:var(--warn-wash);color:var(--warn)}
.pill-status-under_review{background:var(--azure-wash);color:var(--azure-ink)}
.pill-status-sent_back{background:var(--danger-wash);color:var(--danger)}
.stepname.ticked{text-decoration:line-through;color:var(--ink-faint)}
.steprow input[type=checkbox]{accent-color:var(--azure);width:1rem;height:1rem;flex:none}
/* A small table inside a panel, e.g. the VAT return schedule. */
.minitable{margin-top:.4rem;font-size:.85rem}
.minitable th{padding:.25rem .4rem .3rem 0;font-size:.68rem}
.minitable td{padding:.35rem .4rem .35rem 0;border-bottom:1px solid var(--rule-soft)}
.minitable tr:last-child td{border-bottom:0}
.minitable tbody tr:hover{background:none}
/* Service, team, who in that team, then the three dates — one grid so the whole
   sheet reads as a table down the page rather than six separate rows. */
.servicerow,.servicehead{
  display:grid;
  grid-template-columns:minmax(7.5rem,1fr) 10rem 7.5rem 9rem 8rem 9.5rem 7rem 12.5rem;
  gap:.5rem;align-items:center;
}
.servicehead{
  font-size:.68rem;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:600;padding:.35rem .5rem .2rem;
}
.servicerow .checkline{min-width:0;align-items:flex-start}
/* A service's name is the label for its whole row, so it wraps rather than
   truncating: "Corporation Tax Return (…" is not something to have to hover. */
.servicerow .checkline .name{white-space:normal;overflow:visible;text-overflow:clip}
.servicerow .checkline input{margin-top:.15rem}
.miniselect{max-width:15rem;font-size:.8rem;padding:.3rem .5rem}
/* "21 days after" — a number and its unit, reading as one control. */
.targetcell{display:grid;grid-template-columns:3.6rem 1fr;gap:.3rem;align-items:center;min-width:0}
.tinynum{font-size:.8rem;padding:.3rem .4rem;width:100%;min-width:0;text-align:right}
/* The date a service is wanted by, where the practice types it rather than the
   rule working it out. Blank is the normal state, so it sits quietly. */
.tinydate{font-size:.8rem;padding:.25rem .35rem;width:100%;min-width:0}
.targetcell .miniselect{max-width:none}
/* "Repeat every [3] [weeks]" — a number and its unit on one line. */
.everyrow{display:grid;grid-template-columns:5rem 1fr;gap:.5rem;align-items:center}
/* A target that lands after the deadline protects nothing, so it is not drawn
   quietly in grey like an ordinary date. */
.targetbad{color:var(--danger);font-weight:600}
/* A card that came back for correction, sitting in In progress beside work
   that is simply being done — so the card itself has to look different, not
   only carry a chip. A warm tint and a red left edge read as "this one is
   different" from across a board without shouting at anybody. */
.jobcard.returned{
  border-color:var(--danger);
  border-left-width:3px;
  background:linear-gradient(90deg,var(--danger-wash) 0 1.75rem,var(--card) 4.5rem);
}
.jobcard.returned:hover{border-color:var(--danger)}
.returnedchip{
  display:inline-block;align-self:flex-start;margin:.15rem 0 .1rem;
  font-size:.7rem;font-weight:600;letter-spacing:.02em;
  color:var(--danger);background:var(--wash);
  border:1px solid var(--danger);border-radius:999px;padding:.05rem .4rem;
}
/* The performance score. Big enough to read at a glance, because it is the one
   number on the page somebody came for. */
.bigscore{
  font-size:2.6rem;font-weight:700;line-height:1;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;color:var(--ink);
}
/* No score is not a small score. A dash set at 2.6rem reads as a rule across
   the card, so the absence gets its own quieter treatment. */
.bigscore.none{font-size:1.3rem;font-weight:600;color:var(--ink-faint)}
/* The four bands. A word beside the number, because a bare score invites an
   argument about the second decimal place and a word invites a conversation
   about the work. */
.pill.band-strong{background:var(--ok-wash);color:var(--ok)}
.pill.band-good{background:var(--wash);color:var(--ink-soft)}
.pill.band-watch{background:var(--warn-wash);color:var(--warn)}
.pill.band-poor{background:var(--danger-wash);color:var(--danger)}
.pill.band-none{background:var(--wash);color:var(--ink-faint)}
/* One row per company: the name reads as a link, the whole row is clickable. */
.clientpatch tr.rowlink{cursor:pointer}
.clientpatch tr.rowlink:hover{background:var(--wash)}
.clientpatch .nm{font-weight:600;color:inherit;text-decoration:none}
.clientpatch tr.rowlink:hover .nm{color:var(--azure-ink)}
.clientpatch td{vertical-align:top}
/* A button that reads as a link, for a change tucked under a control. */
.linkish{
  background:none;border:0;padding:0;color:var(--azure-ink);cursor:pointer;
  text-align:left;text-decoration:underline;text-underline-offset:2px;font:inherit;
}
.linkish:disabled{color:var(--ink-faint);cursor:default;text-decoration:none}
/* One person at a client: who they are on the left, what to do about them on
   the right. */
.contactrow{
  display:flex;justify-content:space-between;align-items:flex-start;gap:.75rem;
  padding:.55rem .1rem;border-bottom:1px solid var(--rule-soft);
}
.contactrow:last-child{border-bottom:0}
.contactrow .nm{font-weight:600;display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.contactrow .sub{font-size:.82rem;color:var(--ink-soft);margin-top:.1rem}
/* A labelled group of inputs inside a long form. */
.fieldset{
  display:flex;flex-direction:column;gap:.75rem;
  border:1px solid var(--rule);border-radius:8px;padding:.85rem;
}
.tabs{display:flex;gap:.25rem;padding:.25rem;background:var(--wash);border-radius:8px;margin-bottom:1rem}
.tab{
  flex:1;padding:.4rem .7rem;border-radius:6px;border:0;background:none;cursor:pointer;
  font-family:inherit;font-size:.85rem;font-weight:500;color:var(--ink-soft);
}
.tab.on{background:var(--card);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.08)}
/* --------------------------------------------- what stops a job finishing */
.notice-card{border-left:3px solid var(--warn)}
.stopping{margin:.4rem 0 0;padding-left:1.1rem;font-size:.875rem;color:var(--ink-soft)}
.stopping li{margin-bottom:.2rem}
/* --------------------------------------------------------- list filters */
.filters{
  display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;
  padding:.5rem .6rem;background:var(--wash);border-radius:8px;margin-bottom:.75rem;
}
.filters input[type=search]{min-width:12rem;flex:1 1 12rem}
.filters select{flex:0 1 12rem}
/* -------------------------------------------------- the command centre */
/* A number, its name, and somewhere to go. Tone carries meaning on its own —
   the count is bold and coloured, so a red 11 reads before the word does. */
.tiles{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr))}
.tile{
  display:flex;flex-direction:column;gap:.15rem;padding:.7rem .8rem;
  background:var(--wash);border-radius:10px;text-decoration:none;color:inherit;
  border:1px solid transparent;
}
.tile:hover{border-color:var(--azure);background:var(--azure-wash)}
.tile:focus-visible{outline:2px solid var(--azure);outline-offset:2px}
.tile .n{
  font-family:var(--serif);font-size:1.7rem;font-weight:600;line-height:1.1;
  font-variant-numeric:tabular-nums;
}
.tile .l{font-size:.75rem;color:var(--ink-faint);font-weight:500}
.tile-bad .n{color:var(--danger)}
.tile-warn .n{color:var(--warn)}
/* Nought is worth showing — it is the good answer — but not worth shouting. */
.tile.quiet .n{color:var(--ink-faint);font-weight:500}
.joblist td.num{text-align:right;font-variant-numeric:tabular-nums;width:5.5rem}
.joblist td.num.bad{color:var(--danger);font-weight:600}
.joblist td.num.warn{color:var(--warn);font-weight:600}
/* ---------------------------------------------------------- the inbox */
/* Two columns: what needs doing, and what has happened. On a phone they stack,
   needs-doing first, because that is the half somebody acts on. */
.inbox{display:grid;gap:1rem;grid-template-columns:minmax(0,3fr) minmax(0,2fr);align-items:start}
@media (max-width:900px){.inbox{grid-template-columns:1fr}}
.band + .band{margin-top:1rem;padding-top:.9rem;border-top:1px solid var(--rule-soft)}
.band h4{
  margin:0 0 .4rem;font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:600;display:flex;align-items:center;gap:.4rem;
}
.band h4 .n{
  background:var(--wash);border-radius:999px;padding:.05rem .45rem;
  font-size:.72rem;letter-spacing:0;
}
.band-overdue h4,.band-chase h4{color:var(--danger)}
.band-overdue h4 .n,.band-chase h4 .n{background:var(--danger-wash);color:var(--danger)}
.band-today h4{color:var(--warn)}
.band-today h4 .n{background:var(--warn-wash);color:var(--warn)}
.attn{
  display:grid;grid-template-columns:1fr auto;gap:0 .75rem;padding:.45rem .55rem;
  border-radius:8px;text-decoration:none;color:inherit;
}
.attn:hover{background:var(--wash)}
.attn .what{font-size:.875rem;font-weight:600;grid-column:1}
.attn .why{font-size:.78rem;color:var(--ink-faint);grid-column:1}
.attn .when{
  font-size:.78rem;color:var(--ink-faint);grid-column:2;grid-row:1 / span 2;
  align-self:center;white-space:nowrap;font-variant-numeric:tabular-nums;
}
.attn .when.overdue{color:var(--danger);font-weight:600}
.notes{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.note{border-bottom:1px solid var(--rule-soft)}
.note:last-child{border-bottom:0}
.note a{
  display:grid;grid-template-columns:1fr auto;gap:0 .75rem;
  padding:.55rem;border-radius:8px;text-decoration:none;color:inherit;
}
.note a:hover{background:var(--wash)}
.note .what{font-size:.875rem;grid-column:1}
.note .why{font-size:.8rem;color:var(--ink-soft);grid-column:1}
.note .when{font-size:.75rem;color:var(--ink-faint);grid-column:2;white-space:nowrap}
/* Unread is carried by weight and a mark, not colour alone. */
.note.unread .what{font-weight:600}
.note.unread .what::before{
  content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--azure);margin-right:.45rem;vertical-align:middle;
}
.nav-item .badge{
  margin-left:auto;background:var(--danger);color:#fff;border-radius:999px;
  font-size:.68rem;font-weight:600;padding:.05rem .4rem;line-height:1.5;
}
/* ----------------------------------------------------------- blockers */
/* Why a job is not moving. It is not an error — often it is the client's turn,
   not the practice's — so it reads as information until a chase falls due. */
.blockcard{border-left:3px solid var(--warn)}
.blockcard.overdue{border-left-color:var(--danger)}
.blockcard .asked{
  font-size:.9rem;background:var(--wash);border-radius:8px;padding:.6rem .75rem;
}
.jobcard .who.blocked{
  color:var(--warn);font-weight:600;white-space:normal;
}
.jobcard .who.blocked.chase{color:var(--danger)}
.jobcard .who.blocked + .who{border-top:0;margin-top:.15rem;padding-top:0}
/* ------------------------------------------------------------ reviews */
/* A review is a record with something said in it, so the words get the room —
   the verdict is a label above them, not a badge competing with them. */
.reviews{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.9rem}
.review{padding-left:.7rem;border-left:3px solid var(--rule)}
.review-approved{border-left-color:var(--ok)}
.review-returned{border-left-color:var(--warn)}
.review .verdict{display:block;font-size:.85rem;font-weight:600}
.review-approved .verdict{color:var(--ok)}
.review-returned .verdict{color:var(--warn)}
.review .by{display:block;font-size:.78rem;color:var(--ink-faint);margin-top:.05rem}
.review .rung{
  margin-left:.4rem;background:var(--wash);border-radius:999px;padding:.05rem .4rem;
}
.review .when{margin-left:.4rem;font-variant-numeric:tabular-nums}
.review .said{
  margin-top:.35rem;font-size:.875rem;color:var(--ink);white-space:pre-wrap;
}
/* ------------------------------------------------------------ history */
/* A record, so it reads as a list of facts rather than a feed: one line each,
   the event first and who did it after, with the rule between entries doing the
   separating rather than a box round every row. */
.history{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.hist{
  display:flex;flex-wrap:wrap;gap:.25rem 1rem;align-items:baseline;
  padding:.5rem 0;border-bottom:1px solid var(--rule-soft);font-size:.875rem;
}
.hist:last-child{border-bottom:0}
.hist .what{flex:1;min-width:14rem;color:var(--ink)}
.hist .who{color:var(--ink-faint);font-size:.8rem;white-space:nowrap}
.hist .when{margin-left:.4rem;font-variant-numeric:tabular-nums}
/* The two that change who is responsible are the ones people scan for. */
.hist-assigned .what,.hist-unassigned .what{font-weight:600}
.hist-completed .what{color:var(--ok);font-weight:600}
.hist-steps_changed .what{color:var(--ink-soft)}
/* -------------------------------------------------------------- toast */
.toasts{position:fixed;right:1rem;bottom:1rem;z-index:100;display:flex;flex-direction:column;gap:.5rem;max-width:min(24rem,calc(100vw - 2rem))}
.toast{
  background:var(--card);border:1px solid var(--rule);border-left-width:3px;
  border-radius:8px;box-shadow:var(--shadow);padding:.7rem .9rem;font-size:.875rem;
}
.toast-ok{border-left-color:var(--ok)}
.toast-error{border-left-color:var(--danger)}
.notice{border-radius:8px;padding:.7rem .9rem;font-size:.85rem;border:1px solid}
.notice-error{background:var(--danger-wash);border-color:var(--danger-rule);color:var(--ink)}
.notice-info{background:var(--brand-wash);border-color:var(--brand);color:var(--ink)}
/* --------------------------------------------------------------- misc */
.muted{color:var(--ink-faint)}
.small{font-size:.82rem}
.mono{font-family:var(--mono);font-size:.85em}
.center-page{display:grid;place-items:center;min-height:100vh;padding:1.5rem;background:var(--wash)}
.login-card{width:100%;max-width:380px}
.logo-preview{
  width:200px;min-height:88px;flex:none;display:grid;place-items:center;
  border:1px dashed var(--rule);border-radius:8px;padding:.75rem;background:var(--wash);
}
.logo-preview img{max-width:100%;max-height:64px;object-fit:contain;display:block}
.spin{
  width:1rem;height:1rem;border:2px solid currentColor;border-right-color:transparent;
  border-radius:50%;display:inline-block;animation:spin .6s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spin{animation-duration:2s}}
.steps{display:flex;flex-direction:column;gap:.5rem}
@media (max-width:820px){
  .sidebar{
    position:fixed;inset:0 auto 0 0;z-index:40;transform:translateX(-100%);
    transition:transform .18s ease, visibility .18s;box-shadow:var(--shadow);
    /* Off-screen is not enough on its own: without this the closed menu stays
       in the tab order and a keyboard user lands in links they cannot see. */
    visibility:hidden;
  }
  .sidebar.open{transform:none;visibility:visible}
  .btn.burger{display:inline-flex}
  .content{padding:3.5rem 1rem 4rem}
  /* Three columns will not fit a phone; stack them and hide the header that
     was labelling them. */
  .servicerow{grid-template-columns:1fr;gap:.25rem;padding-bottom:.6rem}
  .jobline{grid-template-columns:1fr auto;gap:.25rem .6rem;padding-bottom:.6rem}
  .servicehead{display:none}
  .servicerow .miniselect{max-width:none;margin-left:2.1rem}
  .servicerow .targetcell{margin-left:2.1rem}
  .servicerow .targetcell .miniselect{margin-left:0}
  .servicehead{display:none}
}
@media (prefers-reduced-motion:reduce){.sidebar{transition:none}}
/* ------------------------------------------------------- the client card */
/* One page per client: who they are and their dates across the top, the
   services down the left, the shared notes down the right. */
.clienthead{
  display:flex;gap:1rem;align-items:flex-start;justify-content:space-between;
  margin:.5rem 0 1rem;
}
.clienthead h1{margin:0}
.clienthead p{margin:.15rem 0 0}
.keydates{
  display:grid;grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
  gap:.75rem;margin-bottom:1.25rem;
}
.keyblock{
  background:var(--card);border:1px solid var(--rule);border-radius:8px;
  padding:.7rem .85rem;box-shadow:var(--shadow);
}
.keyblock .rolelabel{display:block;margin-bottom:.35rem}
.kv{
  display:flex;justify-content:space-between;gap:1rem;align-items:baseline;
  font-size:.85rem;padding:.15rem 0;
}
.kv > span:first-child{color:var(--ink-faint);white-space:nowrap}
.kv > span:last-child{text-align:right;font-weight:500}
/* Seventy per cent to the work, thirty to the notes. minmax(0,…) on both so a
   long client name or an unbroken note cannot push its column wider than its
   share. */
.clientgrid{
  display:grid;grid-template-columns:minmax(0,7fr) minmax(0,3fr);
  gap:1rem;align-items:start;
}
@media (max-width: 900px){ .clientgrid{grid-template-columns:minmax(0,1fr)} }
/* A service is a closed line until somebody opens it. Nine open accordions
   would be the same wall of text the card is meant to replace. */
.serviceline{border:1px solid var(--rule);border-radius:8px;overflow:hidden}
.serviceline.open{border-color:var(--azure)}
.servicetop{
  display:flex;gap:.6rem;align-items:center;width:100%;flex-wrap:wrap;
  background:none;border:0;padding:.6rem .7rem;cursor:pointer;
  font:inherit;color:inherit;text-align:left;
}
.servicetop:hover{background:var(--wash)}
.servicetop .caret{color:var(--ink-faint);width:.8rem;flex:none}
.servicetop .nm{
  font-weight:600;font-size:.9rem;display:flex;align-items:center;gap:.4rem;
  flex-wrap:wrap;
}
@media (min-width: 700px){ .servicetop .nm{min-width:11rem} }
.servicetop .who{
  font-size:.78rem;color:var(--ink-faint);flex:1 1 12rem;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.servicetop .tail{
  display:flex;gap:.6rem;align-items:center;flex-wrap:wrap;margin-left:auto;
}
.servicetop .steps{
  background:var(--wash);border-radius:999px;padding:.05rem .4rem;
  color:var(--ink-soft);font-size:.72rem;
}
.servicebody{padding:.2rem .7rem .7rem 1rem;border-top:1px solid var(--rule-soft)}
@media (min-width: 700px){ .servicebody{padding-left:2rem} }
.approved{color:var(--ok);font-weight:600;margin:.4rem 0}
.steprow-edit{
  display:flex;gap:.4rem;align-items:center;flex-wrap:wrap;
}
.steprow-edit input[type=text],.steprow-edit input:not([type]){flex:1;min-width:0}
.steprow-edit .checkline{padding:.2rem .4rem;flex:none}
.notespanel .note{border-left:2px solid var(--rule);padding-left:.6rem}
.notespanel .note p:first-child{margin:0;font-size:.875rem;white-space:pre-wrap}
.notespanel .note p:last-child{margin:.15rem 0 0}
.linkbtn{
  background:none;border:0;padding:0 0 0 .4rem;font:inherit;font-size:inherit;
  color:var(--azure-ink);cursor:pointer;text-decoration:underline;
}
/* --------------------------------------------------- board or list view */
/* The same work, two ways of reading it. The board answers "where is
   everything"; the list answers "what is due next, across the lot", which the
   board cannot, because its columns are stages and a deadline runs across
   all of them. */
.viewbar{display:flex;gap:.5rem;align-items:flex-start;margin-bottom:1rem;flex-wrap:wrap}
.viewbar .tabstrip{margin-bottom:0;flex:1;min-width:0}
.viewbar .viewpick{flex:none}
.joblist tbody tr{cursor:pointer}
.joblist tbody tr:hover{background:var(--wash)}
.joblist td.nm{font-weight:600}
.joblist th{padding:0}
.joblist th button{
  display:flex;align-items:center;gap:.3rem;width:100%;
  background:none;border:0;padding:.55rem .85rem;cursor:pointer;
  font:inherit;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
  font-weight:600;color:var(--ink-faint);text-align:left;
}
.joblist th button:hover{color:var(--ink)}
.joblist th.sorted button{color:var(--azure-ink)}
.joblist th .arrow{font-size:.6rem}
@media (max-width: 800px){
}
/* The row of hand-off buttons under a job's steps. Sits inside the step card,
   separated by a rule rather than a heading: it is the end of that list, not a
   new thing on the page. */
.handoff {
  margin-top: .9rem;
  padding-top: .8rem;
  border-top: 1px solid var(--rule);
}
.handoff textarea { width: 100%; }
.handoff select { min-width: 12rem; }
/* Recurrence and reviewer, under a template's steps. Two rows of small
   controls rather than a form: these are settings somebody adjusts, not a
   thing they fill in. */
.rhythm { margin-top: .9rem; padding-top: .8rem; border-top: 1px solid var(--rule); }
.rhythm-row {
  display: flex; align-items: center; gap: .45rem;
  flex-wrap: wrap; margin-bottom: .5rem;
}
.rhythm-row:last-of-type { margin-bottom: 0; }
.handoff h4 {
  margin: 0 0 .55rem; font-size: .7rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint); font-weight: 600;
}
.handoff-ask {
  margin-top: .7rem; padding: .7rem .8rem;
  background: var(--wash); border-radius: 8px;
}
.handoff-ask textarea { width: 100%; }
/* Whose desk a job is on and who reviews it, under the status pill. A line of
   labelled facts rather than a card of its own — it is context for the pill
   above it, not a new subject. */
.reviewline {
  display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap;
  padding-top: 0; font-size: .85rem;
}
.reopened { padding-top: 0; }
.reopened p { margin: 0; color: var(--warn-ink, var(--ink)); }
/* The service tabs inside a person's board. A second strip under the people,
   quieter than they are: which desk you are looking at is the first question,
   which service the second. */
.servicestrip {
  margin: 0 0 1rem;
  background: transparent;
  border-bottom: 1px solid var(--rule-soft);
  border-radius: 0;
  padding: 0 0 .35rem;
  flex-wrap: wrap;
}
/* Who else an admin has given sight of one service on one client. A quiet line
   under the steps: it is an exception, and it should look like one. */
.grantrow {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  margin-top: .7rem; padding-top: .6rem; border-top: 1px dashed var(--rule-soft);
}
.chipx {
  background: none; border: 0; cursor: pointer; padding: 0 0 0 .35rem;
  color: inherit; opacity: .6; font: inherit; line-height: 1;
}
.chipx:hover { opacity: 1; color: var(--danger); }
/* Two ways to do one thing, with the better one first. The rule says "or"
   rather than stacking two unlabelled halves, which read as one form somebody
   has to fill in twice. */
.or-rule {
  display: flex; align-items: center; gap: .6rem;
  color: var(--ink-faint); font-size: .82rem; margin: .2rem 0;
}
.or-rule::before, .or-rule::after {
  content: ""; flex: 1; height: 1px; background: var(--rule-soft);
}

/* The test site says so, on every screen. Warm and unmissable, because the one
   failure it exists to prevent is somebody entering a real client into a copy. */
.testbar{
  position:sticky;top:0;z-index:60;
  background:#7A4B00;color:#fff;
  font-size:.8rem;font-weight:600;letter-spacing:.01em;
  padding:.4rem .9rem;text-align:center;
}
.testbar a{color:#fff;text-decoration:underline;margin-left:.6rem;font-weight:500}
