/* =====================================================================
   Always Energy CRM — Nocturne.

   Written from the design system rather than carried over. The old
   stylesheet was 1,900 lines describing a layout built on different
   assumptions, with tokens sprayed over it; keeping it would have meant
   keeping the assumptions.

   Order: fonts, tokens, themes, primitives, shell, components, screens.
   ================================================================== */

/* ---- Inter, self-hosted. One variable file per subset. ---- */
@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;
  src:url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;
  src:url('fonts/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Phosphor';font-style:normal;font-weight:400;font-display:block;
  src:url('fonts/phosphor.woff2') format('woff2')}

.ph{font-family:'Phosphor';font-style:normal;font-weight:400;line-height:1;speak:none;
  display:inline-block;vertical-align:-.125em;-webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;flex:0 0 auto}

/* =====================================================================
   Tokens
   ================================================================== */
:root{
  --color-bg:#161826;
  --color-surface:#232532;
  --color-text:#e9e9ed;
  --color-accent:#9184d9;
  --color-accent-2:#a7a1db;
  --color-divider:color-mix(in srgb,#e9e9ed 16%,transparent);
  --color-muted:var(--color-text-muted,color-mix(in srgb,var(--color-text) 55%,transparent));

  --color-neutral-100:#f3f5fe; --color-neutral-200:#e4e7f5; --color-neutral-300:#cfd3e5;
  --color-neutral-400:#b2b6ca; --color-neutral-500:#9397ab; --color-neutral-600:#75798c;
  --color-neutral-700:#595d6c; --color-neutral-800:#3f424d; --color-neutral-900:#292b31;

  --color-accent-100:#f5f4ff; --color-accent-200:#e7e5fe; --color-accent-300:#d2cefd;
  --color-accent-400:#b5abfc; --color-accent-500:#968ae0; --color-accent-600:#796cbf;
  --color-accent-700:#5d5294; --color-accent-800:#423a6a; --color-accent-900:#2b2741;

  --color-accent-2-100:#f5f4ff; --color-accent-2-200:#e7e5fe; --color-accent-2-300:#d2cefd;
  --color-accent-2-400:#b5afe8; --color-accent-2-500:#9690c9; --color-accent-2-600:#7972a9;
  --color-accent-2-700:#5c5783; --color-accent-2-800:#423e5d; --color-accent-2-900:#2b293a;

  /* Status. An addition to Nocturne, which has none — taken from the
     design's own prototype, and used as an icon, a left bar and a 13%
     tint of the surface, never as a flood. */
  --color-ok:#3f9e5a; --color-info:#5b8ec9; --color-warn:#c2902f; --color-danger:#c9564f;

  --font-body:'Inter',system-ui,sans-serif;
  --font-heading:'Inter',system-ui,sans-serif;
  --font-heading-weight:500;
  --font-mono:ui-monospace,'SF Mono',Menlo,Consolas,monospace;

  --space-1:2.8px; --space-2:5.6px; --space-3:8.4px;
  --space-4:11.2px; --space-6:16.8px; --space-8:22.4px;
  --radius-sm:4px; --radius-md:8px; --radius-lg:14px;

  /* Rings drawn FROM the text colour rather than from a fixed grey, so a
     panel edge is as visible on a blue or a green theme as it is here. A
     flat #3f424d all but vanishes against a darker ground. */
  --shadow-sm:0 0 0 1px color-mix(in srgb,var(--color-text) 20%,transparent);
  --shadow-md:0 0 0 1px color-mix(in srgb,var(--color-text) 28%,transparent),
              0 6px 18px rgba(0,0,0,.55);
  --shadow-lg:0 0 0 1px color-mix(in srgb,var(--color-text) 38%,transparent),
              0 16px 40px rgba(0,0,0,.65);

  /* The rail's width is a token so the shell, the scrim and every
     content-width calculation read the same number. */
  --rail-w:234px;
  --topbar-h:58px;
}
html[data-nav="collapsed"]{--rail-w:60px}

/* =====================================================================
   Primitives
   ================================================================== */
*,*::before,*::after{box-sizing:border-box}
/* `hidden` has to beat a component's own display, or setting it does
   nothing to anything laid out with flex or grid — which here is most
   things. Caught by an empty error bar showing above a form. */
[hidden]{display:none!important}
html,body{height:100%}
body{margin:0;background:var(--color-bg);color:var(--color-text);
  font-family:var(--font-body);font-size:15px;line-height:1.55;font-weight:400;
  -webkit-font-smoothing:antialiased;overflow:hidden}

h1,h2,h3,h4{font-family:var(--font-heading);font-weight:var(--font-heading-weight);
  line-height:1.12;letter-spacing:-.015em;margin:0 0 var(--space-2)}
h1{font-size:22px} h2{font-size:19px} h3{font-size:16px}
p{margin:0 0 var(--space-3)}
a{color:var(--color-accent);text-decoration:none;text-underline-offset:3px}
a:hover{color:var(--color-accent-300)}
img{display:block;max-width:100%}
:focus{outline:none}
:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
::selection{background:color-mix(in srgb,var(--color-accent) 30%,transparent)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--color-neutral-800);border-radius:8px}
::-webkit-scrollbar-track{background:transparent}

.muted{color:var(--color-muted)}
.lede{font-size:14px;color:var(--color-muted);margin:0}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.kicker{font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--color-muted)}
.empty{padding:26px 18px;text-align:center;color:var(--color-muted);font-size:13.5px}
/* !important because .icon-btn sets display below this, and a button that
   is only for phones must lose that argument on every other screen. */
.only-mob{display:none!important}

/* — buttons: outlined, per Nocturne — */
.btn,.btn-primary,.btn-secondary,.icon-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;cursor:pointer;
  font-family:var(--font-heading);font-weight:var(--font-heading-weight);
  font-size:14px;line-height:1.2;color:var(--color-text);background:transparent;
  border:1px solid transparent;border-radius:var(--radius-md);padding:7px 13px;
  white-space:nowrap}
.btn-primary{color:var(--color-accent);border-color:var(--color-accent)}
.btn-primary:hover{background:color-mix(in srgb,var(--color-accent) 12%,transparent)}
.btn-primary:active{background:color-mix(in srgb,var(--color-accent) 22%,transparent)}
.btn-secondary{border-color:var(--color-divider)}
.btn-secondary:hover{background:color-mix(in srgb,var(--color-text) 7%,transparent)}
.btn-danger{color:var(--color-danger);border-color:color-mix(in srgb,var(--color-danger) 55%,transparent)}
.btn-danger:hover{background:color-mix(in srgb,var(--color-danger) 12%,transparent)}
.btn:disabled,.btn-primary:disabled,.btn-secondary:disabled{opacity:.45;cursor:not-allowed}
.icon-btn{width:34px;height:34px;padding:0;color:var(--color-muted)}
.icon-btn:hover{color:var(--color-text);background:color-mix(in srgb,var(--color-text) 8%,transparent)}

/* — inputs — */
/* Every text-like input type, listed. `tel` was missing, so a telephone
   box was drawn by the browser while everything beside it was drawn by
   this — and the hover and focus rules below, which match a bare `input`,
   half-styled it on top of that.

   Listed rather than written as input:not([type=checkbox])… on purpose:
   the negation chain would outweigh `.topbar-search input` and undo it. */
.input,input[type=text],input[type=email],input[type=search],input[type=password],
input[type=number],input[type=date],input[type=time],input[type=tel],input[type=url],
input[type=datetime-local],input[type=month],input[type=week],select,textarea{
  width:100%;min-height:36px;padding:6px 10px;font:inherit;font-size:14px;
  color:var(--color-text);caret-color:var(--color-accent);background:var(--color-bg);
  border:1px solid var(--color-divider);border-radius:var(--radius-md)}
select{cursor:pointer}
textarea{min-height:90px;line-height:1.6;resize:vertical}
.input:hover,input:hover,select:hover,textarea:hover{
  border-color:color-mix(in srgb,var(--color-text) 45%,transparent)}
.input:focus,input:focus,select:focus,textarea:focus{border-color:var(--color-accent)}
/* align-content:start, or a field whose neighbour carries a hint is
   stretched to match it — and what stretches is the input, which then
   stands a few pixels taller than every other box on the row. */
.field{display:grid;gap:5px;align-content:start}
.field-html{font-size:13px}
/* When (WHEN): the summary reads like a field, the panel opens beneath it. */
.when-sum{display:flex;align-items:center;gap:10px;width:100%;min-height:38px;padding:7px 12px;font:inherit;font-size:14px;text-align:left;cursor:pointer;
  color:var(--color-text);background:var(--color-surface);border:0;border-radius:var(--radius-md);box-shadow:inset 0 0 0 1px var(--color-divider)}
.when-sum:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}
.when-sum .when-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.when-sum .ph{font-size:18px;color:var(--color-accent);flex:0 0 auto}
.when.is-empty .when-text{color:var(--color-muted)}
.when-panel{margin-top:8px;padding:10px 12px 8px;border-radius:var(--radius-md);box-shadow:inset 0 0 0 1px var(--color-divider);background:color-mix(in srgb,var(--color-text) 3%,transparent)}
.when-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px 14px}
.when-grid .field{margin:0}
.when-all{margin-top:8px}
.check.is-off{opacity:.45;cursor:default}
.bubble-to{font-size:11.5px;color:var(--color-muted);margin:2px 0 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tag.tag-btn{cursor:pointer;font:inherit;font-size:11px;letter-spacing:.02em}
.tag.tag-btn:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}
/* Attachments on a message (ATTACH): the chosen list, and the picker's tabs and rows. */
.att-list{display:flex;flex-direction:column;gap:4px}
.att-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--radius-md);box-shadow:inset 0 0 0 1px var(--color-divider);font-size:13px;min-width:0}
.att-row>.ph{font-size:16px;color:var(--color-accent);flex:0 0 auto}
.att-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.att-meta{flex:0 1 auto;font-size:11.5px;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:45%}
.att-row .icon-btn{width:28px;height:28px;flex:0 0 auto}
.att-tabs{display:flex;align-items:center;gap:2px;flex-wrap:wrap;margin:0 0 10px;border-bottom:1px solid var(--color-divider)}
.att-tab{display:inline-flex;align-items:center;gap:6px;padding:8px 12px;font:inherit;font-size:13px;color:var(--color-muted);background:none;border:0;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer}
.att-tab.on{color:var(--color-text);border-bottom-color:var(--color-accent)}
.att-search{margin-left:auto;margin-bottom:6px;max-width:220px}
.att-up{margin-bottom:6px}
.att-pick-list{display:flex;flex-direction:column;gap:4px;max-height:min(52vh,460px);overflow-y:auto}
.att-pick{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--radius-md);cursor:pointer;min-width:0}
.att-pick:hover{background:color-mix(in srgb,var(--color-text) 5%,transparent)}
.att-pick input{flex:0 0 auto}
.att-thumb{width:40px;height:40px;object-fit:cover;border-radius:4px;flex:0 0 auto;background:var(--color-neutral-800)}
.att-ico{font-size:22px;color:var(--color-accent);width:40px;text-align:center;flex:0 0 auto}
.att-words{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
.att-words .att-meta{max-width:none}
.att-pick .icon-btn{width:28px;height:28px;flex:0 0 auto}
.brace-fix{margin-top:4px}
.brace-fix .check{align-items:flex-start}
.brace-fix code{font-size:12px}
.field>label,.field-l{font-size:12px;color:color-mix(in srgb,var(--color-text) 70%,transparent)}
.field-note{font-size:11.5px;color:var(--color-muted);line-height:1.45}

/* — tooltips: what an icon on its own means — */
.tip{position:fixed;top:0;left:0;z-index:300;opacity:0;pointer-events:none;
  transition:opacity .08s ease;padding:5px 9px;border-radius:6px;max-width:260px;
  font-family:var(--font-body);font-size:11.5px;font-weight:500;line-height:1.35;
  background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-md)}
.tip.on{opacity:1}
@media (prefers-reduced-motion:reduce){.tip{transition:none}}

/* — the theme picker, on the account screen —
   Each swatch carries its own data-theme, so a tile paints in the theme it
   offers rather than in the one already in use. */
.grid-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px 16px}
.theme-group-h{display:flex;align-items:center;gap:8px;margin:14px 0 8px;
  font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:color-mix(in srgb,var(--color-text) 48%,transparent)}
.theme-group-h .rule{flex:1;height:1px;background:var(--color-divider)}
/* Five to a row, whatever the rail is doing: twenty themes, four even
   rows. The tiles give up width before the count does; only a narrow
   screen drops columns. */
/* One tab a design (070); the palettes of the open design in the grid below. */
.theme-fams{display:flex;gap:2px;flex-wrap:wrap;margin-bottom:10px;border-bottom:1px solid var(--color-divider)}
.theme-fam{display:inline-flex;align-items:center;gap:6px;padding:8px 12px;font:inherit;font-size:13px;font-family:var(--font-heading);
  color:var(--color-muted);background:none;border:0;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer}
.theme-fam:hover{color:var(--color-text)}
.theme-fam.on{color:var(--color-text);border-bottom-color:var(--color-accent)}
.theme-fam .ph{font-size:14px;color:var(--color-accent)}
.theme-fam-n{font-size:11px;color:var(--color-muted);font-variant-numeric:tabular-nums;padding:1px 6px;border-radius:999px;
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.theme-fam-note{margin:0 0 10px}
.theme-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px}
@media (max-width:1100px){.theme-grid{grid-template-columns:repeat(4,minmax(0,1fr))}}
@media (max-width:820px){.theme-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:520px){.theme-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
.theme-tile{min-width:0}
.theme-name,.theme-note{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.theme-tile{display:flex;flex-direction:column;gap:8px;padding:10px;cursor:pointer;
  background:var(--color-bg);border:0;border-radius:var(--radius-md);
  color:var(--color-text);font:inherit;text-align:left;
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.theme-tile:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}
.theme-tile.on{box-shadow:inset 0 0 0 1px var(--color-accent)}
.theme-row{display:flex;align-items:center;gap:6px}
/* Four colours, four boxes: the ground, the panels, the accent, the text.
   The row carries the theme, so each box paints in it. */
.sw{width:18px;height:18px;border-radius:5px;flex:0 0 auto;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-text) 20%,transparent)}
.theme-spacer{flex:1}
.theme-row .ph{font-size:15px;color:var(--color-accent)}
.theme-words{display:flex;flex-direction:column;gap:1px;text-align:left}
.theme-name{font-family:var(--font-heading);font-size:13px}
.theme-note{font-size:11px;color:var(--color-muted);line-height:1.35}

/* =====================================================================
   The diary

   People down the side, days across the top. The grid scrolls sideways
   inside its own panel rather than pushing the page, because the answer
   somebody wants — who is free on Thursday — is in the columns.
   ================================================================== */
.diary-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.diary-move{display:flex;gap:4px}
.diary-move .icon-btn{width:30px;height:30px;box-shadow:inset 0 0 0 1px var(--color-divider)}
.diary-move .btn-secondary{padding:5px 12px;font-size:13px}
.diary-when{font-family:var(--font-heading);font-size:14px;margin-left:4px}
.diary-spacer{flex:1}
.diary-pick{width:auto;min-height:32px;padding:4px 8px;font-size:13px}

.diary-panel{padding:0;overflow:hidden}
.diary-scroll{overflow-x:auto;overflow-y:hidden}
.diary-grid{display:grid}
.dg-head{padding:8px 10px;border-bottom:1px solid var(--color-divider);
  border-left:1px solid var(--color-divider)}
.dg-who{border-left:0;font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:color-mix(in srgb,var(--color-text) 55%,transparent)}
.dg-dow{display:block;font-size:12px}
.dg-dom{display:block;font-size:11px;color:var(--color-muted)}
/* A day nobody works reads as one, and today is marked without shouting. */
.dg-head.off,.dg-cell.off{background:color-mix(in srgb,var(--color-text) 4%,transparent)}
.dg-head.today{box-shadow:inset 0 2px 0 var(--color-accent)}
.dg-name{padding:8px 10px;border-bottom:1px solid var(--color-divider)}
.dg-n{display:block;font-size:12.5px}
.dg-r{display:block;font-size:10.5px;color:var(--color-muted)}
.dg-cell{display:flex;flex-direction:column;gap:3px;min-height:54px;padding:4px;
  border-bottom:1px solid var(--color-divider);border-left:1px solid var(--color-divider);
  cursor:pointer}
.dg-cell:hover{background:color-mix(in srgb,var(--color-accent) 7%,transparent)}
.dg-block{display:block;width:100%;text-align:left;padding:4px 6px;border:0;
  border-radius:var(--radius-sm);cursor:pointer;color:#f3f5fe;font:inherit;
  box-shadow:inset 2px 0 0 rgba(255,255,255,.35)}
.dg-b1{display:block;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dg-b2{display:block;font-size:10px;opacity:.85;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}

/* =====================================================================
   Jobs

   No styles of its own for the list any more — it is the same split, the
   same rows and the same record as Leads and Clients, and the only thing
   a job needs that they do not is a mark for running late.
   ================================================================== */
/* Down the row's own edge, in the 2px the selected row uses for its accent
   — so a late job is visible while scrolling and gives that stripe up the
   moment it is the row you are reading. */
.row.late{box-shadow:inset 2px 0 0 var(--color-warn)}
.sort-btn{padding:5px 11px;font-size:12.5px}

/* The job's own checklist. */
.tasks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.task{display:flex;align-items:center;gap:8px;padding:4px 0;
  border-bottom:1px solid var(--color-divider)}
.task:last-child{border-bottom:0}
.task .check{flex:1;min-height:28px;align-items:flex-start}
.task-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.task-t{font-size:13px}
/* Who and when, under the title rather than beside it: most tasks have
   neither, and an empty column reads as missing rather than as not
   applicable. */
.task-m{font-size:11px;color:var(--color-muted)}
.task.done .task-t{color:var(--color-muted);text-decoration:line-through}
.task.done .task-m{opacity:.6}
.task.late .task-m{color:var(--color-warn)}
/* A task waiting on something else reads as not-yet-yours: dimmed, and
   with the thing it is waiting for named underneath. Not disabled — the
   blocker sometimes turns out not to matter, and that is a judgement for
   whoever is standing there. */
.task.blocked .task-t{color:color-mix(in srgb,var(--color-text) 62%,transparent)}
.task.blocked .task-m{color:var(--color-accent)}

/* Waiting on the row above it: indented, with an elbow pointing back at
   what it is waiting for. The indent is the sentence — a nested row says
   "not until that one" without needing the words. */
.task.under{padding-left:calc(var(--tdepth, 1) * 22px)}
.task-el{flex:0 0 auto;margin-right:-2px;font-size:13px;color:var(--color-accent);
  opacity:.75}
/* The chain reads as lit when there is something on the other end of it.
   A colour change alone is not enough on a 14px ghost icon — it reads as
   a slightly different grey. The accent-tinted ground is how every other
   "this one is on" state in the CRM is drawn (the menu, the theme
   tiles, the chips), so this matches rather than inventing a signal. */
.td-btn.on{color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 15%,transparent)}
.td-btn.on:hover{color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 26%,transparent)}

/* =====================================================================
   Tasks since 049 — every row the same width

   A row is the title with where it belongs above it and who/when under
   it, a labelled state pill, and its buttons. Nothing is indented by
   dependency any more; what a task waits on is a pressable line under
   it, leading to the before-and-after view. A group is a heading with a
   caret, summarised from its subtasks, which sit under it when opened.
   ================================================================== */
.task{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:4px 10px}
.task .check{flex:none;min-width:0;align-items:flex-start}
/* The row reads; only the box ticks. */
.task .task-chk{cursor:default}
.task .task-chk input{cursor:pointer}
.task .task-b.task-open{cursor:pointer}
.task .task-b.task-open:hover .task-t{color:var(--color-accent)}
.task-acts{display:flex;align-items:center;gap:1px;flex-wrap:nowrap}
.task-path{font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--color-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.task-wait{display:flex;flex-wrap:wrap;align-items:center;gap:2px 6px;margin-top:2px;padding:0;border:0;
  background:none;font:inherit;font-size:11.5px;color:var(--color-accent);cursor:pointer;text-align:left}
.task-wait .ph{font-size:13px}
.task-wait:hover > span:first-of-type{text-decoration:underline}
.task-wait.over{color:var(--color-warn)}
.task-wait-n{color:var(--color-muted);font-size:11px}
.task-wait-n::before{content:"·";margin-right:6px}
.task.done .task-wait{display:none}

/* The state, as a word with an icon — colour alone is not a label. */
.tstate{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;letter-spacing:.03em;padding:2px 8px;
  border-radius:999px;white-space:nowrap;background:color-mix(in srgb,var(--color-text) 8%,transparent);
  color:var(--color-muted)}
.tstate .ph{font-size:12px}
.tstate.ready{background:color-mix(in srgb,var(--color-ok) 16%,transparent);color:var(--color-ok)}
.tstate.waiting{background:color-mix(in srgb,var(--color-accent) 14%,transparent);color:var(--color-accent)}
.tstate.in_progress{background:color-mix(in srgb,var(--color-accent) 24%,transparent);color:var(--color-accent)}
.tstate.overridden{background:color-mix(in srgb,var(--color-warn) 16%,transparent);color:var(--color-warn)}
.tstate.complete{opacity:.7}
.tstate.sm{padding:1px 7px;font-size:10px}

/* The group heading. */
.task.grp{grid-template-columns:28px minmax(0,1fr) auto auto;padding:7px 0 5px}
.task.grp .task-t{font-weight:500}
.task.grp .task-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.grp-i{font-size:13px;color:var(--color-accent);margin-right:5px;vertical-align:-1px}
.grp-tog{width:28px;height:28px;padding:0;border:0;background:none;color:var(--color-muted);cursor:pointer;
  display:grid;place-items:center;border-radius:var(--radius-sm);transition:transform .15s}
.grp-tog:hover{color:var(--color-text);background:color-mix(in srgb,var(--color-text) 8%,transparent)}
.task.grp.open .grp-tog{transform:rotate(90deg)}
.task.grp.open .grp-view{display:none}
.grp-from .muted{opacity:.8}
.task-sub{list-style:none;padding:0 0 0 34px;border-bottom:1px solid var(--color-divider)}
.task-sub.folded{display:none}
.tasks.sub{border-left:2px solid color-mix(in srgb,var(--color-accent) 30%,transparent);padding-left:10px;margin:2px 0 6px}
.tasks.sub .task:last-child{border-bottom:0}
.task-none{font-size:12px;margin:4px 0 8px}
@media (max-width:640px){
  .task{grid-template-columns:minmax(0,1fr) auto}
  .task .tstate{grid-column:1;justify-self:start;margin-left:24px}
  .task.grp{grid-template-columns:28px minmax(0,1fr) auto}
  .task.grp .tstate{grid-column:2;margin-left:0}
  .task-acts{grid-column:2;grid-row:1 / span 2;flex-wrap:wrap;justify-content:flex-end;max-width:108px}
  .task.grp .task-acts{grid-column:3}
  .task-sub{padding-left:14px}
}

/* The files on a task: thumbnails and chips under the meta line, and
   the band on the form they are managed from. */
.task-files{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
.task-file{display:inline-flex;align-items:center;gap:4px;max-width:180px;height:28px;padding:0 8px 0 6px;
  border-radius:var(--radius-sm);background:color-mix(in srgb,var(--color-text) 7%,transparent);
  color:inherit;text-decoration:none;font-size:11.5px}
.task-file span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.task-file .ph{font-size:13px;color:var(--color-muted)}
.task-file.img{padding:0;width:40px;overflow:hidden}
.task-file img{width:40px;height:28px;object-fit:cover;display:block}
.task-file:hover{background:color-mix(in srgb,var(--color-accent) 18%,transparent)}
.view-files{margin-top:10px}
.tf-files{display:flex;flex-wrap:wrap;gap:6px;min-height:34px;align-items:center}
.tf-file{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:3px 3px 3px 6px;border-radius:var(--radius-sm);
  background:color-mix(in srgb,var(--color-text) 7%,transparent);font-size:12px}
.tf-file img{width:34px;height:26px;object-fit:cover;border-radius:3px;display:block}
.tf-file .ph{font-size:14px;color:var(--color-muted)}
.tf-file-n{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tf-file .td-btn{width:24px;height:24px}
.tf-files-none{font-size:12px}
.tf-files-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
.pk-scope{margin-bottom:4px}
.pk-docs{display:flex;flex-direction:column}
.pk-doc{padding:6px 0;border-bottom:1px solid var(--color-divider);gap:8px;font-size:13px}
.pk-doc:last-child{border-bottom:0}
.pk-doc .ph{color:var(--color-muted);font-size:15px}
.pk-doc-n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pk-doc .muted{font-size:11px;white-space:nowrap}
.modal-b > .panel-h + .gallery{margin-bottom:12px}

/* Under the Tasks screen's chips: whose, and which group. */
.task-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:-4px 0 12px}
.task-bar select{max-width:240px}
.dash-sec-arr{margin-left:auto;width:28px;height:28px}

/* ---- before and after ----------------------------------------------- */
.ch-sec{margin:8px 0 0}
.ch-sec + .ch-sec{margin-top:14px}
.ch-h{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted);margin:0 0 6px}
.ch-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.ch-row{display:flex;align-items:center;gap:8px;padding:7px 0;border-bottom:1px solid var(--color-divider)}
.ch-row:last-child{border-bottom:0}
.ch-row > .ph{font-size:15px;color:var(--color-muted);flex:0 0 auto}
.ch-row.complete > .ph{color:var(--color-ok)}
.ch-row.ready > .ph{color:var(--color-ok)}
.ch-row.waiting > .ph,.ch-row.in_progress > .ph{color:var(--color-accent)}
.ch-row.overridden > .ph{color:var(--color-warn)}
.ch-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;text-align:left}
.ch-t{font-size:13px;overflow-wrap:anywhere}
.ch-row.complete .ch-t{color:var(--color-muted);text-decoration:line-through}
.ch-m{font-size:11px;color:var(--color-muted)}
.ch-go{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:0;border:0;background:none;font:inherit;
  color:inherit;cursor:pointer;text-align:left}
.ch-go > .ph{font-size:13px;color:var(--color-muted)}
.ch-go:hover .ch-t{color:var(--color-accent)}
.ch-focus{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-accent) 10%,transparent)}
.ch-focus .ch-t{font-size:14px;font-weight:500}
.ch-nav{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:4px}
.ch-none{font-size:12.5px;color:var(--color-muted);margin:0;padding:4px 0}
.ch-more{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;padding:8px 0 0;
  font-size:12px;color:var(--color-muted)}

/* ---- arrange — the admin -------------------------------------------- */
.modal.ta-modal{width:min(860px,100%)}
.ta-modal .modal-b{max-height:min(74vh,780px)}
.ta-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.ta-bar .muted{font-size:11.5px;margin-left:auto}
.ta-list{display:flex;flex-direction:column;gap:4px;min-height:60px;position:relative}
.ta-row{display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:2px 8px;align-items:center;
  padding:6px 8px 6px 4px;border-radius:var(--radius-md);background:var(--color-bg);position:relative;
  border:1px solid transparent}
.ta-row.grp{background:color-mix(in srgb,var(--color-accent) 8%,var(--color-bg))}
.ta-row.done{opacity:.6}
.ta-handle{width:24px;height:28px;display:grid;place-items:center;color:var(--color-muted);cursor:grab;
  touch-action:none;border-radius:var(--radius-sm);user-select:none;-webkit-user-select:none}
.ta-handle .ph{font-size:16px}
.ta-handle:hover{color:var(--color-text);background:color-mix(in srgb,var(--color-text) 8%,transparent)}
.ta-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.ta-t{display:flex;align-items:center;gap:6px;font-size:13px;border:0;background:none;padding:0;font:inherit;
  color:inherit;cursor:pointer;text-align:left;min-width:0}
.ta-t > span{overflow-wrap:anywhere}
.ta-t:hover > span{text-decoration:underline}
.ta-t .ph{color:var(--color-accent);font-size:13px;flex:0 0 auto}
.ta-m{font-size:11px;color:var(--color-muted);display:flex;flex-wrap:wrap;gap:2px 8px;align-items:center}
.ta-m .task-wait{margin:0}
.ta-acts{display:flex;align-items:center;gap:1px}
.ta-kids{display:flex;flex-direction:column;gap:4px;margin:-2px 0 4px 26px;padding-left:8px;
  border-left:2px solid color-mix(in srgb,var(--color-accent) 30%,transparent)}
.ta-kids.folded{display:none}
.ta-kids:empty{min-height:18px;border-left-style:dashed}
.ta-src{opacity:.35}
.ta-ghost{position:fixed;z-index:1000;pointer-events:none;opacity:.92;box-shadow:var(--shadow-md);
  transform:rotate(-1deg);background:var(--color-surface)}
.ta-over-before::before,.ta-over-after::after{content:"";position:absolute;left:4px;right:4px;height:3px;
  border-radius:2px;background:var(--color-accent);z-index:1}
.ta-over-before::before{top:-4px}
.ta-over-after::after{bottom:-4px}
.ta-over-onto{border-color:var(--color-accent);box-shadow:inset 0 0 0 1px var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 14%,var(--color-bg))}
.ta-empty{font-size:12.5px;color:var(--color-muted);padding:14px 8px;text-align:center;
  border:1px dashed var(--color-divider);border-radius:var(--radius-md)}
@media (max-width:640px){
  .ta-row{grid-template-columns:24px minmax(0,1fr)}
  .ta-acts{grid-column:2;justify-content:flex-start}
  .ta-kids{margin-left:14px}
}

/* The client portal's grouped steps (includes/_job.php). */
.pt.grp{font-weight:500}
.pt.grp .ph{color:var(--color-accent)}
.pt.sub{padding-left:26px}

/* Reading a task rather than editing it. */
.view-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));
  gap:10px;margin-bottom:12px}
.view-facts > div{display:flex;flex-direction:column;gap:1px;font-size:13px}
.view-facts span{font-size:10px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--color-muted)}
.view-note{padding:10px 12px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-text) 5%,transparent);
  font-size:13px;line-height:1.5;text-wrap:pretty;overflow-wrap:anywhere}

/* =====================================================================
   The written field

   A toolbar over a contenteditable, with the HTML itself one button away.
   Shaped like every other input here — same border, same focus ring — so
   it reads as a field and not as an application embedded in one.
   ================================================================== */
.rte{border:1px solid var(--color-divider);border-radius:var(--radius-md);
  background:var(--color-bg);overflow:visible;position:relative}
.rte:focus-within{border-color:var(--color-accent)}
/* padding-right reserves the strip the pinned buttons sit in, so a
   wrapping toolbar never runs underneath them. */
.rte-bar{position:relative;display:flex;align-items:center;gap:2px;flex-wrap:wrap;
  padding:5px 100px 5px 6px;border-bottom:1px solid var(--color-divider)}
.rte-fixed{position:absolute;top:5px;right:6px;display:flex;align-items:center;gap:2px}
.rte-sp{flex:1}
.rte-b{display:inline-flex;align-items:center;gap:5px;min-height:26px;padding:0 7px;
  border:0;border-radius:var(--radius-sm);background:transparent;cursor:pointer;
  color:var(--color-muted);font:inherit;font-size:12px}
.rte-b:hover{background:color-mix(in srgb,var(--color-text) 8%,transparent);
  color:var(--color-text)}
.rte-b .ph{font-size:15px}
.rte.on-source .rte-b[data-cmd="source"]{background:color-mix(in srgb,var(--color-accent) 16%,transparent);
  color:var(--color-accent)}
/* Filling the screen. Fixed rather than a wider modal: a template is a
   page of markup, and editing one through a 200px window is the single
   thing that makes this feel like a toy. */
.rte.big{position:fixed;inset:14px;z-index:200;display:flex;flex-direction:column;
  background:var(--color-surface);box-shadow:var(--shadow-lg)}
.rte.big .rte-in,.rte.big .rte-src{flex:1;max-height:none}
body.rte-open .modal-b{overflow:hidden}
/* The modal keeps a filled transform from its entrance animation, and ANY
   transform other than none makes an element the containing block for a
   position:fixed child — so the expanded editor sized itself to the modal
   instead of to the window. Dropping the animation drops the filled value
   with it; the identity matrix it left behind was doing nothing anyway. */
body.rte-open .modal{animation:none;transform:none}

.rte-div{width:1px;align-self:stretch;margin:2px 4px;background:var(--color-divider)}

.rte-in{min-height:200px;max-height:46vh;overflow-y:auto;padding:10px 12px;
  font-size:13.5px;line-height:1.55;outline:0}
.rte-in:empty:before{content:attr(data-ph);color:var(--color-muted)}
.rte-in h2{font-size:16px;margin:0 0 6px}
.rte-in p{margin:0 0 8px}
.rte-in ul{margin:0 0 8px;padding-left:20px}
.rte-in a{color:var(--color-accent)}
.rte-src{display:block;width:100%;min-height:200px;max-height:46vh;border:0;
  border-radius:0;background:transparent;
  font-family:var(--font-mono);font-size:12.5px;line-height:1.5}
.rte-src:hover,.rte-src:focus{border:0}
textarea.mono{font-family:var(--font-mono);font-size:12.5px}

/* The merge tags, at the cursor. */
/* overflow-x is explicit, and that is the whole fix for the stray
   horizontal scrollbar: a box with overflow-y:auto and overflow-x:visible
   computes the visible axis to auto, so one pixel of overshoot anywhere
   put a scrollbar across the bottom. */
.rte-menu{position:absolute;z-index:130;right:6px;top:38px;
  width:min(340px,calc(100vw - 48px));max-height:min(320px,60vh);
  overflow-y:auto;overflow-x:hidden;padding:6px;
  border-radius:var(--radius-md);background:var(--color-surface);
  box-shadow:var(--shadow-lg)}
.rte-menu button{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:5px 8px;border:0;border-radius:var(--radius-sm);background:transparent;
  color:var(--color-text);font:inherit;cursor:pointer}
.rte-menu button:hover{background:color-mix(in srgb,var(--color-accent) 12%,transparent)}
.rte-menu-t{font-family:var(--font-mono);font-size:12px;color:var(--color-accent)}
.rte-menu-d{font-size:11px;color:var(--color-muted)}

/* The colour menu: presets, then anything. */
/* Wide enough for eight swatches and the row under them without either
   needing to scroll sideways. */
.rte-colours{width:min(300px,calc(100vw - 48px));padding:8px}
.rte-sw{display:grid;grid-template-columns:repeat(8,1fr);gap:4px;margin-bottom:8px}
.rte-swatch{width:100%;aspect-ratio:1;border:0;border-radius:4px;cursor:pointer;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-text) 22%,transparent)}
.rte-swatch:hover{box-shadow:inset 0 0 0 1px var(--color-text),0 0 0 2px var(--color-accent)}
.rte-c-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px}
.rte-c-pick{width:34px;height:30px;padding:2px;flex:0 0 auto;cursor:pointer;
  border:1px solid var(--color-divider);border-radius:var(--radius-sm);
  background:var(--color-bg)}
.rte-c-hex{flex:1 1 110px;min-width:0;min-height:30px;
  font-family:var(--font-mono);font-size:12px}
.rte-c-go{padding:4px 10px;font-size:12.5px;flex:0 0 auto}
.rte-c-none{width:100%;padding:5px;border:0;border-radius:var(--radius-sm);
  background:transparent;color:var(--color-muted);font:inherit;font-size:11.5px;
  cursor:pointer;text-align:left}
.rte-c-none:hover{background:color-mix(in srgb,var(--color-text) 7%,transparent);
  color:var(--color-text)}

/* A template, read rather than edited. Its own wording renders as it will
   arrive, inside a panel that does not let it style the page around it. */
.tpl-read{font-size:13.5px;line-height:1.55;overflow-wrap:anywhere}
.tpl-read h1,.tpl-read h2,.tpl-read h3{font-size:16px;margin:0 0 6px}
.tpl-read p{margin:0 0 8px}
.tpl-read ul,.tpl-read ol{margin:0 0 8px;padding-left:20px}
.tpl-read a{color:var(--color-accent)}
.tpl-read img{max-width:100%;height:auto}
.tpl-read table{max-width:100%}
.tpl-plain{margin:0;font-family:var(--font-mono);font-size:12.5px;line-height:1.5;
  white-space:pre-wrap;overflow-wrap:anywhere}

/* The merge tags this kind of template may use. */
.tagl{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.tagl li{display:flex;flex-direction:column;gap:1px}
.tagl code{font-family:var(--font-mono);font-size:11.5px;color:var(--color-accent)}
.tagl span{font-size:11px;color:var(--color-muted)}

/* The figures above a table: what it adds up to, before the detail. Only
   drawn where a total is a real answer — a count of entries and their
   hours always is; their cost only for somebody who may see money. */
.sum-row{display:flex;flex-wrap:wrap;gap:22px;margin-bottom:12px}
.sum{display:flex;flex-direction:column;gap:1px}
.sum-l{font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--color-muted)}
.sum-v{font-family:var(--font-heading);font-size:19px;font-variant-numeric:tabular-nums}

/* =====================================================================
   Settings

   Cards with air between them, each with its own heading, its own state
   and its own Save. Not a modal per card, and not one Save at the bottom
   of the page — see the note in app.js.
   ================================================================== */
#setBody{display:flex;flex-direction:column;gap:12px}
.screen-note{margin:0 0 12px;font-size:13px;color:var(--color-muted);text-wrap:pretty;
  max-width:78ch}
#setTabs,#tfTabs{margin-bottom:12px}
/* The state marker on a sub-tab, same dot the menu uses. */
.tab .dot{margin-left:2px}

/* One gap, set here, rather than a bottom margin on each thing that
   might be in a card. Cards carry any of a heading, a grid of fields, a
   table, a note and a row of buttons, in different combinations — with
   margins, every combination needed its own rule, and the one nobody had
   written put Save hard against the text above it. */
.set-card{padding:16px;display:flex;flex-direction:column;gap:14px}
.set-head{display:flex;align-items:flex-start;gap:12px}
.set-h-main{flex:1;min-width:0}
.set-t{margin:0 0 3px;font-size:15px}
.set-note{margin:0;font-size:12px;color:var(--color-muted);line-height:1.5;text-wrap:pretty}
/* Folded instructions under a settings card: read once, then out of the way. */
.set-help{margin-top:10px;font-size:12.5px;color:var(--color-muted)}
.set-help summary{cursor:pointer;color:var(--color-accent);font-weight:500}
.set-help ol{margin:8px 0 0;padding-left:20px;line-height:1.55}
.set-help li{margin:4px 0}
/* The note under a card's title: the card's own child now, so it runs the
   card's width and goes away with the body when the card is folded. */
.set-card > .set-note{margin-top:-8px}
.set-pill{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;
  padding:3px 10px;border-radius:999px;font-size:11.5px;
  background:var(--color-accent-900);color:var(--color-accent-200);
  box-shadow:inset 0 0 0 1px var(--color-accent-700)}
.set-pill.warn{background:color-mix(in srgb,var(--color-warn) 16%,transparent);
  color:var(--color-warn);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-warn) 45%,transparent)}
.set-head-btn{flex:0 0 auto;padding:4px 10px;font-size:13px}
.set-acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.set-hint{font-size:11.5px;color:var(--color-muted)}
.set-err{display:flex;align-items:flex-start;gap:7px;margin:0;font-size:12.5px;
  color:var(--color-warn);text-wrap:pretty}
.set-cron{margin:0;padding:10px 12px;border-radius:var(--radius-md);
  background:var(--color-bg)}
/* The command itself, which is long, has to be selectable whole, and must
   never widen the card. */
.set-cron.wrap{white-space:pre-wrap;overflow-wrap:anywhere;user-select:all}

/* The logo, beside what is known about it. */
.logo-row{display:flex;gap:16px;flex-wrap:wrap}
.logo-box.logo-box-sq{width:110px;height:110px}
.logo-box.logo-box-sq img{max-width:96px;max-height:96px;border-radius:4px}
.logo-box{flex:0 0 auto;width:200px;height:110px;display:grid;place-items:center;
  padding:10px;border-radius:var(--radius-md);background:#fff;
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.logo-box img{max-width:100%;max-height:90px;object-fit:contain}
.logo-box .ph{font-size:28px;color:#9aa0b5}
.logo-facts{flex:1 1 260px;min-width:0}

/* ---- uploading ----------------------------------------------------- */
/* Where it goes, above what goes there. */
.up-where{margin-bottom:12px}
.drop{display:flex;flex-direction:column;align-items:center;gap:5px;
  padding:26px 16px;margin-bottom:12px;cursor:pointer;text-align:center;
  border:1px dashed var(--color-divider);border-radius:var(--radius-md);
  background:var(--color-bg);color:var(--color-muted)}
.drop:hover,.drop.over,.drop:focus-visible{border-color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 8%,transparent);
  color:var(--color-text)}
/* It is what the uploader opens on, so it says so before it is touched. */
.drop:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
.drop > .ph{font-size:22px;color:var(--color-accent)}
.drop-t{font-size:13.5px}
.drop-s{font-size:11.5px}
.up-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.up-i{display:flex;align-items:flex-start;gap:9px;padding:8px 0;
  border-bottom:1px solid var(--color-divider)}
.up-i:last-child{border-bottom:0}
.up-i > .ph{font-size:15px;margin-top:2px;color:var(--color-muted)}
.up-i.done > .ph{color:var(--color-ok)}
.up-i.bad > .ph{color:var(--color-warn)}
.up-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.up-n{font-size:13px;overflow-wrap:anywhere}
.up-s{font-size:11px;color:var(--color-muted)}
.up-i.bad .up-s{color:var(--color-warn)}
/* Only while something is actually moving: a full-width empty bar under
   every queued file reads as eight things stuck at zero. */
.up-bar{display:none;height:4px;border-radius:2px;overflow:hidden;
  background:color-mix(in srgb,var(--color-text) 12%,transparent)}
.up-i.up .up-bar,.up-i.done .up-bar{display:block}
.up-bar-in{display:block;height:100%;background:var(--color-accent);
  transition:width .15s linear}
.up-i.done .up-bar-in{background:var(--color-ok)}
.up-share{margin-top:12px}

/* Settings, read before they are changed. A label and its value on one
   line, wrapping to two when there is no room — a definition list in
   everything but the tag, which would bring its own margins. */
.set-rows{display:flex;flex-direction:column}
.set-row{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:6px 0;
  border-bottom:1px solid var(--color-divider)}
.set-row:last-child{border-bottom:0}
.set-l{flex:0 0 auto;min-width:150px;font-size:11.5px;color:var(--color-muted)}
.set-v{flex:1;min-width:0;font-size:13px;overflow-wrap:anywhere;text-wrap:pretty}
/* Something that is not set up, or is set up wrongly. Not an error — most
   of these are simply not done yet — so it is the warn colour rather than
   anything louder. */
.set-v.warn{color:var(--color-warn)}
.field-note .warn,.panel-p .warn{color:var(--color-warn)}

/* A row of tags that is a set rather than a sentence — what somebody is
   signed off for, which filters are on. */
.chipset{display:flex;flex-wrap:wrap;gap:6px}

/* A heading with one administrative act in it — changing a role, which
   belongs beside what the role IS rather than in the bar above with the
   things done every day. */
.panel-h-btn{margin-left:auto;padding:3px 9px;font-size:12.5px}

/* Read-only content above a form's fields. */
.modal-body-extra{margin-bottom:2px}

/* What one task is waiting for. */
.deps{list-style:none;margin:0 0 14px;padding:0;display:flex;flex-direction:column}
.dep{display:flex;align-items:flex-start;gap:8px;padding:7px 0;
  border-bottom:1px solid var(--color-divider)}
.dep:last-child{border-bottom:0}
.dep > .ph{font-size:15px;margin-top:1px;color:var(--color-accent)}
.dep.done > .ph{color:var(--color-ok)}
.dep-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.dep-t{font-size:13px;overflow-wrap:anywhere}
.dep.done .dep-t{color:var(--color-muted);text-decoration:line-through}
.dep-m{font-size:11px;color:var(--color-muted)}

/* Who is told about the job. A list of people, not a table: there are
   three facts about each and two of them are optional. */
.watchers{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.watcher{display:flex;align-items:center;gap:8px;padding:6px 0;
  border-bottom:1px solid var(--color-divider)}
.watcher:last-child{border-bottom:0}
.watcher-id{flex:1;min-width:0;display:flex;flex-direction:column}
.watcher-n{font-size:13px;overflow-wrap:anywhere}
.watcher-r{font-size:11px;color:var(--color-muted)}
.watcher .tag{font-size:9.5px;padding:1px 7px;line-height:14px;height:16px;
  box-sizing:border-box;flex:0 0 auto}

/* — the address suggestions —
   Fixed to the window rather than to the field: inside the form it was
   clipped by the scrolling body and vanished behind the footer. Above the
   modal in the stack, because it belongs to a field inside one. */
.lookup-list{position:fixed;z-index:130;max-height:min(260px,42vh);overflow-y:auto;
  padding:4px;border-radius:var(--radius-md);
  background:var(--color-surface);box-shadow:var(--shadow-lg)}
.lookup-i{display:block;width:100%;text-align:left;padding:6px 9px;border:0;
  border-radius:var(--radius-sm);background:transparent;color:var(--color-text);
  font:inherit;font-size:13px;cursor:pointer}
.lookup-i:hover,.lookup-i.on{color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 14%,transparent)}

/* — tags — */
.tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;letter-spacing:.02em;
  padding:3px 9px;border-radius:6px;white-space:nowrap;
  background:var(--color-neutral-800);color:var(--color-neutral-100)}
.tag-accent{background:var(--color-accent-800);color:var(--color-accent-100)}
/* The plain one. Same as a bare .tag, and named so markup can say which
   of the three it means rather than leaving it to be inferred. */
.tag-neutral{background:var(--color-neutral-800);color:var(--color-neutral-100)}
.tag-outline{background:transparent;border:1px solid var(--color-accent);color:var(--color-accent)}
/* Not an error — something not finished. Same colour the status dots use. */
.tag-warn{background:color-mix(in srgb,var(--color-warn) 18%,transparent);
  color:var(--color-warn)}
.tag-dot::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--dot,currentColor)}
/* Yes and no, in the theme's own good and bad: a tick that is on, and one
   that is off — said as a state either way rather than a missing line. */
.tag-good{background:color-mix(in srgb,var(--color-ok) 18%,transparent);color:var(--color-ok)}
.tag-bad{background:color-mix(in srgb,var(--color-danger) 16%,transparent);color:var(--color-danger)}
.tag-good .ph,.tag-bad .ph{font-size:12px;font-weight:700}
/* A row that is a bubble rather than a line of text needs air around it. */
.feed-w .tag{margin:3px 0}

/* — cards — */
.card{background:var(--color-surface);border-radius:var(--radius-lg);box-shadow:var(--shadow-sm);
  min-width:0;overflow:hidden}
.card-h{display:flex;align-items:center;gap:9px;padding:13px 16px 0}
.card-h .ph{font-size:15px;color:var(--color-accent)}
.card-t{font-family:var(--font-heading);font-weight:var(--font-heading-weight);
  font-size:15px;letter-spacing:-.01em;flex:1;min-width:0}
.card-b{padding:10px 16px 16px;min-width:0}

/* — the fading rule, a Nocturne signature — */
.hr{height:1px;border:0;margin:var(--space-4) 0;
  background:linear-gradient(to right,transparent,var(--color-divider) 48px,
    var(--color-divider) calc(100% - 48px),transparent)}

/* =====================================================================
   Shell — rail, topbar, view.

   The page itself never scrolls; the view does. That is what keeps the
   topbar and the rail in place on a phone without position:fixed and the
   scroll bugs that come with it.
   ================================================================== */
body{display:grid;grid-template-columns:var(--rail-w) minmax(0,1fr);height:100dvh}

.rail{grid-column:1;display:flex;flex-direction:column;min-height:0;
  background:var(--color-surface);border-right:1px solid var(--color-divider)}
.rail-nav{flex:1;min-height:0;overflow-y:auto;padding:4px 8px 12px;display:flex;
  flex-direction:column;gap:1px;scrollbar-width:none}
.rail-nav::-webkit-scrollbar{display:none}
/* The design's nav button, value for value. Two things I had wrong: an
   inactive item is FULL-strength text, not dimmed — dimming every item
   makes the whole menu look disabled — and the active state is a ring
   round the item rather than a bar down its left edge. */
.rail-a{display:flex;align-items:center;gap:10px;width:100%;padding:7px 10px;
  justify-content:flex-start;background:transparent;color:var(--color-text);
  border:0;border-radius:var(--radius-md);cursor:pointer;text-align:left;
  font:inherit;font-size:13.5px;font-family:var(--font-heading);box-shadow:none;
  /* One line height for the row, shared by the label and by the icon, so
     an item is the same height whether or not it is showing its label —
     and that height is the design's. */
  line-height:21px}
.rail-a:hover{background:color-mix(in srgb,var(--color-text) 7%,transparent)}
.rail-a.on{background:color-mix(in srgb,var(--color-accent) 14%,transparent);
  color:var(--color-accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 40%,transparent)}
/* line-height:inherit, so the icon occupies the same line box as the
   label it stands in for. Without it an item loses 4px of height the
   moment the labels go, and the whole column tightens up as it narrows. */
.rail-a .ph{font-size:17px;line-height:inherit}
.rail-l{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* The break before the super-admin group. A heading, not a rule — the
   design's — with the space above it doing the separating. Drawn only for
   somebody who has those sections at all; see navVisible(). */
.rail-head{padding:14px 10px 4px;font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:color-mix(in srgb,var(--color-text) 42%,transparent);white-space:nowrap}

.dot{width:6px;height:6px;border-radius:50%;flex:0 0 auto;background:var(--color-muted)}
.dot.ok{background:var(--color-ok)}
.dot.warn{background:var(--color-warn)}

.rail-foot{padding:8px;border-top:1px solid var(--color-divider);display:flex;
  flex-direction:column;gap:1px}
.rail-narrow{color:color-mix(in srgb,var(--color-text) 60%,transparent)}
html[data-nav="collapsed"] .rail-narrow .ph{transform:rotate(180deg)}

/* Collapsed: an icon rail. Labels are gone, not hidden with opacity, so
   they cannot be read by a screen reader as though they were visible.

   The icons keep the padding they had, rather than centring in the narrow
   strip — so narrowing the menu does not slide every icon sideways under
   the cursor. Only the labels leave. */
html[data-nav="collapsed"] .rail-l{display:none}
/* The "Super admin" heading has no room for its words when the rail is a
   strip of icons — so it becomes the rule they were there to draw. It is
   still only rendered for somebody who has that group at all, so a staff
   user gets no stray line across their menu. */
html[data-nav="collapsed"] .rail-head{
  height:1px;margin:9px 10px;padding:0;font-size:0;letter-spacing:0;
  background:var(--color-divider);overflow:hidden}
/* The item shrinks to its icon rather than stretching across the strip.
   Stretched, the icon sat 10px from the left of a 43px box and 16px from
   its right — the highlight read as lopsided, and as different padding
   from the open menu even though the icon was in the same place. */
html[data-nav="collapsed"] .rail-a{justify-content:flex-start;padding:7px 10px;gap:0;
  width:auto;align-self:flex-start}

.main{grid-column:2;display:flex;flex-direction:column;min-width:0;min-height:0}
/* z-index so the messages and alerts pops, which hang off the top bar, stay
   above the view whatever a theme pack does to either: a pack that gives the
   bar a backdrop-filter or isolation makes it a stacking context of its own,
   and one that lifts the view with a z-index would then paint over the pops. */
.topbar{display:flex;align-items:center;gap:8px;padding:10px 16px;min-height:var(--topbar-h);
  border-bottom:1px solid var(--color-divider);flex:0 0 auto;position:relative;z-index:30}
.topbar-t{font-size:17px;margin:0;flex:0 0 auto}
.topbar-search{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:420px;
  margin-left:8px;padding:0 10px;background:var(--color-surface);
  border:1px solid var(--color-divider);border-radius:var(--radius-md)}
.topbar-search .ph{color:var(--color-muted);font-size:15px}
.topbar-search input{border:0;background:transparent;min-height:32px;padding:0}
.topbar-search input:hover,.topbar-search input:focus{border:0}
.topbar-acts{display:flex;align-items:center;gap:7px;margin-left:auto;flex:0 0 auto}
.subbar{display:flex;gap:2px;padding:0 12px;overflow-x:auto;flex:0 0 auto;
  border-bottom:1px solid var(--color-divider)}
.view{flex:1;min-height:0;overflow-y:auto;padding:18px 16px 40px}

.scrim{position:fixed;inset:0;z-index:40;background:color-mix(in srgb,var(--color-neutral-900) 55%,transparent)}

/* — phone: the rail slides over rather than taking a column — */
@media (max-width:820px){
  body{grid-template-columns:minmax(0,1fr)}
  .only-mob{display:inline-flex!important}
  /* Fixed and on top whatever a theme pack does to the rail on a desk: a
     pack that lifts the rail and the view with z-indexes of its own would
     otherwise leave the drawer opening behind the page on a phone. */
  .rail{position:fixed!important;inset:0 auto 0 0;width:272px;z-index:50!important;transform:translateX(-100%);
    transition:transform .18s ease;box-shadow:var(--shadow-lg)}
  html[data-mobnav="open"] .rail{transform:none}
  .rail-collapse{display:none}
  /* An icon-only rail makes no sense in an overlay: there is room for
     the words, and the collapse control is not reachable anyway. */
  html[data-nav="collapsed"] .rail-l,
  html[data-nav="collapsed"] .rail-head{display:revert}
  html[data-nav="collapsed"] .rail-a{justify-content:flex-start;padding:7px 10px;gap:10px}
  .rail-narrow{display:none}
  .main{grid-column:1}
  .topbar{padding:8px 12px}
  .topbar-t{font-size:16px}
  .view{padding:14px 12px 40px}
}
@media (min-width:821px){ .scrim{display:none} }

/* =====================================================================
   Screens — the split, which every record screen uses.

   One pattern for leads, clients, jobs, staff and the rest, rather than
   a bespoke layout each. The breakpoint is on the CONTENT, not the
   window, so collapsing the rail widens the split rather than leaving it
   stuck at a width chosen for a narrower page.
   ================================================================== */
.view{display:flex;flex-direction:column}
.split{display:grid;grid-template-columns:minmax(260px,330px) minmax(0,1fr);gap:14px;
  align-items:stretch;flex:1;min-height:0}
.split.one{grid-template-columns:minmax(0,1fr)}
.split.one .split-detail{display:none}
.split.one.showing .split-list{display:none}
.split.one.showing .split-detail{display:flex}

.split-list{display:flex;flex-direction:column;min-height:0;max-height:100%;
  background:var(--color-surface);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm);overflow:hidden}
.split-detail{display:flex;flex-direction:column;min-width:0;min-height:0;max-height:100%;
  background:var(--color-surface);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm);overflow:hidden}

.list-head{display:flex;align-items:center;gap:8px;padding:9px 12px;flex:0 0 auto;
  border-bottom:1px solid var(--color-divider)}
.list-count{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.count-all{color:var(--color-accent)}
.list-rows{flex:1;min-height:0;overflow-y:auto}

.row{display:flex;align-items:flex-start;gap:10px;width:100%;text-align:left;padding:9px 12px;
  cursor:pointer;background:transparent;border:0;color:var(--color-text);font:inherit;
  border-bottom:1px solid var(--color-divider);box-shadow:inset 2px 0 0 transparent}
.row:last-child{border-bottom:0}
.row:hover{background:color-mix(in srgb,var(--color-text) 5%,transparent)}
.row.on{background:color-mix(in srgb,var(--color-accent) 10%,transparent);
  box-shadow:inset 2px 0 0 var(--color-accent)}
.row-main{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.row-t{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:500;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row-t .ph{font-size:13px;color:var(--color-muted)}
.row-s{font-size:12px;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row-side{display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex:0 0 auto}
.row-v{font-family:var(--font-mono);font-size:12.5px;font-variant-numeric:tabular-nums;
  color:var(--color-muted)}

html[data-density="comfortable"] .row{padding-top:12px;padding-bottom:12px}

/* — chips: filters as one control — */
.chips{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:12px}
.chip{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;cursor:pointer;
  background:var(--color-surface);color:var(--color-text);border:0;border-radius:999px;
  font:inherit;font-size:12.5px;box-shadow:inset 0 0 0 1px var(--color-divider)}
.chip:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}
.chip.on{background:color-mix(in srgb,var(--color-accent) 14%,transparent);
  color:var(--color-accent);box-shadow:inset 0 0 0 1px var(--color-accent)}
.chip-n{font-size:11px;opacity:.7;font-variant-numeric:tabular-nums}
.chip-dot{width:7px;height:7px;border-radius:50%}
/* The sheet of filters a phone picks from. */
.chip-sheet{display:flex;flex-wrap:wrap;gap:7px;margin:8px 0 12px}
.chip-more .ph{font-size:13px}
/* Folding on a small screen: the caret, the "all" bar, and what a folded
   card hides. Nothing here applies on a wide screen — the carets are
   only-mob, and the folded class is only ever set there. */
.fold-t{flex:0 0 auto}
.fold-facts{margin-left:auto;align-items:center;gap:4px;padding:3px 9px;border-radius:999px;font:inherit;font-size:12px;
  cursor:pointer;color:var(--color-accent);background:color-mix(in srgb,var(--color-accent) 12%,transparent);
  border:0;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 40%,transparent)}
.fold-facts .ph{font-size:13px}
.set-head .fold-t{margin-left:auto}
.panel-h .fold-t{margin-left:auto}
.fold-all{display:none;gap:8px;margin-bottom:12px}
.fold-all .btn-secondary{padding:4px 10px;font-size:12.5px}
@media (max-width:820px){
  .chips .chip:not(.on):not(.chip-more){display:none}
  .fold-all{display:flex}
  .set-card.folded > :not(.set-head){display:none}
  .set-card.folded{gap:0}
  .panel.folded:not(.set-card) > :not(.panel-h){display:none}
  .panel.folded:not(.set-card) > .panel-h{margin-bottom:0}
  .rec-card.folded > .rec-facts{display:none}
  /* The name row on a phone: the name, then the Details chip at the far
     right, and every tag on a line of its own underneath — so the chip
     is always in the same corner whatever the record is tagged. */
  .rec-h-row .rec-h{order:0;flex:1 1 auto;min-width:0}
  .rec-h-row .fold-facts{order:1}
  .rec-h-row::after{content:"";order:2;flex-basis:100%;height:0}
  /* The line break is a row of its own, so the row gap was paid twice
     above the tags and not at all below: no row gap, and the tags carry
     their own — a little above, the same below. */
  .rec-id .rec-h-row{row-gap:0}           /* outranks the base rule, which comes later in the file */
  .rec-h-row .tag{order:3;margin:4px 0 8px}
}

/* — the record: the tab bar. The rest is further down. — */
.tabs{display:flex;gap:2px;padding:0 12px;overflow-x:auto;flex:0 0 auto;
  border-bottom:1px solid var(--color-divider)}
.tab{display:inline-flex;align-items:center;gap:7px;padding:9px 12px;cursor:pointer;
  background:transparent;border:0;border-bottom:2px solid transparent;color:var(--color-muted);
  font:inherit;font-size:13.5px;white-space:nowrap}
.tab:hover{color:var(--color-text)}
.tab.on{color:var(--color-accent);border-bottom-color:var(--color-accent)}
.tab .ph{font-size:15px}

/* — toast — */
/* The design's, value for value: a 13% tint of the level colour over the
   surface, a 3px bar of it down the left edge, and a 42% ring. They stack
   downwards from the top, and the stack itself ignores the mouse so it
   cannot swallow a click meant for the page underneath. */
.toast{position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:120;
  display:flex;flex-direction:column;align-items:center;gap:8px;pointer-events:none}
.toast-item{--c:var(--color-ok);display:flex;align-items:flex-start;gap:10px;
  width:min(380px,calc(100vw - 32px));padding:10px 12px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--c) 13%,var(--color-surface));
  box-shadow:inset 3px 0 0 var(--c),
             inset 0 0 0 1px color-mix(in srgb,var(--c) 42%,transparent),
             var(--shadow-md);
  animation:ae-drop .2s ease both;pointer-events:auto}
.toast-item.info{--c:var(--color-info)}
.toast-item.warn{--c:var(--color-warn)}
.toast-item.error{--c:var(--color-danger)}
.toast-item>.ph{font-size:16px;flex:0 0 auto;margin-top:1px;color:var(--c)}
.toast-msg{flex:1;min-width:0;font-size:13px;text-wrap:pretty}
.toast-item.toast-stay{align-items:center;flex-wrap:wrap}
.toast-act{padding:5px 11px;font-size:12.5px}
.toast-x{width:24px;height:24px;flex:0 0 auto;border-color:transparent}
.toast-x .ph{font-size:12px}
@keyframes ae-drop{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.toast-item{animation:none}}
/* =====================================================================
   The themes.

   Twenty of them, from the design. Written as [data-theme] blocks rather
   than applied by script, because a theme applied after the page loads is
   a flash of the wrong one on every single load. The server stamps the
   attribute on <html> from the user's saved setting, so the right theme
   is painted first time and there is nothing to wait for.

   Only the tokens a theme actually changes are listed; everything else
   falls through to :root. A LIGHT theme has to invert the neutral ramp as
   a pair — the system pairs step 100 (text) with step 800 (fill), so
   flipping one end alone leaves light text on a light tint — and supplies
   an opaque --color-text-muted, because the 55% alpha mix is tuned for a
   dark ground and drops to 3.45:1 when inverted.
   ================================================================== */
/* Nocturne is :root, but the theme tiles paint each row in its OWN theme
   through data-theme — so the default needs a block of its own, or its tile
   borrows whatever theme is switched on and looks like a copy of it. */
[data-theme="nocturne"]{--color-bg:#161826;--color-surface:#232532;--color-text:#e9e9ed;--color-accent:#9184d9;--color-accent-2:#a7a1db;--color-divider:color-mix(in srgb,#e9e9ed 16%,transparent)}
[data-theme="graphite"]{--color-bg:#17181c;--color-surface:#232529;--color-accent:#b2b6ca;--color-accent-100:#f3f5fe;--color-accent-300:#cfd3e5;--color-accent-700:#595d6c;--color-accent-800:#3f424d;--color-accent-900:#292b31;--color-text:#e9e9ed;--color-divider:color-mix(in srgb,#e9e9ed 16%,transparent)}
[data-theme="amethyst"]{--color-bg:#14152a;--color-surface:#212239;--color-accent:#a79ae8;--color-text:#e9e9ed;--color-divider:color-mix(in srgb,#e9e9ed 16%,transparent)}
[data-theme="harbour"]{--color-bg:#121a20;--color-surface:#1e272d;--color-accent:#6fb8bf;--color-accent-100:#f0fbfc;--color-accent-300:#bfe3e6;--color-accent-700:#3e6b70;--color-accent-800:#2b4a4e;--color-accent-900:#1d3134;--color-text:#e9e9ed;--color-divider:color-mix(in srgb,#e9e9ed 16%,transparent)}
[data-theme="ember"]{--color-bg:#1a1714;--color-surface:#272220;--color-accent:#d9a273;--color-accent-100:#fdf5ec;--color-accent-300:#f0d4b8;--color-accent-700:#8a613e;--color-accent-800:#5e422b;--color-accent-900:#3b2c1e;--color-text:#e9e9ed;--color-divider:color-mix(in srgb,#e9e9ed 16%,transparent)}
[data-theme="daylight"]{--color-bg:#f3f5fe;--color-surface:#ffffff;--color-text:#292b31;--color-divider:color-mix(in srgb, #292b31 14%, transparent);--color-text-muted:color-mix(in srgb, #292b31 78%, #ffffff);--color-accent:#5d5294;--color-accent-2:#5d5294;--color-accent-100:#2b2741;--color-accent-200:#423a6a;--color-accent-300:#2b2741;--color-accent-400:#796cbf;--color-accent-500:#5d5294;--color-accent-600:#b5abfc;--color-accent-700:#d2cefd;--color-accent-800:#e7e5fe;--color-accent-900:#f5f4ff;--color-accent-2-100:#2b2741;--color-accent-2-200:#423a6a;--color-accent-2-300:#2b2741;--color-accent-2-400:#796cbf;--color-accent-2-500:#5d5294;--color-accent-2-600:#b5abfc;--color-accent-2-700:#d2cefd;--color-accent-2-800:#e7e5fe;--color-accent-2-900:#f5f4ff;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="paper"]{--color-bg:#f8f6f1;--color-surface:#ffffff;--color-text:#2f2a22;--color-divider:color-mix(in srgb, #2f2a22 14%, transparent);--color-text-muted:color-mix(in srgb, #2f2a22 78%, #ffffff);--color-accent:#7a5a2e;--color-accent-2:#7a5a2e;--color-accent-100:#33240f;--color-accent-200:#4f3a1c;--color-accent-300:#33240f;--color-accent-400:#9b7a45;--color-accent-500:#7a5a2e;--color-accent-600:#c9ad7e;--color-accent-700:#e6d6b8;--color-accent-800:#f2e8d6;--color-accent-900:#faf4e9;--color-accent-2-100:#33240f;--color-accent-2-200:#4f3a1c;--color-accent-2-300:#33240f;--color-accent-2-400:#9b7a45;--color-accent-2-500:#7a5a2e;--color-accent-2-600:#c9ad7e;--color-accent-2-700:#e6d6b8;--color-accent-2-800:#f2e8d6;--color-accent-2-900:#faf4e9;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="mint"]{--color-bg:#f1f8f4;--color-surface:#ffffff;--color-text:#292b31;--color-divider:color-mix(in srgb, #292b31 14%, transparent);--color-text-muted:color-mix(in srgb, #292b31 78%, #ffffff);--color-accent:#136b4a;--color-accent-2:#136b4a;--color-accent-100:#08301f;--color-accent-200:#0d4a33;--color-accent-300:#08301f;--color-accent-400:#2f8f68;--color-accent-500:#136b4a;--color-accent-600:#79c4a5;--color-accent-700:#b6e0cd;--color-accent-800:#d8efe4;--color-accent-900:#edf8f3;--color-accent-2-100:#08301f;--color-accent-2-200:#0d4a33;--color-accent-2-300:#08301f;--color-accent-2-400:#2f8f68;--color-accent-2-500:#136b4a;--color-accent-2-600:#79c4a5;--color-accent-2-700:#b6e0cd;--color-accent-2-800:#d8efe4;--color-accent-2-900:#edf8f3;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="harbour-light"]{--color-bg:#eef2f7;--color-surface:#ffffff;--color-text:#292b31;--color-divider:color-mix(in srgb, #292b31 14%, transparent);--color-text-muted:color-mix(in srgb, #292b31 78%, #ffffff);--color-accent:#2f5b86;--color-accent-2:#2f5b86;--color-accent-100:#122436;--color-accent-200:#1d3c58;--color-accent-300:#122436;--color-accent-400:#4a7cab;--color-accent-500:#2f5b86;--color-accent-600:#93b4d2;--color-accent-700:#c2d6e8;--color-accent-800:#dfeaf4;--color-accent-900:#f0f5fa;--color-accent-2-100:#122436;--color-accent-2-200:#1d3c58;--color-accent-2-300:#122436;--color-accent-2-400:#4a7cab;--color-accent-2-500:#2f5b86;--color-accent-2-600:#93b4d2;--color-accent-2-700:#c2d6e8;--color-accent-2-800:#dfeaf4;--color-accent-2-900:#f0f5fa;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="ink"]{--color-bg:#f4f7ff;--color-surface:#ffffff;--color-text:#1b2340;--color-divider:color-mix(in srgb, #1b2340 14%, transparent);--color-text-muted:color-mix(in srgb, #1b2340 78%, #ffffff);--color-accent:#1544c9;--color-accent-2:#1544c9;--color-accent-100:#0a1a4d;--color-accent-200:#102a7a;--color-accent-300:#0a1a4d;--color-accent-400:#3a63e0;--color-accent-500:#1544c9;--color-accent-600:#8ba4f0;--color-accent-700:#c2d0f8;--color-accent-800:#e0e8fd;--color-accent-900:#f1f5ff;--color-accent-2-100:#0a1a4d;--color-accent-2-200:#102a7a;--color-accent-2-300:#0a1a4d;--color-accent-2-400:#3a63e0;--color-accent-2-500:#1544c9;--color-accent-2-600:#8ba4f0;--color-accent-2-700:#c2d0f8;--color-accent-2-800:#e0e8fd;--color-accent-2-900:#f1f5ff;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="citrus"]{--color-bg:#fff8f0;--color-surface:#ffffff;--color-text:#2e2318;--color-divider:color-mix(in srgb, #2e2318 14%, transparent);--color-text-muted:color-mix(in srgb, #2e2318 78%, #ffffff);--color-accent:#c25708;--color-accent-2:#c25708;--color-accent-100:#3d1a02;--color-accent-200:#5c2804;--color-accent-300:#3d1a02;--color-accent-400:#e07c1f;--color-accent-500:#c25708;--color-accent-600:#f5b877;--color-accent-700:#fbd9b3;--color-accent-800:#fdeedc;--color-accent-900:#fff8f0;--color-accent-2-100:#3d1a02;--color-accent-2-200:#5c2804;--color-accent-2-300:#3d1a02;--color-accent-2-400:#e07c1f;--color-accent-2-500:#c25708;--color-accent-2-600:#f5b877;--color-accent-2-700:#fbd9b3;--color-accent-2-800:#fdeedc;--color-accent-2-900:#fff8f0;--color-neutral-100:#292b31;--color-neutral-200:#3f424d;--color-neutral-300:#595d6c;--color-neutral-400:#75798c;--color-neutral-600:#b2b6ca;--color-neutral-700:#cfd3e5;--color-neutral-800:#e4e7f5;--color-neutral-900:#e9ebf8;--shadow-sm:0 1px 2px rgba(41,43,49,0.10), 0 0 0 1px rgba(41,43,49,0.07);--shadow-md:0 4px 12px rgba(41,43,49,0.12), 0 0 0 1px rgba(41,43,49,0.08);--shadow-lg:0 12px 32px rgba(41,43,49,0.18), 0 0 0 1px rgba(41,43,49,0.10)}
[data-theme="cobalt"]{--color-bg:#0c111b;--color-surface:#151d2c;--color-text:#eef2f8;--color-divider:color-mix(in srgb, #eef2f8 17%, transparent);--color-accent:#4da3ff;--color-accent-2:#4da3ff;--color-accent-100:#e8f2ff;--color-accent-200:#c4dfff;--color-accent-300:#e8f2ff;--color-accent-400:#7cbcff;--color-accent-500:#4da3ff;--color-accent-600:#2f7fd6;--color-accent-700:#215c9c;--color-accent-800:#173f6b;--color-accent-900:#102a47;--color-accent-2-100:#e8f2ff;--color-accent-2-200:#c4dfff;--color-accent-2-300:#e8f2ff;--color-accent-2-400:#7cbcff;--color-accent-2-500:#4da3ff;--color-accent-2-600:#2f7fd6;--color-accent-2-700:#215c9c;--color-accent-2-800:#173f6b;--color-accent-2-900:#102a47}
[data-theme="neon"]{--color-bg:#0b0f0d;--color-surface:#151b18;--color-text:#e9f2ee;--color-divider:color-mix(in srgb, #e9f2ee 17%, transparent);--color-accent:#3fd99b;--color-accent-2:#3fd99b;--color-accent-100:#e6fff5;--color-accent-200:#b6f5da;--color-accent-300:#e6fff5;--color-accent-400:#79e6bc;--color-accent-500:#3fd99b;--color-accent-600:#22a874;--color-accent-700:#187a55;--color-accent-800:#11543b;--color-accent-900:#0d3628;--color-accent-2-100:#e6fff5;--color-accent-2-200:#b6f5da;--color-accent-2-300:#e6fff5;--color-accent-2-400:#79e6bc;--color-accent-2-500:#3fd99b;--color-accent-2-600:#22a874;--color-accent-2-700:#187a55;--color-accent-2-800:#11543b;--color-accent-2-900:#0d3628}
[data-theme="signal"]{--color-bg:#14100f;--color-surface:#1f1a19;--color-text:#f4ece9;--color-divider:color-mix(in srgb, #f4ece9 17%, transparent);--color-accent:#ff8a63;--color-accent-2:#ff8a63;--color-accent-100:#fff0ea;--color-accent-200:#ffd6c6;--color-accent-300:#fff0ea;--color-accent-400:#ffab8c;--color-accent-500:#ff8a63;--color-accent-600:#d76840;--color-accent-700:#a04b2c;--color-accent-800:#70341e;--color-accent-900:#4a2315;--color-accent-2-100:#fff0ea;--color-accent-2-200:#ffd6c6;--color-accent-2-300:#fff0ea;--color-accent-2-400:#ffab8c;--color-accent-2-500:#ff8a63;--color-accent-2-600:#d76840;--color-accent-2-700:#a04b2c;--color-accent-2-800:#70341e;--color-accent-2-900:#4a2315}
[data-theme="violet"]{--color-bg:#100d1c;--color-surface:#1b172c;--color-text:#efeaf7;--color-divider:color-mix(in srgb, #efeaf7 17%, transparent);--color-accent:#b98cff;--color-accent-2:#b98cff;--color-accent-100:#f4ecff;--color-accent-200:#e0ccff;--color-accent-300:#f4ecff;--color-accent-400:#c9a6ff;--color-accent-500:#b98cff;--color-accent-600:#8f5fd6;--color-accent-700:#6b429f;--color-accent-800:#4a2d6e;--color-accent-900:#2f1c48;--color-accent-2-100:#f4ecff;--color-accent-2-200:#e0ccff;--color-accent-2-300:#f4ecff;--color-accent-2-400:#c9a6ff;--color-accent-2-500:#b98cff;--color-accent-2-600:#8f5fd6;--color-accent-2-700:#6b429f;--color-accent-2-800:#4a2d6e;--color-accent-2-900:#2f1c48}
[data-theme="amber"]{--color-bg:#111114;--color-surface:#1c1c20;--color-text:#f1f0ea;--color-divider:color-mix(in srgb, #f1f0ea 17%, transparent);--color-accent:#ffc93c;--color-accent-2:#ffc93c;--color-accent-100:#fff8e2;--color-accent-200:#ffeeb8;--color-accent-300:#fff8e2;--color-accent-400:#ffdc78;--color-accent-500:#ffc93c;--color-accent-600:#d6a017;--color-accent-700:#a17810;--color-accent-800:#6e520a;--color-accent-900:#473406;--color-accent-2-100:#fff8e2;--color-accent-2-200:#ffeeb8;--color-accent-2-300:#fff8e2;--color-accent-2-400:#ffdc78;--color-accent-2-500:#ffc93c;--color-accent-2-600:#d6a017;--color-accent-2-700:#a17810;--color-accent-2-800:#6e520a;--color-accent-2-900:#473406}
[data-theme="electric"]{--color-bg:#08090c;--color-surface:#13161a;--color-text:#eaf2e6;--color-divider:color-mix(in srgb, #eaf2e6 17%, transparent);--color-accent:#8bf24f;--color-accent-2:#8bf24f;--color-accent-100:#f3ffe9;--color-accent-200:#dcffc4;--color-accent-300:#f3ffe9;--color-accent-400:#b7f78e;--color-accent-500:#8bf24f;--color-accent-600:#5cc229;--color-accent-700:#448f1e;--color-accent-800:#2d5f14;--color-accent-900:#1d3f0d;--color-accent-2-100:#f3ffe9;--color-accent-2-200:#dcffc4;--color-accent-2-300:#f3ffe9;--color-accent-2-400:#b7f78e;--color-accent-2-500:#8bf24f;--color-accent-2-600:#5cc229;--color-accent-2-700:#448f1e;--color-accent-2-800:#2d5f14;--color-accent-2-900:#1d3f0d}
[data-theme="magenta"]{--color-bg:#150a12;--color-surface:#221421;--color-text:#f6eaf1;--color-divider:color-mix(in srgb, #f6eaf1 17%, transparent);--color-accent:#ff5db1;--color-accent-2:#ff5db1;--color-accent-100:#fff0f8;--color-accent-200:#ffd0e8;--color-accent-300:#fff0f8;--color-accent-400:#ff9ccd;--color-accent-500:#ff5db1;--color-accent-600:#d63f8e;--color-accent-700:#a02c69;--color-accent-800:#6e1c47;--color-accent-900:#48122e;--color-accent-2-100:#fff0f8;--color-accent-2-200:#ffd0e8;--color-accent-2-300:#fff0f8;--color-accent-2-400:#ff9ccd;--color-accent-2-500:#ff5db1;--color-accent-2-600:#d63f8e;--color-accent-2-700:#a02c69;--color-accent-2-800:#6e1c47;--color-accent-2-900:#48122e}
[data-theme="ocean"]{--color-bg:#06171b;--color-surface:#0f2429;--color-text:#e6f3f2;--color-divider:color-mix(in srgb, #e6f3f2 17%, transparent);--color-accent:#22e0d0;--color-accent-2:#22e0d0;--color-accent-100:#e6fffd;--color-accent-200:#bafaf4;--color-accent-300:#e6fffd;--color-accent-400:#7deee4;--color-accent-500:#22e0d0;--color-accent-600:#12b5a7;--color-accent-700:#0b877c;--color-accent-800:#075a53;--color-accent-900:#053b36;--color-accent-2-100:#e6fffd;--color-accent-2-200:#bafaf4;--color-accent-2-300:#e6fffd;--color-accent-2-400:#7deee4;--color-accent-2-500:#22e0d0;--color-accent-2-600:#12b5a7;--color-accent-2-700:#0b877c;--color-accent-2-800:#075a53;--color-accent-2-900:#053b36}
[data-theme="sunset"]{--color-bg:#15100a;--color-surface:#221b12;--color-text:#f4eee3;--color-divider:color-mix(in srgb, #f4eee3 17%, transparent);--color-accent:#ffb020;--color-accent-2:#ffb020;--color-accent-100:#fff6e0;--color-accent-200:#ffe6b0;--color-accent-300:#fff6e0;--color-accent-400:#ffd071;--color-accent-500:#ffb020;--color-accent-600:#d68c12;--color-accent-700:#a1690b;--color-accent-800:#6e4707;--color-accent-900:#472d04;--color-accent-2-100:#fff6e0;--color-accent-2-200:#ffe6b0;--color-accent-2-300:#fff6e0;--color-accent-2-400:#ffd071;--color-accent-2-500:#ffb020;--color-accent-2-600:#d68c12;--color-accent-2-700:#a1690b;--color-accent-2-800:#6e4707;--color-accent-2-900:#472d04}
[data-theme="pride"]{--color-bg:#121016;--color-surface:#1d1a24;--color-text:#f4eef8;--color-divider:color-mix(in srgb, #f4eef8 17%, transparent);--color-accent:#ff5c8a;--color-accent-2:#ff5c8a;--color-accent-100:#fff0f4;--color-accent-200:#ffd1de;--color-accent-300:#fff0f4;--color-accent-400:#ff8fae;--color-accent-500:#ff5c8a;--color-accent-600:#d6406c;--color-accent-700:#a02d50;--color-accent-800:#6e1e37;--color-accent-900:#481324;--color-accent-2-100:#fff0f4;--color-accent-2-200:#ffd1de;--color-accent-2-300:#fff0f4;--color-accent-2-400:#ff8fae;--color-accent-2-500:#ff5c8a;--color-accent-2-600:#d6406c;--color-accent-2-700:#a02d50;--color-accent-2-800:#6e1e37;--color-accent-2-900:#481324;--rainbow:linear-gradient(90deg,#ff3b3b 0 16.6%,#ff8c1a 16.6% 33.3%,#ffd400 33.3% 50%,#2ecc71 50% 66.6%,#3b82f6 66.6% 83.3%,#a855f7 83.3% 100%);--rainbow-v:linear-gradient(180deg,#ff3b3b 0 16.6%,#ff8c1a 16.6% 33.3%,#ffd400 33.3% 50%,#2ecc71 50% 66.6%,#3b82f6 66.6% 83.3%,#a855f7 83.3% 100%)}
/* Pride: the six stripes run along the top bar and down the edge of the
   rail, round the brand mark and the primary buttons, and across the
   theme tile. Everything else is the accent, so the screen stays
   readable — the flag is the frame, not the wallpaper. */
[data-theme="pride"] .topbar{border-bottom-color:transparent;background-image:var(--rainbow);background-size:100% 3px;background-repeat:no-repeat;background-position:left bottom}
[data-theme="pride"] .rail{border-right-color:transparent;background-image:var(--rainbow-v);background-size:3px 100%;background-repeat:no-repeat;background-position:right top}
[data-theme="pride"] .brand-mark:not(.img){box-shadow:none;background:var(--rainbow);color:#fff}
[data-theme="pride"] .btn-primary{border-color:transparent;background:linear-gradient(var(--color-surface),var(--color-surface)) padding-box,var(--rainbow) border-box;color:var(--color-text)}
[data-theme="pride"] .btn-primary:hover{background:linear-gradient(color-mix(in srgb,var(--color-accent) 14%,var(--color-surface)),color-mix(in srgb,var(--color-accent) 14%,var(--color-surface))) padding-box,var(--rainbow) border-box}
[data-theme="pride"] .theme-spacer{background:var(--rainbow);height:4px;border-radius:2px;align-self:center}

/* — rail children: the filters, with counts or a status dot — */
/* A badge is a fixed 16px pill, not a tag left to size itself. A bare .tag
   has no line-height of its own, so in the menu it inherited the row's
   21px one and came out 23px tall — 25px with the outline variant's ring.
   That pushed Leads and Jobs to 39px and Clients to 37px while every row
   without a count stayed 35px, and collapsing the menu (badges gone) shrank
   them back. The few pixels that kept moving were vertical, not the side
   padding. box-sizing so the ring is inside the 16px, not added to it. */
.rail-b{font-size:10px;line-height:14px;padding:0 6px;height:16px;box-sizing:border-box;
  font-variant-numeric:tabular-nums;flex:0 0 auto}
.rail-c{font-size:12px;opacity:.55;flex:0 0 auto}
/* Indented past the icon and hung off a rule, so a filter reads as
   belonging to the section above it rather than as another section. */
.rail-kids{display:flex;flex-direction:column;gap:1px;
  margin:2px 0 6px 26px;padding-left:10px;
  border-left:1px solid var(--color-divider)}
.rail-k{display:flex;align-items:center;gap:6px;width:100%;text-align:left;
  padding:5px 8px;border:0;border-radius:var(--radius-sm);cursor:pointer;
  background:transparent;color:color-mix(in srgb,var(--color-text) 72%,transparent);
  font:inherit;font-size:12.5px}
.rail-k:hover{background:color-mix(in srgb,var(--color-text) 6%,transparent);color:var(--color-text)}
.rail-k.on{background:color-mix(in srgb,var(--color-accent) 12%,transparent);color:var(--color-accent)}
.rail-k .rail-l{flex:1;min-width:0}
.rail-k .rail-b{padding:0 6px}
html[data-nav="collapsed"] .rail-kids,
html[data-nav="collapsed"] .rail-b,
html[data-nav="collapsed"] .rail-c{display:none}
@media (max-width:820px){
  html[data-nav="collapsed"] .rail-kids,
  html[data-nav="collapsed"] .rail-b,
  html[data-nav="collapsed"] .rail-c{display:revert}
}

/* =====================================================================
   Modals — asking, and forms.

   Add and edit are the same modal with different contents. The record
   underneath stays as it was, which is what lets the top of it be a
   summary rather than a form.
   ================================================================== */
.backdrop{position:fixed;inset:0;z-index:100;display:grid;place-items:center;
  padding:16px;overflow:auto;background:color-mix(in srgb,#000 48%,transparent)}
.backdrop.top{align-items:start;padding:5vh 16px}

.modal{width:min(560px,100%);display:flex;flex-direction:column;
  background:var(--color-surface);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg);animation:ae-rise .18s ease both}
.modal.wide{width:min(720px,100%)}
@keyframes ae-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.modal{animation:none}}

.modal-h{display:flex;align-items:flex-start;gap:10px;padding:14px 16px;
  border-bottom:1px solid var(--color-divider)}
.modal-h>.ph{font-size:19px;color:var(--color-accent);margin-top:2px}
.modal-h-main{min-width:0;flex:1}
.modal-note{margin:2px 0 0;font-size:11.5px;color:var(--color-muted);text-wrap:pretty}
.modal-b{padding:14px 16px;display:flex;flex-direction:column;gap:12px;
  max-height:64vh;overflow-y:auto}
.modal-f{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:12px 16px;
  border-top:1px solid var(--color-divider)}
.modal-foot-note{flex:1 1 170px;font-size:11.5px;color:var(--color-muted);text-wrap:pretty}
.modal-f-acts{display:flex;gap:8px;margin-left:auto;flex-wrap:wrap}

.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:10px 14px}
.form-grid .wide{grid-column:1 / -1}
.form-err{display:flex;align-items:flex-start;gap:8px;padding:9px 11px;
  border-radius:var(--radius-md);background:var(--color-accent-900);
  box-shadow:inset 0 0 0 1px var(--color-accent);font-size:12.5px;text-wrap:pretty}
.form-err .ph{font-size:15px;color:var(--color-accent);margin-top:1px}
.req{color:var(--color-accent)}
/* The band of ticks: side by side while there is room, wrapping when there
   is not, and never sharing a row with a labelled input. */
.field.checks{display:flex;flex-wrap:wrap;align-items:flex-start;gap:6px 20px;
  padding-top:2px}
.check-cell{display:grid;gap:2px}
/* Several ticks answering one question, as one field. Columns rather than
   a single row: eight kinds of work across a modal would be unreadable,
   and down the left they scan as a list of what somebody can do. */
.check-band{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
  gap:0 16px}
.check-band .check{min-height:28px}
.check{display:flex;align-items:center;gap:8px;min-height:32px;cursor:pointer;
  font-size:13.5px;color:var(--color-text)}
.check input{width:16px;height:16px;min-height:0;padding:0;margin:0;flex:0 0 auto;
  accent-color:var(--color-accent)}

/* — the question. Says what will happen, not just "are you sure" — */
.dlg{padding:16px;display:flex;flex-direction:column;gap:10px}
.dlg-id{display:flex;align-items:flex-start;gap:11px}
.dlg-id-main{min-width:0}
.dlg-badge{width:38px;height:38px;flex:0 0 auto;border-radius:var(--radius-md);
  display:grid;place-items:center;font-size:19px;
  background:color-mix(in srgb,var(--color-accent) 16%,transparent);color:var(--color-accent)}
.dlg-badge.ok{background:color-mix(in srgb,var(--color-ok) 16%,transparent);color:var(--color-ok)}
.dlg-badge.warn{background:color-mix(in srgb,var(--color-warn) 16%,transparent);color:var(--color-warn)}
.dlg-badge.danger{background:color-mix(in srgb,var(--color-danger) 16%,transparent);color:var(--color-danger)}
.dlg-word{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.dlg-t{font-family:var(--font-heading);font-weight:var(--font-heading-weight);font-size:18px}
.dlg-b{margin:4px 0 0;font-size:13.5px;opacity:.85;text-wrap:pretty}
.dlg-tick{padding:8px 10px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-text) 5%,transparent)}
.dlg-tick-note{margin:-4px 0 0;padding:0 10px;font-size:11px;color:var(--color-muted)}
.dlg-acts{display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-top:4px}
.dlg-code{font-family:var(--font-mono);font-size:14px;letter-spacing:.06em;
  background:var(--color-bg);padding:1px 7px;border-radius:var(--radius-sm)}

/* =====================================================================
   The record — a card, the tabs, a panel.

   Read at the top, choose in the middle, and whatever the tab holds
   below. Nothing on the card is an input: editing opens the modal, which
   is what keeps the summary safe to glance at. A heading that is also a
   field is one where "what is this worth" might be a box somebody has
   half-typed into.

   The whole column scrolls as one, rather than a fixed header over a
   scrolling pane — on a phone a pinned header eats half the screen.
   ================================================================== */
/* The detail column is not itself a card any more — the card, the tabs
   and the panel are separate surfaces sitting on the page.

   Nor does it scroll. The card, the tabs and whatever can be DONE on a tab
   stay put, and only the content underneath moves — so the tab you are on,
   the record you are on, and the Add or Reply button are all still there
   however far down a long list you have read. */
.split-detail{display:flex;flex-direction:column;min-height:0;overflow:hidden;
  padding:2px;container-type:inline-size;
  background:transparent;box-shadow:none;border-radius:0}
.split.one.showing .split-detail{display:flex}
.rec-card,.split-detail .tabs{flex:0 0 auto}
.rec-pane{flex:1;min-height:0;display:flex;flex-direction:column}
.pane-bar{flex:0 0 auto}
/* A panel's hairline is an OUTSET shadow — 0 blur, 1px spread — so it is
   painted just outside the panel's box. This scrolls, and a scrolling box
   clips at its own edges: a panel flush against the top of the pane lost
   the line along its top, and one flush against the left lost its left.
   Nothing was wrong with the panels; there was nowhere for their outline
   to be drawn. A couple of pixels of room is the whole fix.

   (overflow-y:auto also computes overflow-x to auto, so the left and right
   edges clip too even though nothing ever scrolls sideways here.) */
.pane-body{flex:1;min-height:0;overflow-y:auto;padding:2px}
/* Panels stacked straight into a pane, rather than inside a .rec-cols
   grid that already spaces them. Without this they touch, and two panels
   with no gap read as one panel with a rule through it. */
.pane-body > .panel + .panel{margin-top:12px}

.rec-card{background:var(--color-surface);border-radius:var(--radius-md);
  box-shadow:var(--shadow-sm);overflow:hidden;margin-bottom:12px}
.rec-id{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;padding:14px}
.rec-av{width:40px;height:40px;flex:0 0 auto;border-radius:var(--radius-md);
  background:var(--color-accent-800);color:var(--color-accent-100);
  display:grid;place-items:center;font-family:var(--font-heading);font-size:14px}
.rec-id-main{min-width:0;flex:1}
.rec-h-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.rec-h{font-size:17px;margin:0}
.rec-sub{font-size:12px;color:var(--color-muted);overflow-wrap:anywhere}
.rec-acts{display:flex;gap:6px;flex-wrap:wrap}
.rec-acts .btn-secondary{padding:4px 10px;font-size:13px}
.rec-back{display:none}
.split.one.showing .rec-back{display:inline-flex}
/* One column: the back arrow tucks under the avatar rather than taking a
   column of its own to the left, so the name and buttons get the width. */
.split.one.showing .rec-id{display:grid;grid-template-columns:40px minmax(0,1fr);gap:6px 12px;align-items:start}
.split.one.showing .rec-av{grid-column:1;grid-row:1}
.split.one.showing .rec-back{grid-column:1;grid-row:2;justify-self:center;width:32px;height:32px}
.split.one.showing .rec-id-main{grid-column:2;grid-row:1 / span 2}
.split.one.showing .rec-acts{grid-column:1 / -1;grid-row:3;width:100%}

/* The strip of facts. One divider colour showing through a 1px grid gap,
   so the rules cannot double up where the cells wrap onto a second row. */
.rec-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));
  gap:1px;background:var(--color-surface);border-top:1px solid var(--color-divider)}
/* The rules are drawn by each cell into the 1px gap above and to its left,
   rather than by a divider-coloured background showing through. Five facts
   never fill two or three columns evenly, and a background would leave the
   empty cell reading as a highlighted box. The outermost rules fall outside
   the card, which clips them. */
.fact{padding:9px 14px;min-width:0;
  box-shadow:-1px 0 0 0 var(--color-divider),0 -1px 0 0 var(--color-divider)}
.fact-l{font-size:10px;letter-spacing:.05em;text-transform:uppercase;color:var(--color-muted)}
.fact-v{font-size:13.5px;overflow-wrap:anywhere}

/* The tabs sit between the card and the panel, on their own line. */
.split-detail .tabs{padding:0;margin-bottom:12px}
/* The tab bar scrolls sideways rather than wrapping — a wrapped row changes
   the header's height as you move between tabs, which makes the page jump.
   Its scrollbar is hidden: a half-cut tab is the affordance, and the bar
   itself reads as a stray rule under the tabs. */
.tabs{scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tab-n{font-size:11px;color:var(--color-muted);font-variant-numeric:tabular-nums}
.tab.on .tab-n{color:var(--color-accent)}

/* Two columns when the RECORD has room for them — measured on the record
   itself rather than the window, so narrowing the rail widens this
   instead of leaving it at a size chosen for a smaller page. Without
   container query support it stays stacked, which is the safe half. */
.rec-cols{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start}
@container (min-width:700px){
  .rec-cols{grid-template-columns:minmax(0,1fr) 290px}
}
.rec-main,.rec-side{display:flex;flex-direction:column;gap:12px;min-width:0}

.panel{background:var(--color-surface);border-radius:var(--radius-md);
  box-shadow:var(--shadow-sm);padding:14px}
.panel-h{display:flex;align-items:center;gap:8px;margin-bottom:10px;font-size:11px;
  font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.panel-h.accent{color:var(--color-accent)}
.panel-n{margin-left:auto;letter-spacing:0;font-variant-numeric:tabular-nums}
.panel-p{margin:0;font-size:13px;text-wrap:pretty;overflow-wrap:anywhere}
.rec-foot{margin-top:12px;font-size:11.5px}
.small{font-size:12.5px}

/* — history: what happened, and who — */
.feed{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.feed li{display:flex;gap:8px}
.feed .ph{font-size:13px;opacity:.7;margin-top:3px}
.feed-t{font-size:12.5px;text-wrap:pretty}
.feed-w{font-size:11px;color:var(--color-muted)}

/* The bar above a pane: what this tab is, and what can be done about it. */
.pane-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.pane-bar-note{flex:1 1 200px;font-size:11.5px;color:var(--color-muted);text-wrap:pretty}
.panel-act{margin-left:auto;padding:2px 9px;font-size:12px;letter-spacing:0;
  text-transform:none;font-weight:400}
.panel-h .panel-n + .panel-act{margin-left:8px}
/* The other kind of button in a panel heading: the one action the panel
   is really for, shaped like the record card own actions rather than
   like a quiet link. panel-act stays for the quieter ones. */
.panel-h .panel-btn{margin-left:auto;padding:4px 10px;font-size:13px;
  letter-spacing:0;text-transform:none}
.panel-h .panel-n + .panel-btn{margin-left:8px}

/* =====================================================================
   Tables

   The rules are drawn as row-level gradients rather than cell borders, so
   the line fades out at both ends of the row instead of stopping square —
   the design's, and the reason a table here does not read as a grid.
   ================================================================== */
.table-wrap{overflow-x:auto;overflow-y:hidden}
.table{width:100%;border-collapse:collapse;font-size:13.5px}
.table th{text-align:left;padding:6px 8px;white-space:nowrap;font-weight:400;
  font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:color-mix(in srgb,var(--color-text) 60%,transparent)}
.table td{padding:7px 8px;white-space:nowrap;vertical-align:top}
.table thead tr{background:linear-gradient(to right,transparent,
  var(--color-divider) 32px,var(--color-divider) calc(100% - 32px),transparent)
  no-repeat bottom / 100% 1px}
.table tbody tr{background:linear-gradient(to right,transparent,
  color-mix(in srgb,var(--color-text) 8%,transparent) 32px,
  color-mix(in srgb,var(--color-text) 8%,transparent) calc(100% - 32px),transparent)
  no-repeat bottom / 100% 1px}
.table tbody tr:hover{background:
  linear-gradient(color-mix(in srgb,var(--color-text) 4%,transparent),
                  color-mix(in srgb,var(--color-text) 4%,transparent)) no-repeat 0 0 / 100% 100%,
  linear-gradient(to right,transparent,
    color-mix(in srgb,var(--color-text) 8%,transparent) 32px,
    color-mix(in srgb,var(--color-text) 8%,transparent) calc(100% - 32px),transparent)
    no-repeat bottom / 100% 1px}
.th-sort{display:inline-flex;align-items:center;gap:5px;padding:0;border:0;
  background:transparent;color:inherit;font:inherit;font-size:11px;
  letter-spacing:inherit;text-transform:inherit;cursor:pointer}
.th-sort:hover{color:var(--color-text)}
.th-sort.on{color:var(--color-accent)}
.th-sort .ph{font-size:12px}
.td-sub{display:block;font-size:11px;color:var(--color-muted)}
.td-act{white-space:nowrap}
.table .sw{display:inline-block;width:14px;height:14px;border-radius:4px;
  vertical-align:-2px}
.td-btn{width:26px;height:26px}
.td-btn .ph{font-size:14px}
.table .tag{font-size:10.5px;padding:1px 7px}

/* The panel a table sits in. It has no heading of its own: what the tab
   is, and what can be done on it, are said once in the bar above — which
   is the part that stays put. */
.table-panel{padding:0;overflow:hidden}
.panel-table{padding:6px 8px 10px}

/* =====================================================================
   Access, drawn

   Every icon is always there and the ones that apply are lit, so a row
   reads as a shape — comparing two roles is comparing two shapes rather
   than reading sixteen words. The ones that do not apply stay visible and
   dim rather than disappearing: a gap says nothing about what is missing.
   ================================================================== */
.lvl{display:inline-flex;align-items:center;gap:10px;flex:0 0 auto}
.lvl-i{font-size:15px;color:color-mix(in srgb,var(--color-text) 22%,transparent)}
.lvl-i.on{color:var(--color-accent)}

.perm-list{display:flex;flex-direction:column}
.perm-row{display:flex;align-items:center;gap:12px;padding:7px 0;
  border-bottom:1px solid var(--color-divider)}
.perm-row:last-child{border-bottom:0}
.perm-l{flex:1;min-width:0;font-size:13px;text-wrap:pretty}

/* One icon an area, for a whole role on one line of a table. */
.area-strip{display:inline-flex;align-items:center;gap:9px;flex-wrap:wrap}
.area-i{font-size:15px;color:color-mix(in srgb,var(--color-text) 20%,transparent)}
.area-i.on{color:var(--color-accent)}

/* The grid that edits one: an area a row, three ticks across. The labels
   are on the header rather than beside every box — eight rows of "See /
   Add and edit / Delete" is the same three words twenty-four times. */
.lvl-grid{display:flex;flex-direction:column;border-radius:var(--radius-md);
  background:var(--color-bg);padding:4px 10px}
.lvl-head,.lvl-row{display:grid;grid-template-columns:1fr repeat(3,84px);
  align-items:center;gap:6px}
.lvl-head{padding:6px 0 4px;font-size:10.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--color-muted)}
.lvl-head span+span{text-align:center}
.lvl-row{padding:5px 0;border-top:1px solid var(--color-divider)}
.lvl-l{font-size:13px;min-width:0;text-wrap:pretty}
.lvl-c{display:flex;align-items:center;justify-content:center;gap:6px;min-height:30px;
  cursor:pointer;font-size:12px;color:var(--color-muted)}
.lvl-c input{width:16px;height:16px;min-height:0;padding:0;margin:0;flex:0 0 auto;
  accent-color:var(--color-accent)}
/* The word beside each box is for a narrow screen, where the header
   scrolls out of reach of the row being ticked. */
.lvl-c-t{display:none}
@media (max-width:640px){
  .lvl-head{display:none}
  .lvl-row{grid-template-columns:1fr;gap:0;padding:8px 0}
  .lvl-c{justify-content:flex-start;padding-left:10px}
  .lvl-c-t{display:inline}
}

/* =====================================================================
   Home — the dashboard

   Twelve columns, and each card says how many it wants. Twelve because it
   divides by two, three and four: a row of two halves, three thirds or
   four quarters all come out even, which is the whole reason anybody
   picked twelve for a grid.

   Below 900px every card is full width. A card narrowed to a quarter on a
   desktop is unreadable on a phone, and honouring a stored width there
   would punish somebody for having arranged their desktop nicely.
   ================================================================== */
.dash{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:12px;
  align-items:stretch}
.dash-card{grid-column:span 12;display:flex;flex-direction:column;
  background:var(--color-surface);border-radius:var(--radius-md);
  box-shadow:var(--shadow-sm);padding:14px;min-width:0}
@media (min-width:900px){
  .dash-card{grid-column:span var(--span,6)}
}

.dash-h{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.dash-h > .ph{font-size:15px;color:var(--color-accent);flex:0 0 auto}
.dash-t{flex:1;min-width:0;font-family:var(--font-heading);
  font-weight:var(--font-heading-weight);font-size:14px}
.dash-acts{display:flex;align-items:center;gap:2px;flex:0 0 auto}
/* Folded to its header: the body goes, the count stays. The header is
   the target, so it says so on hover. */
.dash-h[data-foldh]{cursor:pointer;user-select:none}
.dash-n{flex:0 0 auto;font-variant-numeric:tabular-nums}
.dash-fold{flex:0 0 auto}
.dash-card.folded .dash-b{display:none}
.dash-card.folded .dash-h{margin-bottom:0}
.dash-card.folded{align-self:start}
/* While arranging, the cards say so — otherwise the only sign is a row of
   small buttons somebody has to notice. */
.dash.arranging .dash-card{outline:1px dashed color-mix(in srgb,var(--color-accent) 55%,transparent);
  outline-offset:-1px}
.dash-b{display:flex;flex-direction:column;gap:12px;min-width:0}

/* Put away, and how to get it back. Full width so it never sits beside a
   card and reads as one. */
.dash-off{grid-column:span 12;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:10px 12px;border-radius:var(--radius-md);background:var(--color-bg)}
.dash-off-l{font-size:12px;color:var(--color-muted)}

.dash-note{margin:0;font-size:11.5px;color:var(--color-muted);line-height:1.5;
  text-wrap:pretty}

/* A fact or a cell that is also a way through — the client on a job, the
   job on a quote. Both read as ordinary text until they are pointed at,
   because a card of facts with one of them shouting is a card somebody
   stops reading, and a table where half the cells are blue is a table
   nobody scans. */
.fact-go,.cell-go{display:inline-flex;align-items:center;gap:5px;max-width:100%;
  padding:0;border:0;background:transparent;font:inherit;cursor:pointer;
  text-align:left}
.fact-go{color:var(--color-accent)}
/* In a table it stays the text colour: a column of names all in the accent
   would be the loudest thing on the screen and none of it is urgent. */
.cell-go{color:inherit}
.cell-go .ph{opacity:0;transition:opacity .1s ease}
.cell-go:hover,.cell-go:focus-visible{color:var(--color-accent)}
.cell-go:hover .ph,.cell-go:focus-visible .ph{opacity:.7}
.fact-go:hover,.fact-go:focus-visible{text-decoration:underline}
.fact-go .ph,.cell-go .ph{font-size:12px;flex:0 0 auto}
.fact-go .ph{opacity:.7}
@media (prefers-reduced-motion:reduce){.cell-go .ph{transition:none}}

/* A block inside a card: a heading with a count, then rows that go
   somewhere. Every row is a button, because every row has an answer. */
/* Each block sits under its own rule, with more air above it than the
   rows inside it have. Four lists in a column with only a change of
   typeface between them read as one long list; the rule is what makes
   "Overdue invoices" a heading rather than a row that happens to be in
   capitals. */
.dash-sec{display:flex;flex-direction:column}
.dash-sec + .dash-sec{margin-top:6px}
.dash-sec-h{display:flex;align-items:center;gap:8px;margin-bottom:6px;padding-bottom:6px;
  border-bottom:1px solid var(--color-divider);font-size:11px;
  font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.dash-sec-t{flex:1;min-width:0}

.dash-row{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;
  padding:7px 8px;margin:0 -8px;border:0;border-radius:var(--radius-sm);
  background:transparent;color:var(--color-text);font:inherit;cursor:pointer}
.dash-row:hover,.dash-row:focus-visible{background:var(--color-bg)}
.dash-ref{flex:0 0 auto;font-size:11px;letter-spacing:.04em;color:var(--color-muted);
  font-variant-numeric:tabular-nums}
.dash-who{flex:1;min-width:0;font-size:13px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.dash-sub{display:block;font-size:11px;color:var(--color-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dash-mid{flex:0 0 auto;font-size:11.5px;color:var(--color-muted)}
.dash-mid.warn{color:var(--color-warn)}
.dash-right{flex:0 0 auto;font-size:12.5px;font-variant-numeric:tabular-nums}

/* The tiles. They are buttons where there is somewhere to go and plain
   figures where there is not — a tile that looks pressable and is not is
   worse than one that plainly is not. */
.dash-stats{gap:8px}
/* color set on purpose: a <button> takes the browser's own button text
   colour — black — unless told otherwise, and .stat had only ever been a
   span before it became something to press. Black on the dark ground was
   unreadable, and on a light theme merely wrong. */
.dash-stat{cursor:pointer;text-align:left;border:0;font:inherit;color:var(--color-text)}
.dash-stat:hover:not([disabled]),.dash-stat:focus-visible{
  box-shadow:inset 0 0 0 1px var(--color-accent)}
.dash-stat[disabled]{cursor:default}
.stat-s{font-size:11px;color:var(--color-muted);text-wrap:pretty}

/* What happened while you were away. */
.dash-feed{display:flex;flex-direction:column;gap:2px}
.dash-fi{display:flex;align-items:baseline;gap:8px;padding:5px 0;
  border-bottom:1px solid var(--color-divider)}
.dash-fi:last-child{border-bottom:0}
.dash-fw{flex:0 0 auto;font-size:12px;font-weight:500}
.dash-fb{flex:1;min-width:0;font-size:12.5px;color:var(--color-muted);text-wrap:pretty}
.dash-ft{flex:0 0 auto;font-size:11px;color:var(--color-muted);white-space:nowrap}

/* Bars, in CSS rather than SVG.

   Twelve months of two series is a job for a flex row: it inherits the
   theme's colours without a hard-coded hex, it reflows on a phone, and
   there is no viewBox arithmetic to get wrong. The figures live in each
   column's tooltip, because a bar chart answers "which month" and never
   "how much". */
.bars{display:flex;align-items:flex-end;gap:4px;height:110px;padding-top:4px}
.bar-col{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;
  gap:4px;height:100%}
.bar-set{flex:1;display:flex;align-items:flex-end;gap:2px;width:100%;min-height:0}
.bar{flex:1;min-height:2px;border-radius:2px 2px 0 0}
.bar-a{background:var(--color-accent)}
.bar-b{background:color-mix(in srgb,var(--color-accent-2,var(--color-accent)) 55%,transparent)}
.bar-l{font-size:10px;color:var(--color-muted)}

.legend{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--color-muted);
  flex-wrap:wrap}
.legend .key{width:9px;height:9px;border-radius:2px;flex:0 0 auto}
.legend .key + span{margin-right:8px}
.key-a{background:var(--color-accent)}
.key-b{background:color-mix(in srgb,var(--color-accent-2,var(--color-accent)) 55%,transparent)}
.legend-top{margin-left:auto}

/* The pipeline: a bar per stage on one shared scale, so its shape reads
   without anybody reading a number. */
.pipe{display:flex;flex-direction:column;gap:2px}
.pipe-row{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:5px 8px;margin:0 -8px;border:0;border-radius:var(--radius-sm);
  background:transparent;color:var(--color-text);font:inherit;cursor:pointer}
.pipe-row:hover,.pipe-row:focus-visible{background:var(--color-bg)}
.pipe-l{flex:0 0 88px;font-size:12px;color:var(--color-muted)}
.pipe-track{flex:1;min-width:0;height:8px;border-radius:999px;background:var(--color-bg)}
.pipe-bar{display:block;height:100%;border-radius:999px;background:var(--color-accent);
  min-width:3px}
.pipe-n{flex:0 0 auto;font-size:12.5px;font-variant-numeric:tabular-nums}
.pipe-v{flex:0 0 58px;text-align:right;font-size:11.5px;color:var(--color-muted);
  font-variant-numeric:tabular-nums}

/* =====================================================================
   The lightbox

   Below the modal layer on purpose: a confirm opened from inside it —
   removing a photograph — has to sit above it and take the keys, or Escape
   would answer the question by closing what asked it.
   ================================================================== */
.lb{position:fixed;inset:0;z-index:90;display:grid;
  grid-template-rows:auto 1fr;grid-template-columns:1fr;
  background:color-mix(in srgb,var(--color-neutral-900) 88%,transparent);
  backdrop-filter:blur(3px)}
body.lb-open{overflow:hidden}

.lb-top{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;
  background:color-mix(in srgb,var(--color-surface) 82%,transparent)}
.lb-id{flex:1;min-width:0}
.lb-t{font-family:var(--font-heading);font-size:15px;overflow-wrap:anywhere}
.lb-s{font-size:12px;color:var(--color-muted);text-wrap:pretty}
.lb-acts{display:flex;align-items:center;gap:4px;flex:0 0 auto;flex-wrap:wrap;
  justify-content:flex-end}
.lb-b{color:var(--color-text)}
.lb-b.on{color:var(--color-accent);
  background:color-mix(in srgb,var(--color-accent) 15%,transparent)}

.lb-mid{position:relative;display:flex;align-items:center;justify-content:center;
  min-height:0;padding:14px}
.lb-stage{display:flex;align-items:center;justify-content:center;
  max-width:100%;max-height:100%}
.lb-stage img{max-width:100%;max-height:calc(100vh - 120px);object-fit:contain;
  border-radius:var(--radius-md)}
.lb-none{display:flex;flex-direction:column;align-items:center;gap:10px;
  padding:40px;text-align:center;color:var(--color-muted)}
.lb-none .ph{font-size:34px}

/* Over the picture rather than beside it: the picture is the point, and a
   pair of columns for two buttons would shrink it on every screen. */
.lb-nav{position:absolute;top:50%;transform:translateY(-50%);width:42px;height:42px;
  display:grid;place-items:center;border:0;border-radius:50%;cursor:pointer;
  color:var(--color-text);background:color-mix(in srgb,var(--color-surface) 88%,transparent);
  box-shadow:var(--shadow-md)}
.lb-nav:hover{color:var(--color-accent)}
.lb-nav .ph{font-size:20px}
.lb-nav[data-lb="prev"]{left:10px}
.lb-nav[data-lb="next"]{right:10px}

.lb-side{grid-row:1 / -1;grid-column:2;width:min(360px,86vw);overflow-y:auto;
  padding:14px;display:flex;flex-direction:column;gap:14px;
  background:var(--color-surface)}
/* The panel is a second column only when it is there. */
.lb:has(.lb-side){grid-template-columns:1fr auto}
.lb-sec{display:flex;flex-direction:column;gap:8px}
.lb-map{width:100%;height:200px;border:0;border-radius:var(--radius-md);
  background:var(--color-bg)}
.lb-maplink{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;
  color:var(--color-accent)}
.lb-note{margin:0;font-size:11px;color:var(--color-muted);text-wrap:pretty}

@media (max-width:760px){
  /* The details go under the picture rather than beside it. */
  .lb:has(.lb-side){grid-template-columns:1fr;grid-template-rows:auto 1fr auto}
  .lb-side{grid-row:auto;grid-column:1;width:auto;max-height:46vh}
  .lb-stage img{max-height:calc(100vh - 420px)}
}

/* The two pickers in a gallery's heading. */
.panel-h-tools{margin-left:auto;display:flex;align-items:center;gap:6px}
.gal-sel{width:auto;min-height:28px;padding:2px 24px 2px 8px;font-size:12px;
  text-transform:none;letter-spacing:0}

/* Under a table: how much of it is on screen, and how to see the rest.
   Small, quiet, and only drawn when there is more than a screenful. */
.table-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:8px 2px 2px;font-size:12px;color:var(--color-muted)}
.tf-n{flex:1;min-width:0}
.tf-acts{display:flex;align-items:center;gap:6px;flex:0 0 auto}
.tf-size{min-height:28px;padding:2px 24px 2px 8px;font-size:12px;width:auto}
.tf-p{min-width:58px;text-align:center;font-variant-numeric:tabular-nums}
.td-btn[disabled]{opacity:.35;pointer-events:none}

/* =====================================================================
   Photographs

   A tile is a button, not a link: a photograph is looked at, and sending
   somebody to a new tab to do it loses the list, the next one, and the
   way back.

   Three sizes because they answer different questions — hunting for one
   picture among two hundred wants small tiles, checking whether the work
   was done properly wants large ones.
   ================================================================== */
.gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
.gal-sm{grid-template-columns:repeat(auto-fill,minmax(110px,1fr))}
.gal-md{grid-template-columns:repeat(auto-fill,minmax(170px,1fr))}
.gal-lg{grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
/* The ring is a layer OVER the tile, not a shadow or an outline.

   Both of those are painted with the element's own background — Chrome
   draws an outline there too, whatever the spec's painting order says —
   which puts them under the element's children. The photograph goes edge
   to edge, so it covered the ring along every side it touched and left it
   showing only around the words. Neither an inset shadow nor an outline
   can fix that, and swapping one for the other fixed nothing.

   ::after is the last child, so it is painted after the picture. inset:0
   with an inset shadow draws the hairline just inside the tile, so nothing
   moves when it lights up, and pointer-events:none keeps it out of the way
   of the click. */
.shot{position:relative;display:flex;flex-direction:column;gap:5px;
  border-radius:var(--radius-md);
  overflow:hidden;background:var(--color-bg);padding:0 0 8px;text-align:left;
  border:0;font:inherit;cursor:pointer;color:var(--color-text)}
.shot::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;box-shadow:inset 0 0 0 1px var(--color-divider)}
.shot:hover::after,.shot:focus-visible::after{
  box-shadow:inset 0 0 0 1px var(--color-accent)}
.shot-img{position:relative;display:block}
.shot img{display:block;width:100%;height:110px;object-fit:cover}
.gal-sm .shot img{height:84px}
.gal-lg .shot img{height:170px}
/* What is worth knowing before opening it, over the picture rather than
   under it — the space under the picture is for words. */
.shot-tags{position:absolute;right:5px;bottom:5px;display:flex;gap:4px}
.shot-tags .ph{font-size:12px;color:#fff;padding:3px;border-radius:5px;
  background:color-mix(in srgb,var(--color-neutral-900) 62%,transparent)}
.shot{position:relative}
.shot-n{padding:0 8px;font-size:11.5px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.shot-n:last-child{padding-bottom:8px}
/* Select mode: a ring on each tile that can be picked, filled when it
   is, and a bar above the gallery saying how many and what can be done
   with them. */
.shot.pickable::before{content:"";position:absolute;top:7px;left:7px;z-index:2;width:20px;height:20px;
  border-radius:50%;box-sizing:border-box;border:2px solid #fff;
  background:color-mix(in srgb,var(--color-neutral-900) 45%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,#000 35%,transparent)}
.shot.picked::before{background:var(--color-accent);border-color:#fff}
.shot.picked{background:color-mix(in srgb,var(--color-accent) 14%,var(--color-bg))}
.shot.picked::after{box-shadow:inset 0 0 0 2px var(--color-accent)}
.gallery.selecting .shot:not(.pickable){opacity:.45;cursor:not-allowed}
.gal-pickbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 10px;padding:8px 10px;
  border-radius:var(--radius-md);background:color-mix(in srgb,var(--color-accent) 10%,transparent)}
.gal-pickbar-n{flex:1;min-width:0;font-size:12.5px}
.gal-selbtn.on{box-shadow:inset 0 0 0 1px var(--color-accent);color:var(--color-accent)}
.panel-h .gal-selbtn{margin-left:8px}

/* Whose it is, under the name. Only drawn on the all-records gallery — on
   a record it would be that record's own name on every tile. */
.shot-w{padding:0 8px 8px;font-size:10.5px;color:var(--color-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Over the picture rather than under it: it is a property of what is in
   the frame, and the caption is already two lines. */
.shot-p{position:absolute;top:6px;right:6px;font-size:13px;color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,.7)}

/* A list of people, addresses, jobs or documents — one shape for all of
   them, because they are the same thing: a row with a mark, what it is,
   and what can be done about it. */
.people{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.person{display:flex;align-items:flex-start;gap:10px;padding:10px 0;
  border-bottom:1px solid var(--color-divider)}
.person:last-child{border-bottom:0}
.person:first-child{padding-top:0}
.person.gone{opacity:.55}
.person-ic{font-size:16px;color:var(--color-muted);margin-top:2px}
.person-main{flex:1;min-width:0}
.person-t{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:13.5px}
.person-t .tag{font-size:10px;padding:1px 7px}
.person-s{font-size:12px;color:var(--color-muted);overflow-wrap:anywhere}
.person-w{display:flex;align-items:center;gap:5px;margin-top:2px;font-size:11px;
  color:var(--color-muted)}
.person-v{flex:0 0 auto;font-family:var(--font-mono);font-size:12.5px;
  font-variant-numeric:tabular-nums}
.person-acts{display:flex;gap:6px;flex-wrap:wrap;flex:0 0 auto}
.person-acts .btn-secondary{padding:3px 9px;font-size:12px}
.rec-av.small{width:30px;height:30px;font-size:12px}

/* Figures worth reading at a glance, inside a panel. */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(92px,1fr));gap:8px}
.stat{display:flex;flex-direction:column;gap:1px;padding:8px 10px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-text) 5%,transparent)}
.stat-v{font-size:18px;font-weight:500;font-variant-numeric:tabular-nums;line-height:1.15}
.stat-l{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted)}
.stat.warn .stat-v{color:var(--color-warn)}
.pane-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}

@container (max-width:520px){
  .person{flex-wrap:wrap}
  .person-acts{width:100%}
}

/* Waiting. One shape for every pane, so nothing jumps when the list it
   stands in for arrives. */
.loading{display:flex;align-items:center;gap:9px;padding:8px 0;
  font-size:12.5px;color:var(--color-muted)}
.spin{width:14px;height:14px;flex:0 0 auto;border-radius:50%;
  border:2px solid color-mix(in srgb,var(--color-text) 18%,transparent);
  border-top-color:var(--color-accent);animation:ae-spin .7s linear infinite}
@keyframes ae-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spin{animation-duration:2.4s}}

.recs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.recs audio{width:100%;max-width:420px;margin-top:4px}

/* — the thread, read as it stands — */
.thread{display:flex;flex-direction:column;gap:10px}
.msg{display:flex;flex-direction:column;align-items:flex-start}
.msg.out{align-items:flex-end}
.msg-day{align-self:center;margin:4px 0;font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;color:color-mix(in srgb,var(--color-text) 45%,transparent)}
.bubble{max-width:min(88%,54ch);padding:9px 12px;background:var(--color-bg);
  border-radius:var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--radius-sm);
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.msg.out .bubble{background:var(--color-accent-800);
  border-radius:var(--radius-lg) var(--radius-lg) var(--radius-sm) var(--radius-lg);
  box-shadow:inset 0 0 0 1px var(--color-accent-700)}
.msg.unseen .bubble{box-shadow:inset 0 0 0 1px var(--color-accent),
  0 0 0 3px color-mix(in srgb,var(--color-accent) 18%,transparent)}
.bubble-h{display:flex;align-items:center;gap:6px;margin-bottom:3px;font-size:10.5px;
  letter-spacing:.06em;text-transform:uppercase;opacity:.75}
.bubble-h .tag{padding:0 6px;font-size:9.5px}
.bubble-s{font-size:12.5px;font-weight:500;margin-bottom:2px}
.bubble-b{font-size:13px;text-wrap:pretty;overflow-wrap:anywhere}
.bubble-b p{margin:0 0 6px}
.bubble-b p:last-child{margin-bottom:0}
.bubble-b img{max-width:100%;border-radius:var(--radius-sm)}
/* The password rules under a password field, ticked off as they are met. */
.pw-rules{list-style:none;margin:2px 0 6px;padding:10px 12px;display:grid;gap:5px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-text) 6%,transparent);font-size:12.5px;line-height:1.4;grid-column:1 / -1}
.pw-rules li{display:flex;align-items:center;gap:8px;opacity:.72;transition:opacity .15s}
.pw-rules li i{width:15px;height:15px;border-radius:50%;flex:0 0 auto;display:inline-grid;place-items:center;
  border:1.5px solid currentColor;font-style:normal;font-size:10px;font-weight:700;line-height:1;opacity:.55}
.pw-rules li.ok{opacity:1;color:var(--color-ok)}
.pw-rules li.ok i{border-color:var(--color-ok);background:var(--color-ok);color:#fff;opacity:1}
.pw-rules li.ok i::before{content:"\2713"}
.pw-rules li.bad{color:var(--color-danger)}
.pw-rules li.bad i{border-color:var(--color-danger);opacity:1}
.pw-rules li.bad i::before{content:"\00d7"}
.send-state{font-size:10.5px;padding:2px 7px;max-width:100%;white-space:normal}
.send-state .ph{font-size:11px}
.bubble-f{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:5px;
  font-size:10.5px;color:color-mix(in srgb,currentColor 70%,transparent)}
/* Faded by colour, not opacity: opacity would fade the "Seen by" popover
   inside the footer with it, and a translucent list over a message is
   hard to read. */
/* Seen by, as a blob per side rather than a list of names. `cursor:help`
   because there is a tooltip worth hovering for — who, and when. */
.seen{display:inline-flex;align-items:center;gap:4px;cursor:help;position:relative;
  background:none;border:0;padding:0;font:inherit;color:inherit}
.seen-pop{position:absolute;left:0;top:calc(100% + 6px);z-index:60;min-width:200px;max-width:min(300px,80vw);
  padding:8px 10px;border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);
  box-shadow:var(--shadow-lg);text-align:left;font-size:12.5px;cursor:default;white-space:normal}
.seen-pop.above{top:auto;bottom:calc(100% + 6px)}
.msg.out .seen-pop{left:auto;right:0}
.msg-more{display:none}
.thread-more{align-self:center;margin-top:10px}
.seg{display:inline-flex;border-radius:999px;box-shadow:inset 0 0 0 1px var(--color-divider);overflow:hidden}
.seg-b{display:inline-flex;align-items:center;gap:5px;padding:5px 11px;font:inherit;font-size:12.5px;border:0;
  background:transparent;color:var(--color-muted);cursor:pointer}
.seg-b.on{background:color-mix(in srgb,var(--color-accent) 14%,transparent);color:var(--color-accent)}
.dash-sec-go{display:inline-flex;align-items:center;gap:5px;font:inherit;background:none;border:0;padding:0;
  color:inherit;cursor:pointer;text-align:left}
.dash-sec-go .ph{font-size:12px;color:var(--color-accent)}
.dash-sec .tasks{margin-bottom:4px}
.dash-sec .tl{margin-bottom:8px}
.note-pop{position:fixed;z-index:130;padding:10px 12px;border-radius:var(--radius-md);
  background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-lg);
  font-size:13px;line-height:1.5;max-height:min(60vh,420px);overflow-y:auto;cursor:default}
.has-note{cursor:pointer;border-bottom:1px dotted color-mix(in srgb,var(--color-accent) 55%,transparent)}
.note-i{font-size:11px;color:var(--color-accent);margin-left:4px;vertical-align:-1px}
/* A private task's lock (061): quiet, beside the title, for the few who can see it. */
.task-lock{font-size:11px;color:var(--color-muted);margin-left:5px;vertical-align:-1px}
/* Tick boxes on a table, and the bar above it that acts on what is ticked. */
.th-tick,.td-tick{width:34px;min-width:34px;text-align:center}
.th-tick input,.td-tick input{width:15px;height:15px;accent-color:var(--color-accent);cursor:pointer}
.table tbody tr.ticked{background:color-mix(in srgb,var(--color-accent) 9%,transparent)}
.sel-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:10px 14px;margin-bottom:12px}
.sel-n{flex:1 1 240px;font-size:13px;color:var(--color-muted)}
.sel-n .warn,.sum-v.warn,.pay-lede .warn,.pay-head .warn{color:var(--color-danger)}
.sel-acts{display:flex;gap:8px;flex-wrap:wrap}
/* Payroll (064). */
.pay-lede{margin:0 0 12px}
.pay-years{margin-bottom:12px}
.pay-head{display:flex;align-items:center;gap:14px;padding:12px 14px;margin-bottom:12px}
.pay-head-main{min-width:0;flex:1}
.pay-head-t{font-family:var(--font-heading);font-weight:var(--font-heading-weight);font-size:16px}
.pay-calc{display:flex;flex-direction:column;gap:2px;font-size:13px}
.pay-line{display:flex;justify-content:space-between;gap:14px;padding:5px 0;border-bottom:1px solid var(--color-divider)}
.pay-line-l{min-width:0;flex:1}
.pay-line-v{flex:0 0 auto;font-variant-numeric:tabular-nums}
.pay-line.minus .pay-line-v{color:var(--color-danger)}
.pay-line.total{border-bottom:0;font-weight:600;padding-top:8px}
.pay-sub{margin:12px 0 2px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted)}
.pay-notes{margin:10px 0 0;padding-left:18px;font-size:12.5px;color:var(--color-muted)}
.link-btn{background:none;border:0;padding:0;color:var(--color-accent);cursor:pointer;font:inherit;text-decoration:underline}
.fmt-list{display:flex;flex-direction:column}
.fmt-row{display:flex;align-items:center;gap:10px;padding:7px 0;border-bottom:1px solid var(--color-divider);font-size:13px}
.fmt-row:last-child{border-bottom:0}
.fmt-name{flex:1;min-width:0}
.fmt-acts{display:flex;gap:2px}
/* An audit entry in full. */
.mail-frame{width:100%;height:min(56vh,520px);border:0;border-radius:var(--radius-md);background:#fff;margin-top:10px}
.aud-kv{display:grid;grid-template-columns:minmax(70px,max-content) minmax(0,1fr);gap:5px 14px;font-size:12.5px;margin-top:4px}
.aud-diff{display:grid;grid-template-columns:minmax(90px,max-content) minmax(0,1fr) minmax(0,1fr);gap:5px 12px;font-size:12.5px;margin-top:4px}
.aud-h{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted);padding-bottom:2px;border-bottom:1px solid var(--color-divider)}
.aud-f{color:var(--color-muted)}
.aud-was,.aud-now{min-width:0;overflow-wrap:anywhere;white-space:pre-wrap}
.aud-was{color:var(--color-muted);text-decoration:line-through}
.aud-now{font-weight:600}
/* The owner's switch (069): on every page that can be hiding somebody's private
   entries. Off is a quiet chip with a lock; on is red — a state to notice — and
   the top bar carries a red line on every page while it stays on. */
.reveal-chip{font-size:12px;white-space:nowrap}
/* Phone calls (079): the filters under the chips, the call in full, the transcript. */
.call-filters{display:flex;gap:10px 14px;flex-wrap:wrap;align-items:flex-end;margin:0 0 12px}
.call-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-muted)}
.call-f input,.call-f select{font:inherit;font-size:13px;color:var(--color-text);background:var(--color-surface);border:0;
  border-radius:var(--radius-md);box-shadow:inset 0 0 0 1px var(--color-divider);padding:6px 10px;min-height:34px}
.call-filters .btn-secondary{min-height:34px}
.warn-line{display:flex;align-items:center;gap:8px;margin:0 0 10px;color:var(--color-warn);font-size:13px}
.call-dlg .call-facts{margin:10px 0 0}
.call-dlg .set-v code,.set-v code{font:12px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;word-break:break-all}
.call-audio{margin:12px 0 4px}
.call-audio audio{width:100%;max-width:560px;display:block;margin-bottom:4px}
.call-audio .muted,.call-audio p{font-size:12.5px;margin:0}
.call-audio .muted a{color:var(--color-accent)}
.call-tx-h{margin-top:14px;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.call-tx{margin-top:6px;max-height:min(46vh,420px);overflow:auto;padding:10px 12px;border-radius:var(--radius-md);
  box-shadow:inset 0 0 0 1px var(--color-divider);display:flex;flex-direction:column;gap:6px}
.tx{max-width:84%;padding:7px 11px;border-radius:14px;font-size:13px;line-height:1.4;white-space:pre-wrap}
.tx.ara{align-self:flex-start;background:color-mix(in srgb,var(--color-accent) 12%,transparent);border-bottom-left-radius:4px}
.tx.caller{align-self:flex-end;background:color-mix(in srgb,var(--color-text) 7%,transparent);border-bottom-right-radius:4px}
.tx-who{display:block;font-size:10.5px;color:var(--color-muted);margin-bottom:2px}
.tel{color:var(--color-accent);text-decoration:none;white-space:nowrap}
.tel:hover{text-decoration:underline}
.bubble-audio{display:block;width:100%;max-width:420px;margin-top:8px}
.key-once{display:flex;flex-direction:column;gap:6px;margin:10px 0 4px}
.key-once label{font-size:11.5px;color:var(--color-muted);margin-top:6px}
.key-once code{font-size:11px}
.key-row{display:flex;gap:6px;align-items:center}
.key-row input{flex:1 1 auto;min-width:0;font:12.5px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;padding:7px 10px;
  color:var(--color-text);background:var(--color-surface);border:0;border-radius:var(--radius-md);box-shadow:inset 0 0 0 1px var(--color-divider)}
.set-err-inline{color:var(--color-warn)}
.reveal-chip.on{color:#fff;background:#c62828;box-shadow:none}
.reveal-chip.on:hover{background:#a51f1f;box-shadow:none}
.task-bar .task-bar-end{margin-left:auto}
.home-head{display:flex;align-items:flex-start;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.home-head .screen-note{flex:1 1 200px;margin:0}
.home-head .reveal-chip{flex:0 0 auto;margin-left:auto}
.panel-h .reveal-chip{margin-left:auto;padding:3px 9px;font-size:11.5px;text-transform:none;letter-spacing:0}
.panel-h .panel-h-next{margin-left:0}
body.revealing .topbar{box-shadow:inset 0 3px 0 #c62828}
.field-note.warn{color:#c62828;font-weight:600}
.aud-k{color:var(--color-muted);text-transform:capitalize}
.aud-v{overflow-wrap:anywhere}
.aud-pre{font-size:12px;white-space:pre-wrap;overflow-wrap:anywhere;margin:4px 0 0;padding:8px 10px;background:var(--color-bg);border-radius:var(--radius-sm)}
/* GOV.UK check: one row per figure, stored and published side by side. */
.gov-diff{display:grid;grid-template-columns:1fr auto auto auto;gap:6px 12px;align-items:center;font-size:13px;margin-top:6px}
.gov-h{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted);padding-bottom:2px;border-bottom:1px solid var(--color-divider)}
.gov-l{min-width:0}
.gov-v{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.gov-a{color:var(--color-muted)}
.gov-new{font-weight:600}
.pay-set-l{display:block;font-size:11px;color:var(--color-muted);margin-bottom:3px}
.pay-set{max-width:100%;min-width:260px}
/* A person's dated tax codes. */
.code-list{display:flex;flex-direction:column}
.code-row{display:grid;grid-template-columns:150px auto auto 1fr auto;gap:10px;align-items:center;padding:7px 0;border-bottom:1px solid var(--color-divider);font-size:13px}
.code-row:last-child{border-bottom:0}
.code-row.now .code-code{font-weight:600}
.code-note{min-width:0;overflow-wrap:anywhere;font-size:12px}
@media (max-width:640px){.code-row{grid-template-columns:1fr auto;row-gap:2px}.code-note{grid-column:1/-1}}
/* The route plan before a visit (069). */
.rp-when{display:flex;gap:22px;flex-wrap:wrap;margin-bottom:10px}
.rp-big{display:flex;flex-direction:column;gap:1px}
.rp-l{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted)}
.rp-v{font-family:var(--font-heading);font-weight:var(--font-heading-weight);font-size:22px;line-height:1.1}
.rp-routes{display:flex;flex-direction:column;gap:6px;margin-top:8px}
.rp-route{border:1px solid var(--color-divider);border-radius:var(--radius-md);padding:8px 12px}
.rp-route.best{border-color:color-mix(in srgb,var(--color-accent) 40%,transparent)}
.rp-route summary{display:flex;justify-content:space-between;gap:12px;cursor:pointer;font-size:13px;list-style:none}
.rp-route summary::-webkit-details-marker{display:none}
.rp-route-m{white-space:nowrap;color:var(--color-muted);font-variant-numeric:tabular-nums}
.rp-steps{margin:8px 0 0;padding-left:20px;font-size:12.5px;line-height:1.5;max-height:260px;overflow:auto}
/* flex:0 0 auto — the modal body is a flex column that may scroll, and a shrinkable box with overflow hidden collapses to nothing, which also breaks the map. */
.rp-map{width:100%;height:360px;flex:0 0 auto;border-radius:var(--radius-md);margin:10px 0;overflow:hidden;background:var(--color-bg);position:relative;z-index:0}
.rp-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:6px}
.rp-route.on{background:color-mix(in srgb,var(--color-accent) 8%,transparent)}
.rp-steps li{cursor:pointer}
.rp-steps li:hover{color:var(--color-accent)}
/* Pins are sized by Leaflet (iconSize) and centred by it (iconAnchor); border-box keeps the border inside that size so the centre stays on the line. */
.rp-pin{background:transparent;border:0;box-shadow:none}
.rp-pin svg{display:block;filter:drop-shadow(0 1px 2px rgba(0,0,0,.4))}
/* The turn in hand while the route plays, top right of the map. */
.rp-now{transition:opacity .25s;position:absolute;top:10px;right:10px;z-index:900;max-width:min(64%,340px);background:var(--color-surface);color:var(--color-text);border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:8px 12px;display:flex;flex-direction:column;gap:4px;font-size:12.5px}
.rp-now.ghost{opacity:.16}
.rp-now.ghost:hover{opacity:1}
.rp-now-bar{display:flex;align-items:center;gap:4px}
.rp-ctl{width:28px;height:28px;border:1px solid var(--color-divider);border-radius:var(--radius-sm);background:var(--color-bg);color:var(--color-text);cursor:pointer;display:grid;place-items:center;font-size:15px;padding:0}
.rp-ctl:hover{border-color:var(--color-accent);color:var(--color-accent)}
.rp-ctl:disabled{opacity:.35;cursor:default}
.rp-ctl:disabled:hover{border-color:var(--color-divider);color:var(--color-text)}
.rp-ctl-main{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}
.rp-ctl-main:hover{color:#fff;filter:brightness(1.1)}
.rp-speed{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--color-muted);font-size:12px}
.rp-now-step{display:flex;flex-direction:column;gap:2px;min-height:1em}
.rp-now-n{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted)}
.rp-now-t{font-weight:600;line-height:1.35}
.rp-now-m{color:var(--color-muted);font-variant-numeric:tabular-nums}
/* A row of picture choices: the picture on the left, the words beside it, one lit. */
.pick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:6px}
.pick-opt{display:flex;align-items:center;gap:9px;padding:7px 10px;border:1px solid var(--color-divider);border-radius:var(--radius-md);background:transparent;color:var(--color-text);cursor:pointer;font:inherit;font-size:13px;text-align:left}
.pick-opt .ph{font-size:20px;color:var(--color-accent);flex:0 0 auto}
.pick-opt span{min-width:0;line-height:1.2}
.pick-opt:hover{background:color-mix(in srgb,var(--color-text) 6%,transparent)}
.pick-opt.on{border-color:var(--color-accent);background:color-mix(in srgb,var(--color-accent) 12%,transparent)}
.rp-car{box-sizing:border-box;display:grid;place-items:center;background:transparent}
.rp-car i{box-sizing:border-box;width:34px;height:34px;display:grid;place-items:center;border-radius:50%;color:#fff;font-size:20px;box-shadow:0 2px 8px rgba(0,0,0,.4);border:2px solid #fff;transition:transform .15s linear}
.veh-svg{display:inline-grid;place-items:center}
.veh-svg svg{width:1em;height:1em;display:block}
.veh-preview{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.veh-demo{width:34px;height:34px}
.leaflet-container{font-family:var(--font-body,inherit)}
@media (max-width:640px){.rp-map{height:280px}.rp-when{gap:14px}.rp-v{font-size:19px}}

/* A formatted note, wherever it is read: the same modest set the editor
   writes, kept readable. */
.rich-note p{margin:0 0 6px}
.rich-note p:last-child{margin-bottom:0}
.rich-note ul,.rich-note ol{margin:4px 0 6px;padding-left:20px}
.rich-note h1,.rich-note h2,.rich-note h3{font-size:14px;margin:6px 0 4px}
.rich-note a{color:var(--color-accent)}
.rte.compact .rte-in{min-height:110px}
.seen-pop-h{display:flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;color:var(--color-text);margin:8px 0 3px}
.seen-pop-h:first-child{margin-top:0}
.seen-pop .seen-dot.us{background:var(--color-accent)}
.seen-pop .seen-dot.them{background:transparent;border-color:var(--color-accent-2)}
.seen-pop-r{display:flex;justify-content:space-between;gap:12px;padding:2px 0 2px 15px;font-weight:400}
/* Two markers, two shapes: ours solid, the client's a ring — so they
   tell apart at a glance on any bubble, in colour or not. */
.seen-dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;box-sizing:border-box}
.seen-dot.us{background:var(--color-accent)}
.seen-dot.them{background:transparent;border:2px solid var(--color-accent-2)}
.seen-dot + .seen-dot{margin-left:-1px}
/* On our own bubbles the accent is the ground, so the markers take the
   text colour — still one solid, one ring. */
.msg.out .seen-dot.us{background:currentColor}
.msg.out .seen-dot.them{border-color:currentColor}
.all-msgs{padding:3px 9px;font-size:12px;margin-bottom:10px}

@container (max-width:520px){
  .rec-acts{width:100%}
  .rec-acts .btn-secondary{flex:1}
}

/* =====================================================================
   The rail's head, and the hover-expand

   Collapsed, the rail is a strip of icons. Hovering it expands it OVER the
   page rather than widening its column — a menu that reflows everything
   you are reading because the mouse passed near the edge is worse than one
   that stays narrow. The control becomes a pin, because somebody hovering
   to read the labels usually wants them to stay.
   ================================================================== */
.rail-top{display:flex;align-items:center;gap:9px;padding:12px 12px 10px;min-height:56px;
  flex:0 0 auto}
.brand-mark{width:28px;height:28px;flex:0 0 auto;border-radius:8px;display:grid;
  place-items:center;color:var(--color-accent);
  box-shadow:inset 0 0 0 1px var(--color-accent)}
.brand-mark .ph{font-size:16px}
/* The app's own icon as the mark — the same tile that sits on a phone's
   home screen, so the menu and the icon read as one thing. */
.brand-mark.img{box-shadow:none;overflow:hidden}
.brand-mark img{width:100%;height:100%;display:block;border-radius:8px}
.brand-name{min-width:0;flex:1;display:flex;flex-direction:column;line-height:1.2}
.brand-t{font-family:var(--font-heading);font-size:14px;white-space:nowrap}
.brand-s{font-size:11px;color:var(--color-muted);white-space:nowrap}
.rail-narrow{width:28px;height:28px;flex:0 0 auto}
.rail-narrow .ph{font-size:15px}

/* Collapsed: the head is the mark alone, until the rail is hovered. */
/* Same padding as when it is open: the mark should not shuffle sideways
   as the rail narrows. */
html[data-nav="collapsed"] .rail-top{justify-content:flex-start}
html[data-nav="collapsed"] .brand-name,
html[data-nav="collapsed"] .rail-narrow,
html[data-nav="collapsed"] .rail-b,
html[data-nav="collapsed"] .rail-c,
html[data-nav="collapsed"] .rail-kids{display:none}

@media (min-width:821px){
  /* .no-hover is set the moment somebody CLICKS collapse, and cleared when
     the pointer leaves. Without it the rail is still under the mouse, so
     the hover rule expands it again straight away and the click looks like
     it did nothing but reflow the page behind. */
  html[data-nav="collapsed"] .rail:not(.no-hover):hover,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within{
    position:fixed;top:0;bottom:0;left:0;width:234px;z-index:60;
    box-shadow:var(--shadow-lg)}
  /* Restored to what THIS stylesheet says, one by one. display:revert
     goes to the browser's default instead, which turned the brand from a
     two-line block into "Always EnergyCRM" on one line. */
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .brand-name,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .brand-name{display:flex}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-narrow,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-narrow{display:inline-flex}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-l,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-l{display:block}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-head,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-head{
    height:auto;margin:0;padding:14px 10px 4px;font-size:10px;letter-spacing:.1em;
    background:transparent}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-kids,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-kids{display:flex}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-b,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-b{display:inline-flex}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-c,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-c{display:inline-block}
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-top,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-top{padding:12px 12px 10px}
  /* width and align-self too: collapsed, an item shrinks to its icon, and
     without putting those back the hover-expanded menu highlighted only as
     much as each label rather than the full row. */
  html[data-nav="collapsed"] .rail:not(.no-hover):hover .rail-a,
  html[data-nav="collapsed"] .rail:not(.no-hover):focus-within .rail-a{gap:10px;
    width:100%;align-self:stretch}
}

/* =====================================================================
   The topbar: crumbs, search, the bell
   ================================================================== */
.crumbs{text-transform:uppercase;letter-spacing:.08em;font-size:10px}

/* Search sits right, before the bell and the screen's own buttons. */
/* The identity keeps the left, and everything else is pushed right from
   there — so hiding any one of search, bell or buttons cannot leave the
   rest bunched against the title. */
.topbar-id{margin-right:auto}
.topbar-search{position:relative;flex:0 1 340px}
.topbar-acts{margin-left:0}
@media (max-width:640px){
  .topbar-t{display:none}
}
/* Words that fit a desk but not a phone; .only-mob is their opposite. */
@media (max-width:820px){
  .hide-mob{display:none!important}
  /* Search is an icon until it is asked for; then the whole bar, over
     everything else on it, with an X to put it back. The breakpoint is
     the one .only-mob uses, so the icon and the box never both show. */
  .topbar{position:relative}
  .topbar-search{display:none}
  .topbar.search-open .topbar-search{display:flex;position:absolute;left:8px;right:8px;top:6px;bottom:6px;
    z-index:6;flex:none;max-width:none;margin:0;background:var(--color-surface)}
  .topbar.search-open .topbar-search input{flex:1;min-width:0}
  #searchOpen{flex:0 0 auto}
  .topbar.search-open #searchOpen{visibility:hidden}
  .search-x{flex:0 0 auto;width:32px;height:32px}
  /* A phone zooms into any box whose text is under 16px and does not
     zoom back out when it is left — half the screen then sits off to
     the right until somebody pinches. So on a phone every box is 16px;
     the page's own type stays as it is. */
  .input,input[type=text],input[type=email],input[type=search],input[type=password],
  input[type=number],input[type=date],input[type=time],input[type=tel],input[type=url],
  input[type=datetime-local],input[type=month],input[type=week],select,textarea,
  .dl-row input,.dl-row select,.topbar-search input,[contenteditable="true"]{font-size:16px}
}

/* The bell's popover hangs from the TOPBAR, not from the bell: a screen
   with buttons to the right of the bell would otherwise leave the panel
   short of the page edge — and on a phone, off the left of it by the
   width of those buttons. */
.topbar{position:relative}
.bell-wrap{position:static;flex:0 0 auto}
.pop.bellPop{right:16px;top:calc(100% - 4px)}
@media (max-width:820px){.pop.bellPop{right:12px}}
/* The design's: a little larger than a row icon, and carrying its own
   outline so it reads as a control rather than as a glyph floating beside
   the search box. */
.bell{position:relative;width:38px;height:38px;border-radius:var(--radius-md);
  color:var(--color-text);
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.bell .ph{font-size:19px}
.bell:hover{background:color-mix(in srgb,var(--color-text) 8%,transparent);
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.bell.has-news{color:var(--color-accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 45%,transparent)}
.bell.has-news:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}

/* A mark, not a tally. The number was never the useful part — "there is
   something" is what makes somebody look, and a two-digit count in a 15px
   circle only makes the control look broken. The ring is the topbar's own
   background, so the dot reads as sitting on top of the bell rather than
   inside it. */
.bell-n{position:absolute;top:7px;right:8px;width:8px;height:8px;padding:0;
  border-radius:999px;background:var(--color-accent);
  box-shadow:0 0 0 2px var(--color-bg);
  font-size:0;line-height:0;overflow:hidden}

/* Messages between the people here: the icon beside the bell, its
   count, and the conversation pane. */
.chat-wrap{position:static;flex:0 0 auto}
.chat-btn.has-news{color:var(--color-accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 45%,transparent)}
.chat-n{position:absolute;top:-6px;right:-7px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;
  background:var(--color-accent);color:#fff;font-size:10.5px;font-weight:600;line-height:18px;text-align:center;
  box-shadow:0 0 0 2px var(--color-bg)}
.pop.chatPop{right:61px;top:calc(100% - 4px)}
@media (max-width:820px){.pop.chatPop{right:12px}}
.pop-t .tag{margin-left:4px;padding:0 6px;font-size:9.5px}
.chat-av{width:32px;height:32px;border-radius:50%;flex:0 0 auto;display:grid;place-items:center;font-size:12px;font-weight:600;
  background:color-mix(in srgb,var(--color-accent) 20%,transparent);color:var(--color-accent)}
.row.unread .row-t{color:var(--color-accent)}
.row .row-t .tag{padding:0 6px;font-size:9.5px;height:16px;line-height:16px}
.chat-pane{display:flex;flex-direction:column;gap:10px}
/* On the Messages screen the conversation is the whole right-hand
   column, which clips at its edges — so the thread itself scrolls, under
   a heading and a box that stay put. On a person's Messages tab it sits
   inside a pane that already scrolls, and is left alone. */
.split-detail > .chat-pane{flex:1;min-height:0}
.split-detail > .chat-pane > .thread{flex:1;min-height:0;overflow-y:auto;padding:2px;-webkit-overflow-scrolling:touch}
.chat-head{display:flex;align-items:center;gap:10px;padding-bottom:8px;border-bottom:1px solid var(--color-divider)}
.chat-who{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;font-size:14px}
.chat-who b{font-weight:500}
.chat-who .muted{font-size:11.5px}
.chat-back{display:none}
.split.one .chat-back{display:inline-flex}
.chat-compose{display:flex;gap:8px;align-items:flex-start}
.chat-compose textarea{flex:1;min-height:44px;resize:vertical}
.chat-clip{width:38px;height:38px;flex:0 0 auto}
.chat-compose .btn-primary{flex:0 0 auto;height:38px}
.chat-attach{margin-bottom:-2px}
.bubble-x{width:22px;height:22px;margin-left:auto;opacity:0;transition:opacity .15s}
.msg:hover .bubble-x,.bubble-x:focus-visible{opacity:1}
@media (hover:none){.bubble-x{opacity:.6}}
.bubble-seen{display:inline-flex;align-items:center;gap:3px}
.bubble-seen.on{color:var(--color-ok)}
.msg.out .bubble-seen.on{color:inherit}
.msg.removed .bubble{opacity:.6;outline:1px dashed var(--color-warn);outline-offset:-1px}
.chat-admin{overflow:visible}

/* One popover shape for the search answers and for what is new. */
.pop{position:absolute;z-index:120;top:calc(100% + 6px);right:0;
  width:min(360px,calc(100vw - 24px));max-height:min(70vh,460px);overflow-y:auto;
  padding:6px;border-radius:var(--radius-md);
  background:var(--color-surface);box-shadow:var(--shadow-lg)}
.searchPop{left:0;right:auto;width:min(420px,calc(100vw - 24px))}
.pop-h{padding:7px 9px 4px;font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--color-accent)}
.pop-list{list-style:none;margin:0;padding:0}
.pop-i,.news-i{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:6px 9px;border:0;border-radius:var(--radius-sm);background:transparent;
  color:var(--color-text);font:inherit;cursor:pointer}
.pop-i:hover,.news-i:hover{background:color-mix(in srgb,var(--color-accent) 14%,transparent)}
.pop-t{font-size:13px}
.pop-s{font-size:11px;color:var(--color-muted);overflow-wrap:anywhere}
.pop-none{padding:12px 10px;font-size:12.5px;color:var(--color-muted)}
.pop .loading{padding:12px 10px}

.news{list-style:none;margin:0;padding:0}
.news-i{flex-direction:row;align-items:center;gap:9px;font-size:13px}
.news-i .ph{font-size:15px;color:var(--color-accent)}

/* — the crumbs: the brand, then where you are — */
.topbar-id{display:flex;flex-direction:column;justify-content:center;min-width:0;flex:0 1 auto}
.crumbs{display:flex;gap:5px;font-size:11px;color:color-mix(in srgb,var(--color-text) 45%,transparent);
  white-space:nowrap;overflow:hidden}
.crumb.brand{color:color-mix(in srgb,var(--color-text) 62%,transparent)}
.crumb.on{color:var(--color-accent)}
.topbar{align-items:center}
.topbar-t{font-size:17px;line-height:1.15;margin:0}
@media (max-width:820px){ .crumbs{display:none} }

/* =====================================================================
   A job as a timeline

   One bar per open task over the span of the job, from when it is
   expected to start to when it is expected to finish. The gridlines are
   weeks (--days is the span, so seven days is 700%/--days of the track).
   Green line: today. Orange line: the job's due date. An orange bar has
   slipped past when it is wanted; a hollow one is waiting on another.
   ================================================================== */
.tl{display:flex;flex-direction:column;gap:4px;margin:0 0 14px;padding:8px 0 10px;
  border-bottom:1px solid var(--color-divider)}
.tl-head{display:flex;justify-content:space-between;padding:0 0 4px 160px;
  font-size:11px;color:var(--color-muted);font-variant-numeric:tabular-nums}
.tl-row{display:flex;align-items:center;gap:8px;min-height:18px}
.tl-l{flex:0 0 152px;font-size:11.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tl-track{position:relative;flex:1;min-width:0;height:14px;border-radius:4px;
  background-color:var(--color-bg);
  background-image:repeating-linear-gradient(to right,
    var(--color-divider) 0 1px,transparent 1px calc(700% / var(--days,30)))}
.tl-bar{position:absolute;top:2px;bottom:2px;min-width:3px;border-radius:3px;
  background:var(--color-accent)}
.tl-bar.blocked{background:color-mix(in srgb,var(--color-accent) 30%,transparent);
  outline:1px dashed var(--color-accent);outline-offset:-1px}
.tl-bar.late{background:var(--color-warn)}
.tl-bar.late.blocked{background:color-mix(in srgb,var(--color-warn) 30%,transparent);
  outline-color:var(--color-warn)}
.tl-bar.started{box-shadow:inset 0 0 0 2px color-mix(in srgb,#fff 40%,transparent)}
.tl-today{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--color-ok);opacity:.85}
.tl-due{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--color-warn);opacity:.7}
.tl-note{padding-left:160px;margin-top:2px;font-size:11px;color:var(--color-muted);
  line-height:1.45;text-wrap:pretty}
/* A quiet fact at the right of a panel heading — "expected to finish 18 Sep". */
.panel-h-x{margin-left:auto;font-size:11px;font-weight:400;letter-spacing:0;
  text-transform:none;color:var(--color-muted)}
.panel-h-x.warn{color:var(--color-warn)}
.feed-w.warn{color:var(--color-warn)}
@media (max-width:640px){
  .tl-l{flex-basis:96px}
  .tl-head,.tl-note{padding-left:104px}
}

/* The bell's rows: who and what on one line, where and when beneath. */
.news-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.news-b > span:first-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.news-s{font-size:11px;color:var(--color-muted)}

/* The bell's list: capped, and it scrolls under its own heading. */
.bellPop{max-height:min(55vh,340px);padding-top:0}
.bellPop .pop-h{position:sticky;top:0;z-index:1;padding-top:8px;background:var(--color-surface)}

/* What came with a message: a tile per file under the bubble. */
.bubble-atts{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.bubble-att{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:4px 8px 4px 4px;
  border-radius:var(--radius-sm);background:color-mix(in srgb,var(--color-text) 7%,transparent);
  color:inherit;text-decoration:none;font-size:12px;border:0;font:inherit;cursor:pointer}
.bubble-att:hover{background:color-mix(in srgb,var(--color-accent) 18%,transparent)}
.bubble-att img{width:28px;height:28px;object-fit:cover;border-radius:4px;flex:0 0 auto}
.bubble-att .ph{font-size:16px;color:var(--color-muted);margin-left:3px}
.bubble-att span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* =====================================================================
   Taskflow — the step editor, the chooser on Add a task, the review
   ================================================================== */
.tf-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:14px 0 8px}
.tf-head .field-l{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--color-muted)}
.tf-steps{display:flex;flex-direction:column;gap:8px}
.tf-step{display:grid;grid-template-columns:26px minmax(0,1fr) auto;gap:6px 10px;align-items:start;
  padding:10px;border-radius:var(--radius-md);background:var(--color-bg)}
.tfs-n{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-size:12px;
  font-weight:500;background:color-mix(in srgb,var(--color-accent) 18%,transparent);color:var(--color-accent)}
.tf-main{display:flex;flex-direction:column;gap:6px;min-width:0}
.tf-row{display:grid;grid-template-columns:minmax(0,2fr) minmax(140px,1fr) 96px;gap:6px}
.tf-row-2{grid-template-columns:minmax(0,1fr)}
.tf-waits{display:flex;flex-wrap:wrap;align-items:center;gap:5px;font-size:12px}
.tf-waits-l{color:var(--color-muted);margin-right:2px}
.tf-wait{display:inline-flex;align-items:center;gap:5px;padding:2px 8px 2px 5px;border-radius:999px;
  cursor:pointer;background:var(--color-surface);box-shadow:inset 0 0 0 1px var(--color-divider);font-size:12px}
.tf-wait:has(input:checked){box-shadow:inset 0 0 0 1px var(--color-accent);color:var(--color-accent)}
.tf-wait input{margin:0}
.tfs-acts{display:flex;flex-direction:column;gap:2px}
@media (max-width:640px){.tf-row{grid-template-columns:minmax(0,1fr)}.tf-step{grid-template-columns:26px minmax(0,1fr)}.tfs-acts{grid-column:2;flex-direction:row}}

/* Three ways to start a task, as cards rather than a dropdown. */
.tf-pick{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:8px}
.tf-opt{display:flex;flex-direction:column;gap:3px;padding:12px;text-align:left;cursor:pointer;
  background:var(--color-bg);border:0;border-radius:var(--radius-md);color:var(--color-text);font:inherit;
  box-shadow:inset 0 0 0 1px var(--color-divider)}
.tf-opt:hover{box-shadow:inset 0 0 0 1px var(--color-accent)}
.tf-opt .ph{font-size:20px;color:var(--color-accent)}
.tf-opt-t{font-size:13.5px;font-weight:500}
.tf-opt-s{font-size:11.5px;color:var(--color-muted);text-wrap:pretty}

/* The run, before it is made. */
.tf-review{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:4px;font-size:13px}
.tf-review li{display:flex;flex-wrap:wrap;gap:4px;padding:4px 0;border-bottom:1px solid var(--color-divider)}
.tf-review li:last-child{border-bottom:0}

/* A stack trace or a request body, in a dialog: small, mono, scrolls. */
.err-pre{margin:10px 0 0;padding:8px 10px;max-height:220px;overflow:auto;border-radius:var(--radius-sm);
  background:var(--color-bg);font-family:var(--font-mono);font-size:11px;line-height:1.5;white-space:pre-wrap;
  overflow-wrap:anywhere;color:var(--color-text)}

/* =====================================================================
   Icons LAST, always.

   They are regenerated from the design's sheet whenever a screen needs a
   new one, by truncating this file at the first .ph- rule and appending
   the fresh set. Anything written after them would be thrown away by
   that — as it was once. Nothing goes below this line.
   ================================================================== */

/* ---- the quote and invoice builder ---------------------------------- */
.modal.doc-modal{width:min(1040px,100%)}
.doc-modal .dlg-t{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.doc-banner{display:flex;gap:8px;align-items:center;padding:9px 11px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-warn) 14%,transparent);font-size:12.5px}
.doc-banner .ph{color:var(--color-warn);font-size:15px}
.doc-dates{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}
.dl{display:flex;flex-direction:column;gap:6px}
.dl-head,.dl-row{display:grid;grid-template-columns:minmax(0,1fr) 62px 74px 96px 96px 132px 96px 28px;gap:6px;align-items:start}
.dl-head{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted);padding:0 8px}
.dl-row{padding:8px;border-radius:var(--radius-md);background:var(--color-bg)}
.dl-desc{display:flex;flex-direction:column;gap:4px;min-width:0}
.dl-row input,.dl-row select{min-height:30px;padding:3px 7px;font-size:13px;width:100%}
.dl-row .dl-note{font-size:12px;color:var(--color-muted)}
.dl-num{text-align:right;font-variant-numeric:tabular-nums}
.dl-total{min-height:30px;display:flex;align-items:center;justify-content:flex-end;
  font-variant-numeric:tabular-nums;font-weight:500;padding-right:2px}
.dl-row .dl-cost{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 40%,transparent)}
.dl-row .td-btn{margin-top:2px}
.dl-add{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.dl-add select{flex:1 1 240px}
.doc-sum{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,320px);gap:14px;align-items:start}
.doc-totals{display:flex;flex-direction:column;font-size:13px}
.doc-totals>div{display:flex;justify-content:space-between;gap:10px;padding:5px 0;border-bottom:1px solid var(--color-divider)}
.doc-totals>div:last-child{border-bottom:0}
.doc-totals b{font-variant-numeric:tabular-nums;font-weight:500}
.doc-totals .grand{font-size:14px}
.doc-totals .grand b{font-weight:600}
.doc-totals .muted span{color:var(--color-muted);font-size:12px}
.doc-internal{padding:10px 12px;border-radius:var(--radius-md);background:var(--color-bg);display:flex;flex-direction:column;gap:4px}
.doc-internal-l{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted)}
.doc-pay{display:flex;flex-direction:column;gap:4px}
.doc-pay-row{display:grid;grid-template-columns:auto auto minmax(0,1fr) 28px;gap:10px;align-items:center;font-size:13px;
  padding:4px 0;border-bottom:1px solid var(--color-divider)}
.doc-pay-row:last-child{border-bottom:0}
.doc-pick{display:flex;flex-direction:column;gap:6px;margin:6px 0 10px}
.modal-f>.icon-btn[data-a="remove"]{color:var(--color-muted)}
@media (max-width:760px){
  .dl-head{display:none}
  .dl-row{grid-template-columns:1fr 1fr 1fr}
  .dl-desc{grid-column:1/-1}
  .dl-row .dl-total{grid-column:1/3}
  .doc-sum{grid-template-columns:1fr}
}


/* ---- 048: what a job needs, packages, tools, pencilled-in entries -------- */
.needs-remind{display:flex;align-items:center;gap:12px;padding:12px 14px;
  border-left:3px solid var(--color-warn)}
.needs-remind>.ph{font-size:22px;color:var(--color-warn);flex:0 0 auto}
.needs-remind-b{flex:1;min-width:0;font-size:13px;line-height:1.45}
.needs-remind .btn-secondary{flex:0 0 auto}

.ip{margin-top:12px;display:flex;flex-direction:column;gap:6px}
.ip-rows{display:flex;flex-direction:column;gap:4px}
.ip-row{display:grid;grid-template-columns:minmax(0,1fr) 72px auto 28px;gap:6px;align-items:center;
  padding:6px 8px;border-radius:var(--radius-md);background:var(--color-bg)}
.ip-row select,.ip-row input{min-height:30px;padding:3px 7px;font-size:13px;width:100%}
.ip-row select{padding-right:24px}
.ip-cost{font-size:11.5px;white-space:nowrap}
.ip-sum{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;font-size:12.5px}

.pp{display:flex;flex-direction:column;gap:4px}
.pp-head,.pp-row{display:grid;grid-template-columns:minmax(0,1fr) 64px 90px 90px 90px;gap:6px;align-items:center}
.pp-head{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-muted);padding:0 8px}
.pp-row{padding:6px 8px;border-radius:var(--radius-md);background:var(--color-bg);font-size:13px}
.pp-row input{min-height:30px;padding:3px 7px;font-size:13px;width:100%}
.pp-sum{margin-top:10px;font-size:13px}

.dl-parts-t{display:inline-flex;align-items:center;gap:4px;font:inherit;font-size:11.5px;color:var(--color-accent);
  background:none;border:0;padding:2px 0;cursor:pointer;text-align:left}
.dl-parts-t .ph{font-size:12px}
.dl-parts{display:flex;flex-direction:column;gap:2px;padding:4px 0 2px 10px;
  border-left:2px solid color-mix(in srgb,var(--color-accent) 40%,transparent)}
.dl-part{display:grid;grid-template-columns:minmax(0,1fr) 56px 84px 84px 90px;gap:6px;font-size:12px;
  font-variant-numeric:tabular-nums}
.dl-itemise .check{margin-top:4px}

.tf-waits .tf-box{min-height:26px;padding:2px 24px 2px 8px;font-size:12px;width:auto;margin-right:4px}
.tb-find{max-width:220px;min-height:30px;padding:3px 9px;font-size:13px}
.tb-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}

.dg-block.tentative{background:transparent;color:var(--color-text);box-shadow:none;
  border:1.5px dashed var(--tc,var(--color-accent));padding:2.5px 4.5px}
.dg-block.tentative .dg-b1 .ph{font-size:11px;color:var(--tc,var(--color-accent));vertical-align:-1px}
.dg-block.tentative .dg-b2{opacity:.7}
/* Booked for somebody else's private task (061): the time, a lock, nothing else. */
.dg-block.private{background:var(--color-neutral-700);color:var(--color-neutral-100);cursor:default}
.dg-block.private .dg-b1 .ph{font-size:11px;vertical-align:-1px}
/* A site visit the website booked, waiting to be confirmed: solid like
   a real booking (somebody IS expected), but ringed in the warning
   colour until a person has said so. */
/* A site visit's four states, each unmistakable at a glance:
     pending    amber ring, "?"      booked by the website, not yet confirmed
     confirmed  green ring, tick     somebody is going
     overdue    red ring, "!"        the day has gone, nobody said it happened
     done       faded, tick          carried out                              */
.dg-block.pending,.dg-block.vconfirmed,.dg-block.voverdue,.dg-block.vdone{position:relative}
.dg-block.pending .dg-b1::before,.dg-block.vconfirmed .dg-b1::before,.dg-block.voverdue .dg-b1::before,.dg-block.vdone .dg-b1::before{
  display:inline-grid;place-items:center;width:14px;height:14px;margin-right:4px;border-radius:50%;
  color:#fff;font-size:10px;font-weight:700;line-height:1;vertical-align:1px}
.dg-block.pending{box-shadow:0 0 0 2px var(--color-warn)}
.dg-block.pending .dg-b1::before{content:"?";background:var(--color-warn)}
.dg-block.vconfirmed{box-shadow:0 0 0 2px var(--color-ok)}
.dg-block.vconfirmed .dg-b1::before{content:"\2713";background:var(--color-ok)}
.dg-block.voverdue{box-shadow:0 0 0 2px var(--color-danger);
  background-image:repeating-linear-gradient(135deg,transparent 0 6px,rgba(255,255,255,.14) 6px 8px)}
.dg-block.voverdue .dg-b1::before{content:"!";background:var(--color-danger)}
.dg-block.vdone{opacity:.55;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-ok) 55%,transparent)}
.dg-block.vdone .dg-b1::before{content:"\2713";background:var(--color-ok)}
.dg-block.vdone .dg-b1{text-decoration:line-through;text-decoration-thickness:1px}
/* How a closed-off visit ended (073): nobody in is a warning mark and a
   note; cancelled is grey, struck through, and says so. */
.dg-block.vmissed,.dg-block.vcancelled{position:relative}
.dg-block.vmissed .dg-b1::before,.dg-block.vcancelled .dg-b1::before{
  display:inline-grid;place-items:center;width:14px;height:14px;margin-right:4px;border-radius:50%;
  color:#fff;font-size:10px;font-weight:700;line-height:1;vertical-align:1px}
.dg-block.vmissed{opacity:.7;box-shadow:0 0 0 2px var(--color-warn)}
.dg-block.vmissed .dg-b1::before{content:"!";background:var(--color-warn)}
.dg-block.vmissed .dg-b2::after{content:" \00b7 nobody in"}
.dg-block.vcancelled{opacity:.45;box-shadow:0 0 0 2px var(--color-divider);background:var(--color-neutral-700)!important;
  background-image:repeating-linear-gradient(135deg,transparent 0 6px,rgba(0,0,0,.12) 6px 8px)!important}
.dg-block.vcancelled .dg-b1::before{content:"\00d7";background:var(--color-muted)}
.dg-block.vcancelled .dg-b1{text-decoration:line-through;text-decoration-thickness:1px}
.dg-block.vcancelled .dg-b2::after{content:" \00b7 cancelled"}

/* The day as a route: a small arrow at the foot of a cell with visits,
   opening Google Maps with the stops in order. Quiet until hovered. */
.dg-route{align-self:flex-end;margin-top:auto;display:inline-flex;align-items:center;gap:3px;
  padding:2px 6px;border-radius:999px;font-size:10px;color:var(--color-accent);text-decoration:none;
  background:color-mix(in srgb,var(--color-accent) 10%,transparent);opacity:.75}
.dg-route:hover{opacity:1;background:color-mix(in srgb,var(--color-accent) 18%,transparent)}
.dg-route .ph{font-size:12px}
.visit-panel-acts a.btn-secondary,.visit-acts a.btn-secondary{text-decoration:none}

/* The key under the diary. */
.diary-key{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:11.5px;color:var(--color-muted)}
.diary-key span{display:inline-flex;align-items:center;gap:6px}
.diary-key i{width:14px;height:14px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;font-style:normal;font-size:9px;font-weight:700}
.diary-key .k-pending i{background:var(--color-warn)}
.diary-key .k-confirmed i{background:var(--color-ok)}
.diary-key .k-overdue i{background:var(--color-danger)}
.diary-key .k-done i{background:color-mix(in srgb,var(--color-ok) 55%,transparent)}
.diary-key .k-tent i{background:transparent;border:1.5px dashed var(--color-accent)}

/* The Home card of visits to confirm. */
.visits{display:flex;flex-direction:column;gap:10px}
.visit-row{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-warn) 8%,var(--color-surface));
  box-shadow:inset 3px 0 0 var(--color-warn)}
.visit-row.has-swap{box-shadow:inset 3px 0 0 var(--color-accent)}
.visit-row.unbooked{background:color-mix(in srgb,var(--color-danger) 8%,var(--color-surface));box-shadow:inset 3px 0 0 var(--color-danger)}
.visit-row.overdue{background:color-mix(in srgb,var(--color-danger) 10%,var(--color-surface));box-shadow:inset 3px 0 0 var(--color-danger)}
.visit-row.overdue .visit-when .ph{color:var(--color-danger)}
/* A bubble that says a sentence: wraps, and its icon stays on the first line. */
/* A tag that runs to several lines is a box, not a pill: the corners stay
   the same small radius however tall it grows. Held with !important because
   a theme pack that rounds every tag to a pill (999px) would turn a
   three-line one into a blob. */
.tag.tag-wrap{display:flex;white-space:normal;align-items:flex-start;line-height:1.45;padding:7px 11px;text-wrap:pretty;
  text-align:left;border-radius:10px!important}
.tag.tag-wrap .ph{margin-top:2px}
.visit-panel-acts{margin-top:10px;flex-wrap:wrap;gap:6px}
.visit-panel-acts .btn-primary,.visit-panel-acts .btn-secondary{padding:6px 10px;font-size:12.5px}
.visit-row.unbooked .visit-when .ph{color:var(--color-danger)}
.visit-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;font-size:13.5px}
.visit-when{font-family:var(--font-heading);font-weight:var(--font-heading-weight);font-size:14px}
.visit-when .ph,.visit-staff .ph,.visit-swap .ph{color:var(--color-accent);font-size:14px;vertical-align:-2px;margin-right:2px}
.visit-swap{color:var(--color-text);font-size:12.5px;text-wrap:pretty;margin-top:2px}
.visit-acts{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end;flex:0 0 auto}
.visit-acts .btn-primary,.visit-acts .btn-secondary{padding:6px 10px;font-size:12.5px}
@media (max-width:640px){
  .visit-row{flex-direction:column}
  .visit-acts{justify-content:flex-start;width:100%}
}

/* =====================================================================
   The client portal — what only it has

   Everything above this line is the CRM's stylesheet, copied whole.
   It is already self-contained and every colour in it is a token, so the
   portal gets the same components, the same themes and the same fixes
   by carrying the same file. This block is the difference: the signed-out
   card, the composer, and a flash banner for a browser with no script.
   Nothing here overrides anything above; it only adds.
   ================================================================== */

/* ---- signed out: one card, centred, nothing else on the page ------- */
.auth-wrap{min-height:100vh;display:grid;place-items:center;padding:24px 16px;
  background:var(--color-bg)}
.auth-card{width:min(420px,100%);padding:26px 24px 24px;border-radius:var(--radius-lg);
  background:var(--color-surface);box-shadow:var(--shadow-md);
  display:flex;flex-direction:column;gap:14px}
.auth-brand{display:flex;align-items:center;gap:9px;font-family:var(--font-heading);
  font-weight:var(--font-heading-weight);font-size:15px;color:var(--color-accent)}
.auth-brand .ph{font-size:18px}
.auth-logo{width:160px;height:auto;display:block;margin-bottom:-2px}
.auth-h{margin:0;font-family:var(--font-heading);font-weight:var(--font-heading-weight);
  font-size:20px;text-wrap:balance}
.auth-s{margin:0;font-size:13.5px;color:var(--color-muted);text-wrap:pretty}
.auth-form{display:flex;flex-direction:column;gap:12px}
.auth-form label{display:flex;flex-direction:column;gap:5px}
.auth-form label > span{font-size:12px;color:color-mix(in srgb,var(--color-text) 70%,transparent)}
.auth-form .btn-primary{justify-content:center;margin-top:4px}
.auth-alt{margin:0;font-size:13px}
.auth-alt a{color:var(--color-accent)}
.auth-ok,.auth-err{padding:10px 12px;border-radius:var(--radius-md);font-size:13px;
  text-wrap:pretty}
.auth-ok{background:color-mix(in srgb,var(--color-ok) 14%,transparent);color:var(--color-ok)}
.auth-err{background:color-mix(in srgb,var(--color-warn) 14%,transparent);color:var(--color-warn)}

/* ---- a flash, for the page that arrives after a redirect ------------
   With script it becomes a toast on load; without, this is what shows. */
.flash{display:flex;align-items:flex-start;gap:8px;margin:0 0 12px;padding:10px 12px;
  border-radius:var(--radius-md);font-size:13px;text-wrap:pretty;
  background:color-mix(in srgb,var(--color-ok) 14%,transparent);color:var(--color-ok)}
.flash.error{background:color-mix(in srgb,var(--color-warn) 14%,transparent);
  color:var(--color-warn)}
.flash .ph{font-size:15px;margin-top:1px}
body.has-script .flash{display:none}

/* ---- the composer: one box and one button, under the thread --------- */
.composer{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.composer textarea{min-height:72px}
.composer-acts{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.composer-note{flex:1;min-width:0;font-size:11.5px;color:var(--color-muted);text-wrap:pretty}

/* ---- a task on a job, read only: done or not ------------------------- */
.plist{display:flex;flex-direction:column}
.pt{display:flex;align-items:center;gap:10px;padding:8px 0;font-size:13.5px;
  border-bottom:1px solid var(--color-divider)}
.pt:last-child{border-bottom:0}
.pt .ph{font-size:16px;color:var(--color-muted);flex:0 0 auto}
.pt.done{color:var(--color-muted)}
.pt.done .ph{color:var(--color-ok)}
.pt-bar{height:6px;border-radius:999px;background:var(--color-bg);overflow:hidden;
  margin:4px 0 10px}
.pt-bar > span{display:block;height:100%;background:var(--color-accent)}

/* ---- a quote, and what can be done with it --------------------------- */
.quote{display:flex;flex-direction:column;gap:8px;padding:12px 0;
  border-bottom:1px solid var(--color-divider)}
.quote:last-child{border-bottom:0;padding-bottom:0}
.quote-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.quote-ref{font-family:var(--font-mono);font-size:12px;color:var(--color-muted)}
.quote-t{flex:1;min-width:0;font-size:14px}
.quote-total{font-size:16px;font-weight:500;font-variant-numeric:tabular-nums}
.quote-acts{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.quote-why{font-size:12.5px;color:var(--color-muted);text-wrap:pretty}
/* The no-script route to turning one down, kept honest and visible. */
details.q-alt > summary{cursor:pointer;font-size:12.5px;color:var(--color-accent)}
details.q-alt form{display:flex;flex-direction:column;gap:8px;margin-top:8px}

/* ---- the drop zone's no-script twin ---------------------------------- */
.up-plain{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
body.has-script .up-plain{display:none}

/* ---- the signed-out body is not the two-column grid ------------------ */
body.auth-body{display:block}

/* ---- a second line under the person in the rail foot ----------------- */
.rail-sub{display:block;font-size:11px;color:var(--color-muted);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}

/* ---- documents as rows: what, where, when, and a way back ----------- */
.docs{display:flex;flex-direction:column}
.doc-row{display:flex;align-items:center;gap:10px;padding:8px 4px;
  border-bottom:1px solid var(--color-divider)}
.doc-row:last-child{border-bottom:0}
.doc-row > .ph{flex:0 0 auto;font-size:20px;color:var(--color-muted)}
.doc-row.new > .ph{color:var(--color-accent)}
.doc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;color:inherit;
  text-decoration:none}
.doc-main:hover .doc-n{color:var(--color-accent)}
.doc-n{font-size:13.5px;overflow-wrap:anywhere}
.doc-s{font-size:11.5px;color:var(--color-muted)}
.doc-acts{display:flex;gap:2px;flex:0 0 auto}
.doc-acts form{display:contents}

/* ---- the line above a gallery ---------------------------------------- */
.gal-tools{display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-bottom:10px;font-size:12.5px}
.gal-size{display:flex;align-items:center;gap:6px;color:var(--color-muted)}
.gal-size select{width:auto;padding:3px 26px 3px 8px;font-size:12.5px}
a.shot{color:inherit;text-decoration:none}
/* ---- the tag on a photograph tile sits over the picture: solid ------- */
.shot-tags .tag{background:var(--color-accent);color:#fff;border:0;
  box-shadow:0 1px 3px rgba(0,0,0,.45)}
/* ---- links wearing the CRM's button/row classes ---------------------- */
a.row,a.chip,a.rail-k,a.rail-a,a.rec-back{text-decoration:none}
a.row{color:inherit}

/* ---- a job pane inside the split: fills it, and only the panes scroll -- */
[data-job-pane]{display:flex;flex-direction:column;flex:1;min-height:0;padding:12px}
[data-job-pane] .rec-card{margin-bottom:12px}
.split.showing .split-detail > .empty{display:none}
.rec-acts{display:flex;gap:8px;align-items:center;margin-left:auto}
.fact-v.warn{color:var(--color-warn)}

/* ---- a step's own line: done when, started, waiting, expected --------- */
.pt-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.pt-m{font-size:11px;color:var(--color-muted)}
.pt.going .ph{color:var(--color-accent)}
.pt.waiting{color:color-mix(in srgb,var(--color-text) 62%,transparent)}
.pt-note{margin-top:10px;font-size:12px}

/* ---- opening a job takes a moment ------------------------------------ */
.empty .spin{display:inline-block;width:12px;height:12px;border-radius:50%;
  border:2px solid var(--color-divider);border-top-color:var(--color-accent);
  vertical-align:-2px;animation:pspin .8s linear infinite}
@keyframes pspin{to{transform:rotate(360deg)}}

/* ---- without script the split is two columns, which a phone cannot show */
@media (max-width:760px){
  body:not(.has-script) .split{grid-template-columns:minmax(0,1fr)}
}
/* ---- tabs at the top of a page, not inside a record: compact --------- */
/* Same air under the rail as a job's tabs have above its panes (12px of
   margin plus the pane body's 2px), and the same size of tab. Applies to
   every tab bar that sits directly in the view, so Quotes and Invoices
   match without being told. */
.view > .tabs,.page-tabs{padding:0;margin-bottom:14px}
.view > .tabs .tab,.page-tabs .tab{padding:7px 10px;font-size:13px}
.view > .tabs .tab .ph,.page-tabs .tab .ph{font-size:14px}
.pane-acts{display:flex;gap:8px;flex-wrap:wrap}
/* The password form: a modal with script, a plain block without. */
body:not(.has-script) .pw-form{display:block!important;margin-top:14px}
body:not(.has-script) .pw-form .modal-h .icon-btn,
body:not(.has-script) .pw-form [data-x]{display:none}
input[readonly]{opacity:.75;cursor:default}

/* ---- the bell's rows and the search's rows are links here ------------ */
a.pop-i,a.news-i{text-decoration:none;color:inherit}
.news-b{display:flex;flex-direction:column;gap:1px;min-width:0}
.news-b > span:first-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.news-s{font-size:11px;color:var(--color-muted)}
.pop .loading{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--color-muted)}
@media (max-width:640px){.topbar-search{flex-basis:160px}}

/* ---- panels stacked in a page-level pane keep the CRM's gap ---------- */
.view [data-pane] > .panel + .panel,.view > .panel + .panel{margin-top:12px}

/* ---- appearance: the density and menu choices under the theme tiles -- */
.pref-row{display:flex;gap:8px;flex-wrap:wrap}

.ph-address-book:before{content:"\e6f8"}
.ph-airplane:before{content:"\e002"}
.ph-archive:before{content:"\e00c"}
.ph-arrow-bend-up-left:before{content:"\e024"}
.ph-arrow-bend-up-right:before{content:"\e026"}
.ph-arrow-clockwise:before{content:"\e036"}
.ph-arrow-counter-clockwise:before{content:"\e038"}
.ph-arrow-down:before{content:"\e03e"}
.ph-arrow-elbow-down-right:before{content:"\e046"}
.ph-arrow-right:before{content:"\e06c"}
.ph-arrow-square-out:before{content:"\e5de"}
.ph-arrow-u-up-left:before{content:"\e08a"}
.ph-arrow-up:before{content:"\e08e"}
.ph-arrows-clockwise:before{content:"\e094"}
.ph-arrows-down-up:before{content:"\e098"}
.ph-arrows-in:before{content:"\e09a"}
.ph-arrows-left-right:before{content:"\e0a0"}
.ph-arrows-out:before{content:"\e0a2"}
.ph-arrows-out-line-vertical:before{content:"\e536"}
.ph-bank:before{content:"\e0b4"}
.ph-bell:before{content:"\e0ce"}
.ph-bell-ringing:before{content:"\e5e8"}
.ph-bicycle:before{content:"\e0d6"}
.ph-boat:before{content:"\e786"}
.ph-brackets-curly:before{content:"\e860"}
.ph-broom:before{content:"\ec54"}
.ph-buildings:before{content:"\e102"}
.ph-bus:before{content:"\e106"}
.ph-calculator:before{content:"\e538"}
.ph-calendar-blank:before{content:"\e10a"}
.ph-calendar-check:before{content:"\e712"}
.ph-calendar-dots:before{content:"\e7b4"}
.ph-calendar-plus:before{content:"\e714"}
.ph-calendar-x:before{content:"\e10c"}
.ph-car:before{content:"\e112"}
.ph-car-profile:before{content:"\e8cc"}
.ph-caret-down:before{content:"\e136"}
.ph-caret-left:before{content:"\e138"}
.ph-caret-right:before{content:"\e13a"}
.ph-caret-up:before{content:"\e13c"}
.ph-chart-bar:before{content:"\e150"}
.ph-chart-line:before{content:"\e154"}
.ph-chat-teardrop-text:before{content:"\e178"}
.ph-chats:before{content:"\e17c"}
.ph-check:before{content:"\e182"}
.ph-check-circle:before{content:"\e184"}
.ph-check-square-offset:before{content:"\e188"}
.ph-checks:before{content:"\e53a"}
.ph-circle:before{content:"\e18a"}
.ph-circle-dashed:before{content:"\e602"}
.ph-clock:before{content:"\e19a"}
.ph-code:before{content:"\e1bc"}
.ph-coins:before{content:"\e78e"}
.ph-copy:before{content:"\e1ca"}
.ph-currency-gbp:before{content:"\e556"}
.ph-door:before{content:"\e61c"}
.ph-dots-six-vertical:before{content:"\eae2"}
.ph-dots-three:before{content:"\e1fe"}
.ph-download-simple:before{content:"\e20c"}
.ph-envelope:before{content:"\e214"}
.ph-envelope-open:before{content:"\e216"}
.ph-envelope-simple:before{content:"\e218"}
.ph-eraser:before{content:"\e21e"}
.ph-eye:before{content:"\e220"}
.ph-eye-slash:before{content:"\e224"}
.ph-fast-forward:before{content:"\e6a6"}
.ph-file:before{content:"\e230"}
.ph-file-doc:before{content:"\eb1e"}
.ph-file-pdf:before{content:"\e702"}
.ph-file-text:before{content:"\e23a"}
.ph-file-xls:before{content:"\eb22"}
.ph-files:before{content:"\e710"}
.ph-flag:before{content:"\e244"}
.ph-folder:before{content:"\e24a"}
.ph-funnel:before{content:"\e266"}
.ph-gear:before{content:"\e270"}
.ph-git-branch:before{content:"\e278"}
.ph-globe-hemisphere-west:before{content:"\e28c"}
.ph-hammer:before{content:"\e80e"}
.ph-hard-drives:before{content:"\e2a0"}
.ph-hash:before{content:"\e2a2"}
.ph-horse:before{content:"\e2b0"}
.ph-house:before{content:"\e2c2"}
.ph-identification-badge:before{content:"\e6f6"}
.ph-identification-card:before{content:"\e2c8"}
.ph-image:before{content:"\e2ca"}
.ph-image-square:before{content:"\e2cc"}
.ph-images:before{content:"\e836"}
.ph-info:before{content:"\e2ce"}
.ph-jeep:before{content:"\e2d4"}
.ph-key:before{content:"\e2d6"}
.ph-lightning:before{content:"\e2de"}
.ph-link:before{content:"\e2e2"}
.ph-link-break:before{content:"\e2e4"}
.ph-link-simple:before{content:"\e2e6"}
.ph-list:before{content:"\e2f0"}
.ph-list-bullets:before{content:"\e2f2"}
.ph-list-checks:before{content:"\eadc"}
.ph-list-numbers:before{content:"\e2f6"}
.ph-list-plus:before{content:"\e2f8"}
.ph-lock-simple:before{content:"\e308"}
.ph-magnifying-glass:before{content:"\e30c"}
.ph-map-pin:before{content:"\e316"}
.ph-microphone:before{content:"\e326"}
.ph-minus:before{content:"\e32a"}
.ph-money:before{content:"\e588"}
.ph-monitor:before{content:"\e32e"}
.ph-moped:before{content:"\e824"}
.ph-motorcycle:before{content:"\e80a"}
.ph-navigation-arrow:before{content:"\eade"}
.ph-note-pencil:before{content:"\e34c"}
.ph-package:before{content:"\e390"}
.ph-palette:before{content:"\e6c8"}
.ph-paper-plane-right:before{content:"\e396"}
.ph-paper-plane-tilt:before{content:"\e398"}
.ph-paperclip:before{content:"\e39a"}
.ph-paragraph:before{content:"\e960"}
.ph-path:before{content:"\e39c"}
.ph-pause:before{content:"\e39e"}
.ph-pencil-simple:before{content:"\e3b4"}
.ph-person-simple-bike:before{content:"\e734"}
.ph-person-simple-walk:before{content:"\e73a"}
.ph-phone:before{content:"\e3b8"}
.ph-phone-incoming:before{content:"\e3be"}
.ph-play:before{content:"\e3d0"}
.ph-plus:before{content:"\e3d4"}
.ph-plus-circle:before{content:"\e3d6"}
.ph-prohibit:before{content:"\e3de"}
.ph-push-pin:before{content:"\e3e2"}
.ph-quotes:before{content:"\e660"}
.ph-receipt:before{content:"\e3ec"}
.ph-rewind:before{content:"\e6a8"}
.ph-rocket:before{content:"\e3fc"}
.ph-sailboat:before{content:"\e78a"}
.ph-scooter:before{content:"\e820"}
.ph-scroll:before{content:"\eb7a"}
.ph-seal:before{content:"\e604"}
.ph-seal-check:before{content:"\e606"}
.ph-shield-check:before{content:"\e40c"}
.ph-shopping-cart:before{content:"\e41e"}
.ph-sidebar-simple:before{content:"\ec24"}
.ph-sign-in:before{content:"\e428"}
.ph-sign-out:before{content:"\e42a"}
.ph-skip-back:before{content:"\e5a4"}
.ph-sliders:before{content:"\e432"}
.ph-speaker-high:before{content:"\e44a"}
.ph-speaker-slash:before{content:"\e45a"}
.ph-square:before{content:"\e45e"}
.ph-squares-four:before{content:"\e464"}
.ph-stack:before{content:"\e466"}
.ph-star:before{content:"\e46a"}
.ph-table:before{content:"\e476"}
.ph-taxi:before{content:"\e902"}
.ph-text-aa:before{content:"\e6ee"}
.ph-text-align-center:before{content:"\e480"}
.ph-text-align-left:before{content:"\e484"}
.ph-text-align-right:before{content:"\e486"}
.ph-text-b:before{content:"\e5be"}
.ph-text-h-three:before{content:"\e6c0"}
.ph-text-h-two:before{content:"\e6be"}
.ph-text-italic:before{content:"\e5c0"}
.ph-text-strikethrough:before{content:"\e5c2"}
.ph-text-underline:before{content:"\e5c4"}
.ph-toolbox:before{content:"\eca0"}
.ph-tractor:before{content:"\ec6e"}
.ph-train:before{content:"\e496"}
.ph-trash:before{content:"\e4a6"}
.ph-tree-structure:before{content:"\e67c"}
.ph-truck:before{content:"\e4b4"}
.ph-truck-trailer:before{content:"\e4b6"}
.ph-upload-simple:before{content:"\e4c0"}
.ph-user:before{content:"\e4c2"}
.ph-user-circle:before{content:"\e4c4"}
.ph-user-focus:before{content:"\e6fc"}
.ph-user-gear:before{content:"\e4cc"}
.ph-user-minus:before{content:"\e4ce"}
.ph-user-plus:before{content:"\e4d0"}
.ph-user-switch:before{content:"\e756"}
.ph-users-three:before{content:"\e68e"}
.ph-van:before{content:"\e826"}
.ph-warning:before{content:"\e4e0"}
.ph-warning-circle:before{content:"\e4e2"}
.ph-whatsapp-logo:before{content:"\e5d0"}
.ph-wrench:before{content:"\e5d4"}
.ph-x:before{content:"\e4f6"}
.ph-x-circle:before{content:"\e4f8"}
