/* Campaign desk. The Build Portal's design system, which follows the
   buildteam.com website (Oliver, 7 Oct 2026).

   Petrol blue #00517D carries the brand: the top bar, links, outlines and
   filled secondary buttons. The ONE next action on a screen is a black pill
   (.btn-action). There is no orange. A filled colour on a small pill means
   status and nothing else: green done, amber needs a look, red a problem,
   blue under way, grey to come. Poppins in three weights: 400 body, 500
   labels and buttons, 600 headings and figures. Buttons, inputs and chips
   are pills; inputs are grey with no border, so they always sit on white.

   The desk has two campaigns, each an area with a colour of its own
   (app/areas.py): build-only in the petrol blue, pre-design in aubergine.
   Everything coloured by the brand takes it from the tokens below, which
   html.area-predesign redefines, so nothing else needs to know which area
   it is in. */
:root {
  --brand: #00517d;
  --brand-rgb: 0, 81, 125;
  --brand-dark: #003b5c;
  --tint: #e0eef5;
  --highlight: #eaf1f5;
  --primary: #000000;
  --navy: #0a142f;
  --ink: #1a1a1a;
  --muted: #616161;
  --soft: #6e6e6e;
  --surface: #f5f5f5;
  --field: #f0f0f0;
  --line: #e5e5e5;
  --card: #ffffff;
  --success: #17633f;
  --success-soft: #e6f4ec;
  --amber: #9a4e00;
  --amber-soft: #fdf3e4;
  --amber-line: #f3ddbd;
  --danger: #b3261e;
  --danger-soft: #fbeaea;
  --neutral: #4a4a4a;
  --neutral-soft: #ececec;
  --shadow-card: 0 4px 24px rgba(var(--brand-rgb), .07);
  --shadow-panel: 0 8px 28px rgba(var(--brand-rgb), .22);
  --shadow-bar: 0 2px 10px rgba(var(--brand-rgb), .25);
  --bar-height: 68px;
}
/* Pre-design: the same system in aubergine. Clear of the status colours
   (green done, amber needs a look, red a problem), so a filled pill still
   means only status. */
:root.area-predesign {
  --brand: #4e2a84;
  --brand-rgb: 78, 42, 132;
  --brand-dark: #3a1f63;
  --tint: #ece6f5;
  --highlight: #f2eef8;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  /* At least the window's height, so a short page's glow fades out before the page ends. */
  min-height: 100vh;
  /* Pale grey ground with a soft blue glow under the top bar. */
  background:
    radial-gradient(90% 460px at 50% 120px, rgba(var(--brand-rgb), .1), rgba(var(--brand-rgb), 0)) no-repeat,
    var(--surface);
  color: var(--ink);
  font: 15px/1.5 Poppins, "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}
h1 { font-size: 30px; line-height: 1.2; font-weight: 600; color: var(--navy); margin: 0; }
h2 { font-size: 18px; line-height: 1.35; font-weight: 600; color: var(--navy); margin: 0 0 10px; }
h3 { font-size: 15px; font-weight: 600; color: var(--navy); margin: 0 0 6px; }
p { margin: 0 0 12px; }
a { color: var(--brand); }
b, strong { font-weight: 600; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.num { font-variant-numeric: tabular-nums; font-weight: 500; color: var(--muted); }
.nowrap { white-space: nowrap; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
button:not(:disabled), summary, label:has(> input[type="checkbox"]) { cursor: pointer; }

/* ---- Top bar ---- */
.top { position: sticky; top: 0; z-index: 40; background: var(--brand); color: #fff; }
/* The bar's shadow sits on a layer behind it rather than on the bar itself.
   A shadow on the sticky bar makes the browser draw everything in the bar
   with plain grey smoothing instead of ClearType, and the text goes soft. */
.top::after { content: ""; position: absolute; inset: 0; z-index: -1; box-shadow: var(--shadow-bar); pointer-events: none; }
/* The desk is used on a PC monitor, so the page runs nearly the full width of
   one; the cap only stops lines growing too long on a very large screen. */
.top-inner, .page { max-width: 1880px; margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.top-inner { display: flex; align-items: center; gap: 20px; padding-top: 12px; padding-bottom: 12px; }
.brand { flex: none; display: block; }
.brand img { display: block; height: 28px; width: auto; }
/* Which campaign: the two areas side by side, the one open in white. Going
   to the other rolls the desk over (static/roll.js). */
.areas { flex: none; display: flex; gap: 2px; padding: 3px; border-radius: 999px; background: rgba(0, 0, 0, .2); }
.areas a {
  display: inline-flex; align-items: center; border-radius: 999px; padding: 5px 14px;
  font-size: 13px; line-height: 20px; font-weight: 600; color: rgba(255, 255, 255, .78); text-decoration: none; white-space: nowrap;
  transition: background-color .2s, color .2s;
}
.areas a:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.areas a[aria-current] { background: #fff; color: var(--brand); }
.areas a:focus-visible { outline-color: #fff; }
.role { flex: none; border-radius: 4px; background: rgba(255, 255, 255, .15); padding: 3px 10px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .025em; white-space: nowrap; }
/* Only the tabs scroll on a narrow window; the links on the right stay put. */
.tabs-wrap { flex: 1; min-width: 0; }
.tabs { display: flex; align-items: center; gap: 4px; width: fit-content; max-width: 100%; overflow-x: auto; scrollbar-width: none; border-radius: 999px; background: rgba(255, 255, 255, .1); padding: 4px; }
.tabs a {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; border-radius: 999px; padding: 7px 16px;
  font-size: 15px; line-height: 22px; font-weight: 500; color: #fff; text-decoration: none; transition: background-color .2s, color .2s;
}
.tabs a:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.tabs a[aria-current] { background: #fff; color: var(--navy); }
.tabs a:focus-visible, .top-links a:focus-visible, .account summary:focus-visible, .brand:focus-visible { outline-color: #fff; }
/* How many are waiting: white on the bar, blue on the white pill. */
.count { min-width: 20px; border-radius: 999px; background: #fff; color: var(--navy); padding: 0 6px; font-size: 12px; line-height: 20px; font-weight: 600; text-align: center; font-variant-numeric: tabular-nums; }
.tabs .count { min-width: 20px; font-size: 12px; line-height: 20px; }
.tabs a[aria-current] .count { background: var(--brand); color: #fff; }
/* Beside To post's count of what can go: what is held back, in the amber of something waiting. */
.tabs .count-held, .tabs a[aria-current] .count-held { margin-left: 4px; background: var(--amber-soft); color: var(--amber); }
.count-held::after { content: " held"; }
.top-links { flex: none; display: flex; align-items: center; gap: 20px; }
.top-links a { font-size: 14px; font-weight: 500; color: rgba(255, 255, 255, .8); text-decoration: none; white-space: nowrap; }
.top-links a:hover, .top-links a[aria-current] { color: #fff; }
.top-links a[aria-current] { text-decoration: underline; text-underline-offset: 4px; }
/* Whoever is signed in, at the far right: their picture, or their initials
   in a white circle, with a small menu under it. */
.avatar { flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: #fff; color: var(--brand); font-size: 14px; font-weight: 600; letter-spacing: .02em; }
.account { flex: none; position: relative; }
.account summary { display: block; list-style: none; border-radius: 50%; padding: 2px; transition: box-shadow .15s; }
.account summary::-webkit-details-marker { display: none; }
.account summary:hover, .account[open] summary { box-shadow: 0 0 0 2px rgba(255, 255, 255, .6); }
.account-menu { position: absolute; top: calc(100% + 10px); right: 0; width: max-content; min-width: 220px; max-width: min(360px, calc(100vw - 32px)); padding: 8px; background: var(--card); border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow-panel); color: var(--ink); }
.account-who { margin: 0 0 6px; padding: 8px 12px 12px; border-bottom: 1px solid var(--line); font-size: 14px; overflow-wrap: anywhere; }
.account-menu a, .account-menu button { display: flex; align-items: center; width: 100%; min-height: 40px; padding: 0 12px; border: 0; border-radius: 10px; background: none; font: inherit; font-size: 14px; font-weight: 500; color: var(--navy); text-decoration: none; text-align: left; }
.account-menu a:hover, .account-menu button:hover, .account-menu a[aria-current] { background: var(--highlight); color: var(--brand); }

/* ---- Rolling over between the two campaigns ----
   Going from one area to the other, nothing moves across the screen: the
   top bar, then each block in view below it, rolls over where it stands,
   one after another from the top, the old one away and the new one in.
   static/roll.js picks the pieces and numbers them (--roll-i) and decides
   when; this is how. Every navigation may animate (the at-rule below), and
   roll.js calls off any that stays within one area. */
:root { --roll-half: .17s; --roll-step: 26ms; }
@view-transition { navigation: auto; }
@keyframes roll-away-forward { to { transform: perspective(1200px) rotateX(90deg); } }
@keyframes roll-in-forward { from { transform: perspective(1200px) rotateX(-90deg); } }
@keyframes roll-away-back { to { transform: perspective(1200px) rotateX(-90deg); } }
@keyframes roll-in-back { from { transform: perspective(1200px) rotateX(90deg); } }
@keyframes roll-fade-out { to { opacity: 0; } }
@keyframes roll-fade-in { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  /* Chrome and Edge: each piece rolls as a picture, the old one edge-on
     before the new one starts. Past the fourteenth they roll together. */
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-old(*),
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-new(*) {
    mix-blend-mode: normal; backface-visibility: hidden; animation-duration: var(--roll-half); animation-fill-mode: both;
  }
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-old(*) {
    animation-timing-function: cubic-bezier(.55, 0, .8, .25);
    animation-delay: calc(min(var(--roll-i, 0), 14) * var(--roll-step));
  }
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-new(*) {
    animation-timing-function: cubic-bezier(.2, .75, .4, 1);
    animation-delay: calc(min(var(--roll-i, 0), 14) * var(--roll-step) + var(--roll-half));
  }
  html:active-view-transition-type(roll-forward)::view-transition-old(*) { animation-name: roll-away-forward; }
  html:active-view-transition-type(roll-forward)::view-transition-new(*) { animation-name: roll-in-forward; }
  html:active-view-transition-type(roll-back)::view-transition-old(*) { animation-name: roll-away-back; }
  html:active-view-transition-type(roll-back)::view-transition-new(*) { animation-name: roll-in-back; }
  /* The ground and anything not rolled cross-fade. */
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-old(root) { animation: roll-fade-out .3s both; mix-blend-mode: plus-lighter; }
  html:active-view-transition-type(roll-forward, roll-back)::view-transition-new(root) { animation: roll-fade-in .3s both; mix-blend-mode: plus-lighter; }

  /* Other browsers: the pieces themselves roll, away before the link is
     followed and in as the next page opens (hidden until it is read in). */
  html.roll-waiting body { visibility: hidden; }
  html.rolling-away .roll-piece, html.rolling-in .roll-piece {
    backface-visibility: hidden; animation-duration: var(--roll-half); animation-fill-mode: both;
    animation-delay: calc(min(var(--roll-i, 0), 14) * var(--roll-step));
  }
  html.rolling-away .roll-piece { animation-timing-function: cubic-bezier(.55, 0, .8, .25); }
  html.rolling-in .roll-piece { animation-timing-function: cubic-bezier(.2, .75, .4, 1); }
  html.roll-forward.rolling-away .roll-piece { animation-name: roll-away-forward; }
  html.roll-forward.rolling-in .roll-piece { animation-name: roll-in-forward; }
  html.roll-back.rolling-away .roll-piece { animation-name: roll-away-back; }
  html.roll-back.rolling-in .roll-piece { animation-name: roll-in-back; }
}

/* ---- Page furniture ---- */
.page { padding-top: 28px; padding-bottom: 80px; }
.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px 24px; flex-wrap: wrap; margin-bottom: 20px; }
.lede { color: var(--muted); font-size: 14px; max-width: 78ch; }
.head .lede { margin: 4px 0 0; }
.hint { color: var(--muted); font-size: 13px; }
.eyebrow { display: block; font-size: 12px; line-height: 1.4; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
/* What was just done, said once and plainly: green for done, amber for not done, pale for worth knowing. */
.flashes { position: sticky; top: calc(var(--bar-height) + 10px); z-index: 30; display: grid; gap: 8px; margin: 0 0 14px; }
.flash.is-leaving { opacity: 0; transition: opacity .25s; }
.flash { display: flex; align-items: center; gap: 10px; margin: 0; padding: 12px 18px; cursor: pointer; box-shadow: var(--shadow-panel); border: 1px solid #bfdfcc; border-radius: 16px; background: var(--success-soft); font-size: 15px; font-weight: 500; color: var(--success); }
.flash::before { content: "\2713"; flex: none; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--success); color: #fff; font-size: 13px; }
.flash-warn { border-color: var(--amber-line); background: var(--amber-soft); color: var(--amber); }
.flash-warn::before { content: "!"; background: var(--amber); font-weight: 600; }
.flash-note { border-color: var(--line); background: var(--highlight); color: var(--navy); }
.flash-note::before { content: "i"; background: var(--navy); font-weight: 600; }
.card, .panel { background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); }
.panel { padding: 24px; margin-bottom: 20px; }
.empty { background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); padding: 28px 24px; font-size: 14px; color: var(--muted); }
.empty b { display: block; margin-bottom: 2px; font-size: 16px; color: var(--navy); }
.notice { border-radius: 16px; padding: 14px 18px; margin-bottom: 16px; font-size: 14px; }
.notice ul { margin: 6px 0 12px; padding-left: 20px; }
.notice-warn { background: var(--amber-soft); border: 1px solid var(--amber-line); color: var(--ink); }
.notice-warn b { color: var(--amber); }
.notice-danger { background: var(--danger-soft); color: var(--danger); font-weight: 500; }

/* The data line under the Review heading: how fresh the list is. */
.fresh { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-top: 4px; font-size: 14px; color: var(--muted); }
.fresh.is-running #refresh-text::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%;
  background: var(--brand); animation: pulse 1.2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .fresh.is-running #refresh-text::before { animation: none; } }
/* While a check runs: a bar for the step it is on, and that step's count.
   .is-waiting is a step that cannot yet say how much there is to do. */
.fresh-progress { flex-basis: 100%; display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.fresh-bar { flex: none; width: min(320px, 60vw); height: 8px; border-radius: 999px; background: rgba(var(--brand-rgb), .16); overflow: hidden; }
.fresh-bar span { display: block; width: 0; height: 100%; border-radius: 999px; background: var(--brand); transition: width .4s ease; }
.fresh-bar.is-waiting span { width: 30%; animation: slide 1.4s ease-in-out infinite; }
@keyframes slide { from { transform: translateX(-100%); } to { transform: translateX(334%); } }
@media (prefers-reduced-motion: reduce) {
  .fresh-bar span { transition: none; }
  .fresh-bar.is-waiting span { width: 100%; opacity: .35; animation: none; }
}
.fresh-agents { flex-basis: 100%; font-size: 13px; }

/* ---- Controls ---- */
/* Buttons are pills. A plain .btn is a filled petrol one, for the main jobs
   on a row or a form; .btn-quiet is the outline for everything else. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 0 18px;
  font: inherit; font-size: 14px; line-height: 1.25; font-weight: 500; text-decoration: none; white-space: nowrap; cursor: pointer;
  border-radius: 999px; border: 1.5px solid var(--brand); background: var(--brand); color: #fff;
  transition: background-color .15s, color .15s, border-color .15s;
}
.btn:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.btn-quiet { background: #fff; color: var(--brand); }
.btn-quiet:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
/* The one next action on a screen: black, and a size up. */
.btn-action { min-height: 48px; padding: 0 24px; font-size: 16px; background: var(--primary); border-color: var(--primary); }
.btn-action:hover { background: #262626; border-color: #262626; }
.btn-action .count { background: rgba(255, 255, 255, .2); color: #fff; }
.btn[disabled] { opacity: .5; cursor: default; }
.link { font: inherit; font-weight: 500; color: var(--brand); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: none; }
.link:hover { text-decoration: underline; text-underline-offset: 3px; }
.link-stop { color: var(--danger); }
.ext::after { content: " \2197"; font-size: .9em; }
.inline { display: inline; }

/* Inputs are grey pills with no border. */
select, input[type="text"], input[type="search"], input[type="date"], input[type="number"], input[type="tel"], input[type="email"], input[type="password"], input:not([type]), textarea {
  /* Not "inherit": a field inside a label would take the label's weight. */
  font: 400 15px/1.5 Poppins, "Segoe UI", system-ui, sans-serif; letter-spacing: normal; text-transform: none;
  color: var(--ink); background: var(--field);
  height: 48px; padding: 0 18px; border: 0; border-radius: 999px; max-width: 100%;
}
select { padding-right: 12px; }
::placeholder { color: var(--muted); opacity: 1; }
textarea { width: 100%; height: auto; min-height: 96px; padding: 14px 18px; border-radius: 16px; resize: vertical; }
select:focus-visible, input:focus-visible, textarea:focus-visible { outline-offset: 0; }
.check { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; font-size: 14px; }
input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--brand); flex: none; cursor: pointer; }

/* A choice between a few lists under one tab: outline pills, the chosen one filled. */
/* Which list is on show: one toggle, a track with the list in view filled in. */
.views { display: flex; width: fit-content; max-width: 100%; gap: 2px; padding: 4px; margin-bottom: 16px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); box-shadow: var(--shadow-card); }
.views a { display: inline-flex; align-items: center; min-height: 38px; padding: 0 18px; border-radius: 999px; font-size: 15px; font-weight: 500; color: var(--muted); text-decoration: none; white-space: nowrap; transition: background-color .15s, color .15s; }
.views a:hover { background: var(--tint); color: var(--brand); }
.views a[aria-current] { background: var(--brand); color: #fff; }

/* Grey inputs need a white ground, so the filters are a card of their own. */
.filters { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin: 0 0 16px; padding: 16px 20px; background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); }
.filters label { display: flex; flex-direction: column; gap: 6px; font-size: 14px; font-weight: 500; color: var(--ink); }
.filters select { width: 164px; }
.filters select[name="weeks"] { width: 150px; }
/* Wide enough for "Single-storey extension", which is what Review opens on. */
.filters select[name="work_type"] { width: 236px; }
.filters select[name="state"] { width: 270px; }
.filters .grow { flex: 1 1 180px; }
.filters .grow input { width: 100%; }
/* A filter left as the page opens stays grey; one that has been set is ringed in blue. */
.filters .is-set { background: var(--card); box-shadow: inset 0 0 0 2px var(--brand); color: var(--brand); font-weight: 500; }
.filters .btn { min-height: 48px; }
.filters > .link { min-height: 48px; display: inline-flex; align-items: center; font-size: 14px; }
.panel .filters { margin: 0; padding: 0; border: 0; border-radius: 0; box-shadow: none; }
/* Which list and how it is narrowed, side by side when there is room. The
   pills line up with the fields inside the filter card. */
.toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 16px; }
/* On Review the toggle sits beside the heading, and whatever acts on the list keeps the right. */
.head .views { margin: 0 auto 0 12px; }
.toolbar .filters { flex: 1 1 760px; }

/* A filled pill is a status, and nothing else. */
.chip { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; line-height: 1.5; font-weight: 500; background: var(--neutral-soft); color: var(--neutral); text-decoration: none; white-space: nowrap; vertical-align: 1px; }
.chip-warn { background: var(--amber-soft); color: var(--amber); }
/* Under the filters on Review: a line naming the firms Setup leaves out. */
.left-out { margin: -4px 0 16px; padding: 0 6px; font-size: 14px; color: var(--muted); }
/* In Setup: the firms left out, each with a cross to bring it back, and one to add. */
.firms-out { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
.firms-out h3 { margin: 0 0 6px; }
.firm-chips, .firm-add { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.firm-add select { width: min(100%, 340px); }
.chip-firm { display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 12px; font-size: 13px; vertical-align: 0; }
.chip-firm .num { font-weight: 400; }
.chip-firm button { width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%; background: none; font: inherit; font-size: 16px; line-height: 1; color: var(--neutral); cursor: pointer; }
.chip-firm button:hover { background: var(--neutral); color: #fff; }
.chip-responded, .chip-quoted { background: var(--tint); color: var(--brand); }
.chip-won { background: var(--success-soft); color: var(--success); }
.chip-lost, .chip-returned, .chip-stop { background: var(--danger-soft); color: var(--danger); }
/* A job won or lost is solid, as on Leads; the paler green and red are the planning decision. */
.chip-job.chip-won { background: var(--success); color: #fff; font-weight: 600; }
.chip-job.chip-lost { background: var(--danger); color: #fff; font-weight: 600; }
/* The borough, in its own colour, so a list can be read by area.
   app/boroughs.py picks the hue and which of three looks it takes: there
   are more boroughs than colours the eye can tell apart. Squarer than a
   status chip, so the two are not mistaken for each other. */
.borough { display: inline-block; margin-right: 6px; padding: 2px 9px; border-radius: 7px; font-size: 12px; line-height: 1.5; font-weight: 600; white-space: nowrap; vertical-align: 1px; }
.borough-tint { background: hsl(var(--hue) 85% 90%); color: hsl(var(--hue) 80% 23%); }
.borough-solid { background: hsl(var(--hue) 72% 28%); color: #fff; }
.borough-line { background: #fff; color: hsl(var(--hue) 80% 26%); box-shadow: inset 0 0 0 1.5px hsl(var(--hue) 65% 48%); }

/* ---- Lead list ---- */
/* Stays under the top bar while the list scrolls, so the buttons for the
   ticked leads are in reach from anywhere down the page. */
.listbar {
  position: sticky; top: calc(var(--bar-height) + 8px); z-index: 5;
  display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: center; justify-content: space-between;
  min-height: 60px; margin-bottom: 10px; padding: 8px 10px 8px 22px;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px; box-shadow: var(--shadow-card);
  font-size: 14px; color: var(--muted);
}
.listbar b { color: var(--navy); }
.listbar-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.listbar .check { padding-right: 12px; }
/* The order of the list sits beside its count; whatever acts on the ticked rows keeps the right. */
.listbar-sort { margin-right: auto; }
.listbar-sort label { display: inline-flex; align-items: center; gap: 8px; font-weight: 500; color: var(--ink); }
.listbar-sort select { height: 40px; width: 230px; font-size: 14px; }
.listbar-sort .is-set { background: var(--card); box-shadow: inset 0 0 0 2px var(--brand); color: var(--brand); font-weight: 500; }
[data-bulk] .btn { gap: .3em; }
.leads { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.lead {
  display: grid; grid-template-columns: minmax(0, 1fr) 250px 150px; gap: 14px 22px; align-items: start;
  background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); padding: 20px 22px;
  transition: opacity .18s;
}
.lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 1fr) 250px 150px; }
.lead-compact, .lead-followup { grid-template-columns: minmax(0, 1fr) auto; }
.lead-enquiry { grid-template-columns: minmax(0, 1fr) 300px; scroll-margin-top: calc(var(--bar-height) + 16px); }
.lead-enquiry .chip { margin-left: 6px; }
/* On Leads, how far the job has got is the first thing on the card: bigger
   than a chip, and solid once it is settled, with the card's edge in the same
   colour. A job won or lost is then told at a glance down the list, and is
   not taken for the paler planning chip beside the kind of work. */
.lead-stage { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-bottom: 8px; }
.stage { display: inline-flex; align-items: center; gap: 6px; padding: 4px 14px; border-radius: 999px; font-size: 14px; line-height: 1.5; font-weight: 600; background: var(--tint); color: var(--brand); white-space: nowrap; }
.stage svg { flex: none; margin-left: -4px; }
.stage-won { background: var(--success); color: #fff; }
.stage-lost { background: var(--danger); color: #fff; }
.stage-on { font-size: 13px; color: var(--muted); }
.stage-on b { font-weight: 600; color: var(--ink); }
.lead-enquiry.is-won, .lead-enquiry.is-lost { padding-left: 30px; background: linear-gradient(90deg, var(--edge) 0 8px, var(--card) 8px); }
.lead-enquiry.is-won { --edge: var(--success); }
.lead-enquiry.is-lost { --edge: var(--danger); }
.lead-contact input { width: 100%; }
.lead-contact .btn { justify-self: start; }
.lead-followup:has(> .lead-check) { grid-template-columns: auto minmax(0, 1fr) auto; }
.lead.is-leaving { opacity: 0; }
/* The submission last looked up on the council's site (static/app.js), found
   again by its edge on coming back to the desk. */
.lead.is-opened { border-color: var(--brand); background: linear-gradient(90deg, var(--brand) 0 8px, var(--card) 8px); }
.lead.is-drop { outline: 2px dashed var(--brand); outline-offset: -2px; background: var(--highlight); }
.lead-check { margin-top: 3px; }
.lead-addr { font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--navy); }
.lead-addr-missing { color: var(--muted); font-weight: 500; }
.pc { font-weight: 500; color: var(--brand); white-space: nowrap; margin-left: 4px; }
.lead-meta { color: var(--muted); font-size: 13px; margin-top: 2px; }
.lead-agent { color: var(--muted); font-size: 13px; margin-top: 2px; }
.lead-agent b { font-weight: 500; color: var(--ink); }
/* Typing the firm in: a quiet link on the line, opening a box under it. */
.agent-edit { display: inline; margin-left: 6px; }
.agent-edit summary { display: inline; list-style: none; font-weight: 400; cursor: pointer; }
.agent-edit summary::-webkit-details-marker { display: none; }
.agent-edit form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 4px; }
.agent-edit input[type="text"] { flex: 1 1 220px; max-width: 340px; height: 40px; }
.agent-edit .btn { min-height: 40px; padding: 0 16px; }
.agent-edit .hint { flex: 0 0 100%; }
.ref { font-family: ui-monospace, Consolas, monospace; font-size: 12px; overflow-wrap: anywhere; }
.lead-desc { margin: 8px 0 0; max-width: 80ch; font-size: 15px; overflow-wrap: anywhere; }
/* Pre-design Review: where a recent buyer stood in its borough. */
.buyer-place { font-size: 16px; font-weight: 600; color: var(--navy); font-variant-numeric: tabular-nums; }
.lead-reason { margin: 8px 0 0; color: var(--amber); font-size: 14px; font-weight: 500; }
.lead-links { display: flex; flex-wrap: wrap; gap: 4px 20px; margin-top: 10px; font-size: 14px; }

/* What the letter will say: the work, the room and the figure. */
.lead-work { display: grid; gap: 14px; border-left: 1px solid var(--line); padding-left: 22px; align-self: stretch; align-content: start; }
.lead-work:empty { border: 0; }
.lead-work select, .lead-work input[type="text"] { width: 100%; }
.work-fixed { font-weight: 500; font-size: 14px; }
.field { display: grid; gap: 6px; }
.room[hidden] { display: none; }
.room-input { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.room-input input { width: 110px; }
.room .js-measure { justify-self: start; }
/* The two steps to a room size, side by side: fetch the drawings, then measure. */
.room-tools { display: flex; flex-wrap: wrap; gap: 6px; }
.room-tools .btn { min-height: 40px; padding: 0 12px; font-size: 13px; }
/* More than a surname was kept: one click cuts it down. */
.name-fix { justify-self: start; font-size: 13px; text-align: left; }
/* Under Include while the postcard could not yet be written: what it is waiting for. */
.lead-needs { margin: 0; max-width: 16ch; font-size: 12px; line-height: 1.35; font-weight: 500; color: var(--amber); text-align: center; }
/* On To post: what a held-back postcard lacks, filled in where it is listed. */
.held-fix { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.held-fix label { display: grid; gap: 4px; font-size: 13px; font-weight: 500; color: var(--ink); }
.held-fix input[type="text"] { width: 190px; height: 44px; }
.held-fix .room-input input { width: 96px; height: 44px; }
.held-fix .btn { min-height: 44px; }
.room .hint { font-size: 12px; overflow-wrap: anywhere; }
.room .drop-hint { color: var(--brand); font-weight: 500; }
.price { font-size: 13px; color: var(--muted); }
.price-figure { font-size: 20px; font-weight: 600; color: var(--navy); font-variant-numeric: tabular-nums; margin-right: 6px; }
.price-basis { display: block; }
.price-none { display: block; }
.price form { margin-top: 2px; display: block; }

.lead-actions { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.lead-actions .link { text-align: center; font-size: 14px; }
.lead-actions form { display: contents; }

.record { display: flex; flex-wrap: wrap; gap: 8px; }
.record input[type="text"], .record input[type="tel"], .record input[type="email"] { flex: 1 1 160px; }
.record input[type="number"] { width: 150px; }
.history { margin: 8px 0 0; padding-left: 18px; font-size: 13px; color: var(--ink); }
.history .link { margin-left: 8px; }
.history .ev-won { font-weight: 600; color: var(--success); }
.history .ev-lost { font-weight: 600; color: var(--danger); }

.reasons { list-style: none; padding: 0; margin: 0 0 16px; display: flex; flex-wrap: wrap; gap: 8px; }
.reasons li { font-size: 13px; }
.reasons a { display: block; padding: 5px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink); text-decoration: none; }
.reasons a:hover { border-color: var(--brand); color: var(--brand); }
.reasons a[aria-current] { background: var(--brand); border-color: var(--brand); color: #fff; }
.reasons a[aria-current] .num { color: #fff; }
.pager { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; justify-content: center; margin-top: 24px; font-size: 15px; color: var(--muted); }
/* Each page by its number: the one open is filled in. */
.pager-pages { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px; }
.pager-pages a, .pager-pages b, .pager-gap { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; height: 40px; padding: 0 8px; border-radius: 999px; font-weight: 500; font-variant-numeric: tabular-nums; }
.pager-pages a, .pager-step { color: var(--brand); text-decoration: none; }
.pager-pages a:hover { background: var(--tint); }
.pager-pages b { background: var(--brand); color: #fff; }
.pager-step { display: inline-flex; align-items: center; height: 40px; padding: 0 14px; border-radius: 999px; font-weight: 500; white-space: nowrap; }
.pager-step:hover { background: var(--tint); }

/* ---- Tables ---- */
.scroll { overflow-x: auto; margin-bottom: 20px; }
table.plain { border-collapse: collapse; width: 100%; font-size: 14px; }
table.plain th, table.plain td { text-align: left; padding: 14px 20px; border-top: 1px solid var(--line); vertical-align: middle; }
table.plain thead th { border-top: 0; font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
table.plain tbody th { font-weight: 500; color: var(--navy); }
table.sent td { vertical-align: top; }
table.sent td:first-child { width: 44%; }
table.sent td:last-child { width: 300px; }
table.sent td:nth-child(n+2):nth-child(-n+5) { padding-top: 18px; }
/* Under a letter's date: quiet, for the rare one marked as posted by mistake. */
.take-back { margin-top: 4px; font-size: 13px; }
.take-back .link { font-weight: 400; color: var(--muted); }
.take-back .link:hover { color: var(--brand); }
.record-box summary { display: inline-flex; align-items: center; min-height: 40px; padding: 0 16px; border: 1.5px solid var(--brand); border-radius: 999px; background: #fff; font-size: 14px; font-weight: 500; color: var(--brand); white-space: nowrap; list-style: none; transition: background-color .15s, color .15s; }
.record-box summary::-webkit-details-marker { display: none; }
.record-box summary:hover, .record-box[open] summary { background: var(--brand); color: #fff; }
.record-box .record { margin: 12px 0 8px; max-width: 420px; }
.lead-main .record-box { margin-top: 12px; }
.lead-main .record-box .record { max-width: 640px; }
table.prices { width: auto; min-width: min(560px, 100%); }
.prices input { width: 130px; font-variant-numeric: tabular-nums; }

/* ---- To post ---- */
.print-group + .print-group { margin-top: 36px; }
.print-group h3 { margin: 24px 0 10px; }
.steps { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.step { display: flex; gap: 14px; background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); padding: 20px 22px; }
.step-n { flex: none; width: 32px; height: 32px; border-radius: 50%; background: var(--brand); color: #fff; font-size: 14px; font-weight: 600; display: grid; place-items: center; }
.step-body { display: grid; gap: 12px; align-content: start; min-width: 0; }
.step-body h3, .step-body p { margin: 0; }
.step-body h3 { font-size: 16px; line-height: 32px; }
.step-date { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 500; }
.step-actions { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; }
.step-actions .link { font-size: 14px; }
/* Postcards are written by hand off the screen, so what goes on each one is
   set large enough to read from where the card is being written. */
.write-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin-bottom: 12px; }
.write-head h3 { margin: 0; font-size: 16px; }
.write-progress { font-size: 14px; font-weight: 600; color: var(--success); }
.write-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.write-card {
  display: grid; grid-template-columns: auto minmax(0, 1.4fr) minmax(0, .8fr) minmax(0, 1.2fr) auto; gap: 14px 24px; align-items: start;
  background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); padding: 20px 22px;
  transition: opacity .18s;
}
.write-card p { margin: 0; }
.write-tick { display: grid; justify-items: center; gap: 4px; min-width: 44px; padding-top: 2px; }
.write-tick input { width: 26px; height: 26px; }
.write-big { font-size: 24px; line-height: 1.3; font-weight: 600; color: var(--navy); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.write-address { font-style: normal; font-size: 18px; line-height: 1.4; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.write-about { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: 0 18px; padding-top: 10px; border-top: 1px solid var(--line); }
.write-about > * { margin-top: 0; }
/* Written: out of the way, but still readable to check against the card. */
.write-card:has(.js-written:checked) { opacity: .45; box-shadow: none; }
.write-posted { margin-top: 14px; }

/* ---- Sent letters ---- */
.totals { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin: 0 0 16px; }
.totals div { background: var(--card); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow-card); padding: 16px 20px; }
.totals dt { font-size: 12px; line-height: 1.4; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.totals dd { margin: 4px 0 0; font-size: 26px; line-height: 1.2; font-weight: 600; color: var(--navy); font-variant-numeric: tabular-nums; }
.totals small { font-size: 13px; font-weight: 500; color: var(--success); }

/* ---- Setup ---- */
.stack { display: grid; gap: 14px; justify-items: start; }
.stack > .scroll, .stack > .boroughs { justify-self: stretch; margin: 0; }
.panel .hint { margin: 0; }
.more { margin-bottom: 12px; }
.more summary { padding: 6px 0; font-size: 14px; font-weight: 500; color: var(--brand); }
.more p, .more dl { font-size: 13px; color: var(--muted); max-width: 78ch; }
.boroughs { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 0 16px; }
.letter-setup { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; align-items: start; }
.letter-form { display: grid; gap: 16px; }
.letter-form label { display: grid; gap: 6px; font-size: 14px; font-weight: 500; color: var(--ink); }
.letter-form input[type="text"] { width: 100%; }
.letter-form h3 { margin: 12px 0 0; font-size: 16px; }
.letter-form .btn { justify-self: start; }
.more dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 0; }
.more dt { font-family: ui-monospace, Consolas, monospace; font-size: 12px; color: var(--ink); }
.more dd { margin: 0; }
/* The A4 sheet scaled down to fit beside the form, and kept in view while
   the form scrolls past it. */
.letter-preview { position: sticky; top: calc(var(--bar-height) + 12px); }
.letter-frame { background: var(--surface); padding: 12px; border-radius: 16px; overflow: auto; max-height: calc(100vh - 140px); }
.letter-frame .letter { zoom: .58; margin: 0 auto; box-shadow: 0 4px 24px rgba(0, 81, 125, .14); }
/* One sheet for each letter, named above it. */
.letter-frame .eyebrow { margin: 4px 4px 8px; }
.letter-frame .letter + .eyebrow { margin-top: 20px; }

/* Who can sign in: one row a person. Who they are, with Remove apart at the
   right; beneath, what else can be done. The password form unfolds under its
   own button, so opening it moves nothing that was already there. */
.logins { list-style: none; margin: 0; padding: 0; }
.logins li { display: grid; gap: 12px; padding: 16px 0; border-top: 1px solid var(--line); }
.logins li:first-child { border-top: 0; padding-top: 4px; }
.logins li:last-child { padding-bottom: 0; }
.login-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.login-who { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; min-height: 36px; font-weight: 500; color: var(--navy); }
.login-email { margin-right: 4px; overflow-wrap: anywhere; }
.login-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; }
/* A set width, open or shut, so it wraps the same way either way. */
.login-password { flex: 1 1 220px; min-width: 0; }
.login-password summary { width: fit-content; list-style: none; }
.login-password summary::-webkit-details-marker { display: none; }
.login-password summary::after { content: "\25BE"; font-size: 12px; }
.login-password[open] summary { background: var(--brand); border-color: var(--brand); color: #fff; }
.login-password[open] summary::after { content: "\25B4"; }
.login-password form { margin-top: 12px; }
.btn-small { min-height: 36px; padding: 0 14px; font-size: 13px; gap: 6px; }
.btn-remove { border-color: var(--danger); color: var(--danger); }
.btn-remove:hover { background: var(--danger); border-color: var(--danger); color: #fff; }
#backup .btn { margin-bottom: 12px; }

/* ---- Results ---- */
.quiet { font-size: 15px; font-weight: 500; color: var(--muted); }
.results-note { max-width: 110ch; margin: 0 0 16px; }
.results { display: grid; gap: 20px; }
.results-table { padding: 22px 4px 4px; }
.results-table h2 { margin: 0 20px 6px; }
.results-table .scroll { margin: 0; }
/* Pre-design's Results: a warning and a note inside the card, in line with its heading. */
.results-table > .notice, .results-table > .hint { margin: 10px 20px 16px; }
/* Figures line up down the column; the group's name takes the room left over. */
table.figures td { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
table.figures thead th:not(:first-child) { text-align: right; white-space: nowrap; }
table.figures td, table.figures thead th:not(:first-child) { width: 1%; }
table.figures th, table.figures td { padding: 11px 10px; }
table.figures th:first-child { padding-left: 20px; }
table.figures td:last-child, table.figures thead th:last-child { padding-right: 20px; }
table.figures td.rate { font-weight: 600; color: var(--navy); padding-right: 6px; }
/* Too few letters behind it to be read as a rate. */
table.figures td.rate.is-thin { font-weight: 400; color: var(--soft); }
/* One colour, growing from the left: the bar says no more than the figure beside it. */
table.figures td.rate-bar { width: 96px; min-width: 72px; padding-left: 6px; text-align: left; }
.rate-bar span { display: block; height: 8px; min-width: 2px; border-radius: 0 4px 4px 0; background: var(--brand); }

/* ---- Stats ---- */
/* A second line under a figure that explains it, rather than a figure of its own. */
.totals small.note { display: block; margin-top: 2px; font-weight: 400; color: var(--muted); }
.stats-table { margin-bottom: 12px; }

/* ---- An observer's login ---- */
/* Looks at everything and changes nothing but who can sign in (forms marked data-allowed). What would change something is switched off by app.js and still shows what it holds; what is only there to be pressed is taken away. */
.look-only form[method="post"]:not([data-allowed]) button, .look-only form[method="post"]:not([data-allowed]) input[type="file"], .look-only [formmethod="post"], .look-only .record-box, .look-only .write-posted, .look-only .step:has([formmethod="post"]) { display: none; }
.look-only :is(input, select, textarea):disabled { opacity: 1; color: var(--ink); -webkit-text-fill-color: var(--ink); cursor: default; }
/* An empty box is empty: a hint of what to type there would read as what it holds. */
.look-only :disabled::placeholder { color: transparent; -webkit-text-fill-color: transparent; }

/* ---- Signing in ---- */
.page.signin { display: grid; justify-items: center; padding-top: 12vh; }
.signin-card { display: grid; gap: 16px; width: 100%; max-width: 440px; padding: 32px; }
.signin-card .lede, .signin-card .notice { margin: 0; }
.signin-card .lede { margin-top: -12px; }
.signin-card label, .password-form label { display: grid; gap: 6px; font-size: 14px; font-weight: 500; color: var(--ink); }
.signin-card input { width: 100%; }
.signin-card .btn { margin-top: 4px; }
/* As wide as it needs on a desk, and no wider than the card on a phone. */
.password-form { grid-template-columns: minmax(0, 1fr); }
.password-form label { justify-self: stretch; }
.password-form input, .password-form select { width: 100%; max-width: 320px; }
.password-form select { max-width: 380px; }

/* ---- Your settings ---- */
.settings { display: grid; grid-template-columns: repeat(2, minmax(0, 560px)); gap: 20px; align-items: start; }
.settings .panel { margin: 0; }
.picture-form { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 20px 28px; }
/* On a white card, so the circle is tinted where the bar's is white. */
.picture-now .avatar { width: 112px; height: 112px; background: var(--tint); font-size: 36px; }
.picture-form label { display: grid; gap: 6px; font-size: 14px; font-weight: 500; color: var(--ink); }
.picture-form input[type="file"], .filters input[type="file"] { max-width: 100%; font: inherit; font-weight: 400; }
.picture-form input[type="file"]::file-selector-button, .filters input[type="file"]::file-selector-button { min-height: 40px; margin-right: 12px; padding: 0 16px; border: 1.5px solid var(--brand); border-radius: 999px; background: #fff; font: inherit; font-weight: 500; color: var(--brand); cursor: pointer; }
.picture-remove { margin-top: 16px; font-size: 14px; }

/* ---- Measuring a room on a drawing ---- */
.measure {
  /* A plan is a wide sheet: the window takes the whole screen. */
  width: calc(100vw - 32px); height: calc(100vh - 32px); max-width: none; max-height: none;
  padding: 0; border: 0; border-radius: 24px; color: var(--ink); overflow: hidden; box-shadow: var(--shadow-panel);
}
.measure[open] { display: flex; flex-direction: column; }
.measure::backdrop { background: rgba(10, 20, 47, .5); }
.measure-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 22px; border-bottom: 1px solid var(--line); }
.measure-head h2, .measure-head .hint { margin: 0; }
.measure-body { flex: 1; min-height: 0; display: grid; grid-template-columns: 350px minmax(0, 1fr); }
.measure-side { display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--line); }
.measure-steps { flex: 1; min-height: 0; overflow-y: auto; padding: 0 22px 16px; }
.measure-side h3 { margin-top: 20px; font-size: 16px; }
.measure-side .link { text-align: left; font-size: 14px; }
.measure-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 10px; font-size: 14px; }
.measure-row input { width: 104px; height: 44px; }
.measure-row .btn { min-height: 40px; padding: 0 16px; }
.measure-file { margin-bottom: 8px; }
.measure-file:focus-within { outline: 2px solid var(--brand); outline-offset: 2px; }
.measure-file-name { font-size: 14px; font-weight: 500; overflow-wrap: anywhere; }
.measure-note { font-size: 14px; font-weight: 500; margin: 10px 0 0; }
.measure-note:empty { display: none; }
/* Stays in view below the steps, however far they scroll. It is the one
   thing in the window waiting on the reader, so it sits on pale blue. */
.measure-result { display: grid; gap: 8px; justify-items: start; padding: 16px 22px 18px; background: var(--highlight); }
.measure-result p, .measure-result .measure-row { margin: 0; }
.measure-area { font-size: 16px; color: var(--muted); }
.measure-area b { font-size: 32px; font-weight: 600; color: var(--navy); font-variant-numeric: tabular-nums; }
.measure-firms { margin-top: 12px; }
.measure-printed { margin-top: 14px; }
.measure-printed .btn { text-align: left; white-space: normal; }
/* Sent letters: the batches, folded away under the totals. */
.batches { margin: 0 0 16px; }
.batches summary { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 16px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); box-shadow: var(--shadow-card); font-size: 14px; font-weight: 500; color: var(--brand); cursor: pointer; list-style: none; }
.batches summary::-webkit-details-marker { display: none; }
.batches summary::after { content: "\25BE"; font-size: 12px; }
.batches[open] summary { margin-bottom: 10px; }
.batches[open] summary::after { content: "\25B4"; }
.batches .card { max-width: 860px; }
.batches .hint { margin: 0 0 10px; }
.measure-firms .measure-row .measure-row { margin: 0; }
.measure-tips { margin: 0 0 10px; }
.measure-tips summary { cursor: pointer; list-style: none; }
.measure-tips summary::-webkit-details-marker { display: none; }
.measure-tips ul { margin: 6px 0 0; padding-left: 18px; }
.measure-tips li + li { margin-top: 4px; }
.measure-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* The pages of the file, small: the one open is ringed in the brand's blue. */
.measure-pages { flex: none; display: flex; gap: 10px; padding: 10px 14px; overflow-x: auto; background: #6E7A8A; }
.measure-thumb { flex: none; position: relative; min-width: 54px; height: 82px; padding: 0; border: 3px solid transparent; border-radius: 6px; background: #fff; cursor: pointer; overflow: hidden; }
.measure-thumb canvas { display: block; height: 76px; width: auto; }
.measure-thumb span { position: absolute; left: 3px; bottom: 3px; min-width: 20px; padding: 0 5px; border-radius: 999px; background: var(--navy); color: #fff; font-size: 12px; font-weight: 500; line-height: 20px; text-align: center; }
.measure-thumb:hover { border-color: #fff; }
.measure-thumb[aria-current] { border-color: var(--brand); box-shadow: 0 0 0 2px #fff; }
.measure-stage { flex: 1; position: relative; min-height: 0; overflow: hidden; background: #8A96A6; touch-action: none; }
.measure-stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; outline-offset: -4px; }
.measure-stage[data-mode] canvas { cursor: crosshair; }
.measure-stage.is-over { outline: 4px dashed #fff; outline-offset: -12px; }
.measure-empty { position: absolute; inset: 0; margin: 0; display: grid; place-items: center; color: #fff; font-size: 18px; font-weight: 500; pointer-events: none; }
.measure-status { position: absolute; left: 50%; top: 16px; transform: translateX(-50%); margin: 0; padding: 8px 16px; border-radius: 999px; background: var(--card); font-size: 14px; font-weight: 500; box-shadow: var(--shadow-panel); }
/* Bottom left: a drawing's title block, with its scale, is usually bottom right. */
.measure-zoom { position: absolute; left: 14px; bottom: 14px; display: flex; gap: 6px; }
.measure-zoom .btn { min-width: 44px; padding: 0 14px; }
.measure-zoom svg { display: block; }
/* Bottom right, clear of the zoom buttons, and solid so it is found at a glance. */
.measure-undo { position: absolute; right: 14px; bottom: 14px; display: flex; gap: 6px; }
.measure-undo .btn { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 18px; box-shadow: var(--shadow-panel); }

/* Seven tabs with their counts, and the two campaigns beside the logo, need
   about 1,700px at full size. On a narrower window the bar tightens, then
   drops its label, before the tabs scroll. */
@media (max-width: 1720px) {
  .top-inner, .top-links { gap: 14px; }
  .tabs a { padding: 7px 11px; font-size: 14px; }
}
@media (max-width: 1560px) { .role { display: none; } }
@media (max-width: 1400px) { .areas a { padding: 5px 10px; } }
/* Too narrow for the tabs beside everything else: they take a row of their own under it. */
@media (max-width: 1180px) {
  :root { --bar-height: 118px; }
  .top-inner { flex-wrap: wrap; row-gap: 10px; }
  .tabs-wrap { order: 1; flex-basis: 100%; }
  .top-links { margin-left: auto; }
}
@media (max-width: 980px) {
  h1 { font-size: 26px; }
  .measure-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .measure-side { max-height: 46vh; border-right: 0; border-bottom: 1px solid var(--line); }
  .listbar { border-radius: 24px; }
  .lead, .lead:has(> .lead-check) { grid-template-columns: minmax(0, 1fr); }
  .lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 1fr); }
  .lead:has(> .lead-check) > .lead-work, .lead:has(> .lead-check) > .lead-actions { grid-column: 2; }
  .lead-work { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 14px; }
  .lead-work:empty { display: none; }
  .lead-actions { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .lead-actions form { display: block; }
  .steps, .letter-setup, .settings { grid-template-columns: minmax(0, 1fr); }
  .write-card { grid-template-columns: auto minmax(0, 1fr); }
  .write-card > :not(.write-tick) { grid-column: 2; }
  .letter-preview { position: static; }
  .letter-frame { max-height: 900px; }
}
@media (max-width: 640px) {
  .top-inner, .page { padding-left: 16px; padding-right: 16px; }
  .brand { display: none; }
  .tabs a { padding: 8px 12px; }
  .picture-form { grid-template-columns: minmax(0, 1fr); }
  .letter-frame .letter { zoom: .42; }
}

/* Setup: the pictures printed on the second letter */
.letterhead { display: grid; gap: 12px; margin-bottom: 24px; }
.letterhead h3 { margin: 0; font-size: 16px; }
.letterhead .btn { justify-self: start; }
.letterhead-slots { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.letterhead-slots label { display: grid; gap: 8px; align-content: start; justify-items: start; font-size: 14px; font-weight: 500; }
.letterhead-slots img { max-height: 56px; max-width: 100%; }
.letterhead-slots input { max-width: 100%; }
@media (max-width: 900px) { .letterhead-slots { grid-template-columns: minmax(0, 1fr); } }

/* ---- Wide screens ----
   A monitor is far wider than it is tall, so height is what runs out. Where
   there is width to spare, what would stack down the page is laid across it:
   more of the work in view at once, less empty card, and less scrolling. The
   rules above remain the layout for a narrower window.

   Each page is picked out by what is on it (the toolbar on Review, #letter
   on Setup), not by a class on <main>, so these need nothing from the
   templates beyond what they already hold. */
@media (min-width: 1200px) {
  /* Results: two tables to a row. */
  .results { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  /* Less air above the list, so the first rows start higher up the screen. */
  .page { padding-top: 20px; }
  .head { margin-bottom: 14px; }
  .filters { padding: 12px 18px; margin-bottom: 12px; }
  .filters select, .filters input { height: 44px; }
  .filters .btn, .filters > .link { min-height: 44px; }
  .listbar { min-height: 52px; margin-bottom: 8px; padding-top: 4px; padding-bottom: 4px; }

  /* Review: the fields for the letter sit beside the description, two by two. */
  .toolbar ~ .leads { gap: 10px; }
  /* The two decisions and what the row still needs, no taller than the fields beside them. */
  .toolbar ~ .leads .lead-actions { gap: 6px; }
  .toolbar ~ .leads .lead-actions .btn { min-height: 40px; }
  .toolbar ~ .leads .lead { padding: 16px 22px; grid-template-columns: minmax(0, 1fr) minmax(430px, 44%) 150px; }
  .toolbar ~ .leads .lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 1fr) minmax(430px, 44%) 150px; }
  /* The first column is the wider: "Single-storey extension" has to fit in its box. */
  .toolbar ~ .leads .lead-work { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 12px 20px; grid-template-areas: "type name" "room fig"; }
  .toolbar ~ .leads .lead-work > :first-child { grid-area: type; }
  .toolbar ~ .leads .lead-work > .room { grid-area: room; }
  .toolbar ~ .leads .lead-work > div.field { grid-area: fig; }
  .toolbar ~ .leads .lead-work > form:nth-child(4) { grid-area: name; }
  /* A field that shares a row with a taller one keeps its own height. */
  .toolbar ~ .leads .lead-work > * { align-content: start; }
  .toolbar ~ .leads .lead-work select, .toolbar ~ .leads .lead-work input { height: 44px; }
  .toolbar ~ .leads .lead-work select { font-size: 14px; padding-left: 14px; padding-right: 2px; }
  /* The room's size and the button to measure it share a line when they fit. */
  .toolbar ~ .leads .room { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
  .toolbar ~ .leads .room > .eyebrow, .toolbar ~ .leads .room > .hint { flex: 0 0 100%; }
  .toolbar ~ .leads .room-input input { width: 86px; padding: 0 14px; }
  .toolbar ~ .leads .room .js-measure { min-height: 40px; padding: 0 12px; font-size: 13px; }
  /* Where to find the surname is the same advice on every row, so it is
     shown for the row being typed in, not printed forty times down the page. */
  .toolbar ~ .leads .lead-work > form:nth-child(4) { position: relative; }
  .toolbar ~ .leads .lead-work > form:nth-child(4) > .hint {
    display: none; position: absolute; z-index: 6; top: 100%; left: 0; right: 0; margin-top: 6px;
    padding: 10px 14px; border-radius: 12px; background: var(--navy); color: #fff; box-shadow: var(--shadow-panel);
  }
  .toolbar ~ .leads .lead-work > form:nth-child(4):focus-within > .hint { display: block; }
  /* Who submitted it and the links to look it up share a line. */
  .toolbar ~ .leads .lead-agent { display: inline-block; margin-right: 18px; }
  .toolbar ~ .leads .lead-links { display: inline-flex; margin-top: 4px; }
  /* Nothing to fill in (set aside, or no work detected): the description keeps the room. */
  .toolbar ~ .leads .lead:has(.work-fixed), .toolbar ~ .leads .lead:has(> .lead-work:empty) { grid-template-columns: minmax(0, 1fr) 250px 150px; }
}
@media (min-width: 1400px) {
  /* Setup: the price table, and who can sign in under it, beside the letter. */
  .page:has(> #letter), .page:has(> .panel > form[action$="/do-not-mail"]) { display: grid; column-gap: 24px; grid-auto-flow: dense; align-items: start; }
  .page:has(> #letter) { grid-template-columns: minmax(0, 640px) minmax(0, 1fr); }
  .page:has(> #letter) > *, .page:has(> .panel > form[action$="/do-not-mail"]) > * { grid-column: 1 / -1; }
  .page:has(> #letter) > .setup-side { grid-column: 1; }
  .page:has(> #letter) > #letter { grid-column: 2; }
  /* Do not mail: the form to add an address stays beside the list. */
  .page:has(> .panel > form[action$="/do-not-mail"]) { grid-template-columns: 440px minmax(0, 1fr); }
  .page:has(> .panel > form[action$="/do-not-mail"]) > .panel { grid-column: 1; position: sticky; top: calc(var(--bar-height) + 16px); }
  .page:has(> .panel > form[action$="/do-not-mail"]) > .card, .page:has(> .panel > form[action$="/do-not-mail"]) > .empty { grid-column: 2; }
}
@media (min-width: 1500px) {
  /* Room for a whole borough, kind of work or firm's name in each filter. */
  .filters select, .filters select[name="weeks"], .filters select[name="state"] { width: 220px; }
  /* Short rows go two to a line, so the eye and the pointer travel less
     between an address and its buttons. */
  .leads:has(> .lead-followup), .leads:has(> .lead-compact) { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  /* Sent letters: room to record a response without it wrapping. */
  table.sent td:last-child { width: 30%; }
  table.sent .record-box .record { max-width: 560px; }
  .page:has(table.sent) > h2 + .card { max-width: 860px; }
  /* Leads: the client's name, phone and email on one line beside the history. */
  .lead-enquiry { grid-template-columns: minmax(0, 1fr) minmax(0, 760px); }
  .lead-enquiry .lead-contact { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 16px; }
  .lead-enquiry .lead-contact .btn { grid-column: 1; }
}
@media (min-width: 1600px) {
  /* Review: three columns of fields. The work and its figure; the room to
     measure; the name. */
  .toolbar ~ .leads .lead { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) 150px; }
  .toolbar ~ .leads .lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 5fr) minmax(0, 6fr) 150px; }
  .toolbar ~ .leads .lead-work { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.05fr) minmax(0, .9fr); grid-template-rows: auto 1fr; grid-template-areas: "type room name" "fig room name"; }
  .toolbar ~ .leads .lead:has(.work-fixed), .toolbar ~ .leads .lead:has(> .lead-work:empty) { grid-template-columns: minmax(0, 1fr) 250px 150px; }
}
@media (min-width: 1800px) {
  /* Review on a full monitor: one line of fields, read left to right in the
     order the row is worked. What was submitted; the kind of work; the room
     to measure; the figure that gives; the name; then the decision. */
  .toolbar ~ .leads .lead { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr) 150px; }
  .toolbar ~ .leads .lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 4fr) minmax(0, 7fr) 150px; }
  .toolbar ~ .leads .lead-work { grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) minmax(0, .9fr) minmax(0, 1fr); grid-template-rows: auto; grid-template-areas: "type room fig name"; }
  .toolbar ~ .leads .lead:has(.work-fixed), .toolbar ~ .leads .lead:has(> .lead-work:empty) { grid-template-columns: minmax(0, 1fr) 250px 150px; }
}

/* Pre-design Review: a column of facts beside the address, where build-only's
   wide layout above sets out four fields for the letter. */
.toolbar ~ .leads-buyers .lead { grid-template-columns: minmax(0, 1fr) 260px 150px; }
.toolbar ~ .leads-buyers .lead:has(> .lead-check) { grid-template-columns: auto minmax(0, 1fr) 260px 150px; }
.toolbar ~ .leads-buyers .lead-work { grid-template-columns: none; grid-template-areas: none; }
.toolbar ~ .leads-buyers .lead-work > div.field { grid-area: auto; }
