/* ==========================================================================
   The customer's portal.

   np.css carries the palette, the type and the controls; this is only what the
   customer's three screens add on top of it. It is the same layer staff.css is
   for the admin side, and it obeys the same rules: no hex outside np.css, no
   shadows — separation is always an edge — radii of 0, var(--r), 50% or 2px
   only, and the three breakpoints 1200, 980 and 640 and no fourth.

   There is deliberately no sidebar. Staff need one because they move between
   customers, projects, people and the tables; a customer arrives with one
   project and one question, and a column of navigation they will never use is
   230px of furniture standing in front of it.
   ========================================================================== */

body.client {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ---- The bar -------------------------------------------------------------- */
/* Wraps rather than overflows. The mark and whoever is signed in are both allowed to take the width
   they need; on a narrow phone with a long name they take two lines instead of pushing the page sideways. */
.client-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 14px 24px;
  background: var(--np-panel);
  border-bottom: 1px solid var(--np-rule);
}

.client-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  color: var(--np-head);
  text-decoration: none;
}

.client-brand:hover .client-wordmark {
  color: var(--np-accent);
}

/* The mark is drawn twice, once in each ink, because artwork cannot recolour itself and the page can be
   either scheme. The one that does not belong is removed from the layout rather than hidden, so it is
   not read out twice. */
.client-brand .on-dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .client-brand .on-light {
    display: none;
  }

  :root:not([data-theme='light']) .client-brand .on-dark {
    display: block;
  }
}

:root[data-theme='dark'] .client-brand .on-light {
  display: none;
}

:root[data-theme='dark'] .client-brand .on-dark {
  display: block;
}

.client-wordmark {
  display: block;
  font-size: 19px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.client-wordmark small {
  display: block;
  margin-top: 2px;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.client-who {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 0;
  /* A flex item will not shrink below its content unless it is told it may, and "Bartholomew
     Fitzwilliam-Harrington the Third" is content. Without this the bar pushes the whole page sideways. */
  min-width: 0;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* A flex item shrinks before it wraps, and a 34px square that has been squeezed to 21 is no longer a
   thing a thumb can hit. The name is the one thing here that may give ground. */
.client-who > :not(#user-name) {
  flex: none;
}

.client-who #user-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- The nav ----------------------------------------------------------------- */
/* Four words in the bar: Home, Projects (a menu of what they can see), Help, Account. The current
   one is underlined in the accent, the way a tab is, and the menu is a details element so it works
   with nothing running. */
.client-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.client-nav > a,
.client-nav .nav-menu > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 12px;
  border-radius: var(--r);
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-text);
  text-decoration: none;
  cursor: pointer;
  list-style: none;
}

.client-nav .nav-menu > summary::-webkit-details-marker {
  display: none;
}

.client-nav > a:hover,
.client-nav .nav-menu > summary:hover {
  color: var(--np-accent);
  background: var(--np-sunken);
}

.client-nav [aria-current='page'] {
  box-shadow: inset 0 -2px 0 var(--np-accent);
  color: var(--np-head);
}

.nav-caret {
  width: 0;
  height: 0;
  border: 4px solid transparent;
  border-top-color: currentColor;
  border-bottom: 0;
  margin-top: 1px;
}

.nav-menu {
  position: relative;
}

.nav-menu[open] > summary {
  background: var(--np-sunken);
}

.nav-menu > ul {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  min-width: 280px;
  max-width: min(420px, 92vw);
  max-height: 60vh;
  overflow: auto;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
  border-radius: var(--r);
  box-shadow: 0 12px 32px rgba(20, 35, 55, 0.14);
}

.nav-menu > ul a {
  display: block;
  padding: 8px 10px;
  border-radius: var(--r);
  color: var(--np-text);
  text-decoration: none;
  font-size: var(--t-small);
  line-height: 1.35;
}

.nav-menu > ul a small {
  display: block;
  color: var(--np-muted);
  font-size: var(--t-meta);
}

.nav-menu > ul a:hover,
.nav-menu > ul a[aria-current='page'] {
  background: var(--np-sunken);
  color: var(--np-head);
}

.nav-menu > ul .nav-empty {
  padding: 8px 10px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* ---- Account ----------------------------------------------------------------- */
.account {
  display: grid;
  gap: 24px;
  max-width: 760px;
}

.acct-block {
  padding: 20px 24px;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
}

.acct-block > .eyebrow {
  margin: 0 0 12px;
}

.acct-facts {
  display: grid;
  gap: 10px 24px;
  margin: 0 0 12px;
}

.acct-facts > div {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 4px 16px;
}

.acct-facts dt {
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.acct-facts dd {
  margin: 0;
}

.acct-company {
  margin: 8px 0 2px;
}

.acct-sites {
  margin: 0 0 8px;
  padding-left: 20px;
}

.acct-requests {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}

.acct-request {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--np-rule);
}

.acct-request p {
  margin: 4px 0 0;
  font-size: var(--t-small);
  color: var(--np-muted);
  max-width: 60ch;
}

.acct-request .acct-pending {
  color: var(--np-mark);
}

.acct-request .secondary-button {
  flex: none;
}

.acct-ask {
  margin: 0 0 8px;
  padding: 14px 16px;
  border: 1px solid var(--np-rule);
  border-left: 2px solid var(--np-mark);
  background: var(--np-sunken);
}

.acct-ask .eyebrow {
  margin: 0 0 10px;
}

.acct-ask textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--np-field-edge);
  border-radius: var(--r);
  background: var(--np-panel);
  font: inherit;
  color: var(--np-text);
}

.acct-ask-acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-top: 10px;
}

.acct-done {
  margin: 0;
  padding-left: 20px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* ---- Legal --------------------------------------------------------------------- */
.legal-body {
  max-width: 72ch;
  margin: 0 0 40px;
}

.legal-body h2 {
  margin: 28px 0 8px;
  font-size: var(--t-body);
  font-weight: 600;
}

.legal-body p,
.legal-body li {
  margin: 0 0 10px;
  font-size: var(--t-small);
  line-height: 1.6;
}

.legal-body ul {
  padding-left: 20px;
}

/* ---- Help --------------------------------------------------------------------- */
.help {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin: 0 0 32px;
}

.help-block {
  padding: 18px 20px;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
}

.help-block h2 {
  margin: 0 0 6px;
  font-size: var(--t-body);
  font-weight: 600;
}

.help-block p {
  margin: 0;
  font-size: var(--t-small);
  color: var(--np-text);
}

@media (max-width: 720px) {
  .help {
    grid-template-columns: 1fr;
  }

  .acct-facts > div {
    grid-template-columns: 1fr;
  }

  .client-nav {
    order: 3;
    flex-basis: 100%;
    overflow-x: auto;
  }
}

/* ---- Standing in for somebody, or a pilot build --------------------------- */
/* A note by default. Redline is the warning colour and it is kept for the one case that is a warning:
   a member of staff standing in for a customer, reading what that customer can read. */
.client-notice {
  margin: 0;
  padding: 10px 24px;
  background: var(--np-accent-quiet);
  border-bottom: 1px solid var(--np-rule);
  font-size: var(--t-small);
  color: var(--np-head);
}

.client-notice.warn {
  background: var(--np-mark-quiet);
}

/* ---- The line back to the main website ------------------------------------ */
.promo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
  padding: 10px 24px;
  background: var(--np-sunken);
  border-bottom: 1px solid var(--np-rule);
  font-size: var(--t-small);
}

.promo-line {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0;
  min-width: 0;
}

.promo-label {
  flex: none;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.promo a {
  color: var(--np-text);
  text-decoration: none;
}

.promo a:hover,
.promo a:focus-visible {
  color: var(--np-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* A tap target, not a line of text: the rotating link sat at 22px, under the 24px anyone can reliably
   hit with a thumb. Inline-flex rather than padding so the banner's own height does not move. */
#promo-link {
  display: inline-flex;
  align-items: center;
  /* A flex container drops the whitespace text node between the two children, so the name ran
     straight into the line: "Mesh partitioningsecure cages and separated areas". */
  gap: 0.35em;
  min-height: 24px;
}

#promo-link strong {
  font-weight: 500;
  color: var(--np-head);
}

#promo-link span {
  color: var(--np-muted);
}

/* Scoped to beat `.promo a` on specificity rather than with !important. */
.promo .promo-all {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--np-accent);
  white-space: nowrap;
}

/* ---- The measured column -------------------------------------------------- */
.client-main {
  flex: 1;
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 40px 24px 72px;
}

.client-main > * + * {
  margin-top: 40px;
}

.lead h1 {
  margin: 0 0 8px;
  font-size: var(--t-page);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--np-head);
}

.lead p {
  margin: 0;
  max-width: 62ch;
  color: var(--np-muted);
}

.eyebrow {
  margin: 0;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.note {
  margin: 0;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.empty {
  margin: 0;
  padding: 28px 20px;
  background: var(--np-panel);
  border: 1px dashed var(--np-rule-strong);
  color: var(--np-muted);
  text-align: center;
}

/* ---- Finding a project, when there are enough to need finding ------------- */
.proj-filter {
  display: flex;
  align-items: end;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--np-rule);
}

.field {
  display: block;
  min-width: 0;
  flex: 1 1 220px;
}

.field > span {
  display: block;
  margin-bottom: 6px;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.field input,
.field select {
  width: 100%;
}

.proj-count {
  flex: none;
  margin: 0 0 9px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* ---- The projects, which are the point of the page ------------------------ */
/* auto-fill rather than auto-fit: one project stretched across the whole 1120px reads as an empty
   letterbox, where a card of its own width reads as a card. */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
  gap: 16px;
  align-items: start;
}

.proj-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 22px 24px;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
  border-left: 2px solid var(--np-accent);
  color: var(--np-text);
  text-decoration: none;
}

.proj-card:hover {
  border-color: var(--np-accent);
  border-left-color: var(--np-accent);
}

.proj-name {
  font-size: var(--t-section);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--np-head);
}

.proj-card:hover .proj-name {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.proj-summary {
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* The facts sit on the bottom edge whatever length the title ran to, so a row of cards reads along one
   line instead of a ragged band. */
.proj-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin-top: auto;
  padding-top: 14px;
  font-size: var(--t-meta);
  color: var(--np-muted);
}

.proj-facts span + span::before {
  content: '·';
  margin-right: 16px;
  color: var(--np-faint);
}

.proj-go {
  margin-top: 10px;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-accent);
}

/* ---- One project: the head ------------------------------------------------ */
.proj-head h1 {
  margin: 6px 0 8px;
  font-size: var(--t-page);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--np-head);
}

.proj-sub {
  margin: 0;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.proj-blurb {
  margin: 12px 0 0;
  max-width: 68ch;
}

/* ---- Where the job is up to ----------------------------------------------- */
/* No `margin: 0` here: it and `.client-main > * + *` score the same, this one is later in the file, and
   the strip landed on top of the line above it. Only the side the browser supplies is reset. */
.stages {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0 12px;
  margin-bottom: 0;
  padding: 0;
  list-style: none;
}

.stages li {
  padding-top: 12px;
  border-top: 2px solid var(--np-rule);
  color: var(--np-muted);
}

.stages li.done {
  border-top-color: var(--np-rule-strong);
}

.stages li.now {
  border-top-color: var(--np-accent);
  color: var(--np-head);
}

/* Muted, not faint: --np-faint is ink-400 and measures 2.79:1 on the paper page, which is a colour for
   a rule and not for a number somebody reads. */
.stage-no {
  display: block;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  color: var(--np-muted);
}

.stages li.now .stage-no {
  color: var(--np-accent);
}

.stage-text {
  display: block;
  margin-top: 2px;
  font-size: var(--t-small);
}

.stage-text strong {
  display: block;
  font-weight: 500;
}

.stage-text span {
  display: block;
  margin-top: 2px;
  font-size: var(--t-meta);
  color: var(--np-muted);
}

/* ---- When Northpoint has asked for something ------------------------------
   The only thing on a customer's project that asks them to act, so it takes the mark colour and sits
   above the documents. Redline is the revision and error colour everywhere else in this system; a
   question waiting on the reader is the one other thing that has earned it. */
.asked {
  padding: 22px 24px;
  background: var(--np-mark-quiet);
  border: 1px solid var(--np-rule);
  border-left: 2px solid var(--np-mark);
}

.asked h2 {
  margin: 6px 0 8px;
  font-size: var(--t-section);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--np-head);
}

.asked p {
  margin: 0;
  max-width: 68ch;
}

/* One card per question, each its own form: a prompt, a line of help, its control and its button. Two
   cards in a row sit closer than two sections do, so a run of questions reads as one ask, not three. */
.asked + .asked {
  margin-top: 12px;
}

.asked form {
  margin-top: 10px;
}

.ask-q {
  margin: 0 0 18px;
}

.ask-prompt {
  display: block;
  margin: 0 0 4px;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--np-head);
}

.ask-prompt i {
  font-style: normal;
  color: var(--np-mark);
}

.asked .ask-help {
  margin: 0 0 8px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.ask-q textarea,
.ask-q input[type='date'] {
  width: 100%;
  max-width: 560px;
  padding: 10px 12px;
  border: 1px solid var(--np-field-edge);
  border-radius: var(--r);
  background: var(--np-panel);
  font: inherit;
  color: var(--np-text);
}

.ask-q input[type='date'] {
  max-width: 220px;
}

.ask-q textarea:focus,
.ask-q input:focus-visible {
  outline: 2px solid var(--np-focus);
  outline-offset: 1px;
}

.ask-range {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.ask-range label {
  display: grid;
  gap: 4px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.ask-options {
  display: grid;
  gap: 6px;
}

.ask-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 28px;
  cursor: pointer;
}

.ask-opt input {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--np-accent);
}

.asked .ask-error {
  margin: 4px 0 0;
  font-size: var(--t-small);
  color: var(--np-mark);
}

.ask-error:empty {
  display: none;
}

.ask-acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-top: 4px;
}

.asked .ask-state {
  font-size: var(--t-small);
  color: var(--np-muted);
}

.asked .ask-state.alert {
  color: var(--np-mark);
}

/* ---- The documents -------------------------------------------------------- */
.files-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px 24px;
  flex-wrap: wrap;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--np-rule);
}

.files-head h2 {
  margin: 0;
  font-size: var(--t-section);
  letter-spacing: -0.015em;
  color: var(--np-head);
}

/* A list you can see all of, beside the one you are looking at. The old screen put every file behind a
   dropdown, which is a fine control for choosing and a poor one for browsing. */
.files-layout {
  display: grid;
  grid-template-columns: 296px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.file-rail {
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
}

.file-group + .file-group {
  border-top: 1px solid var(--np-rule);
}

/* The fold. A summary is a real control, so it gets a tap target and a focus ring; the marker goes and
   the arrow is drawn instead, because the browser's own triangle is a different shape in every browser. */
.file-head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 10px 16px;
  cursor: pointer;
  list-style: none;
}

.file-head::-webkit-details-marker {
  display: none;
}

.file-head:hover {
  background: var(--np-sunken);
}

.file-head:focus-visible {
  outline: 2px solid var(--np-focus);
  outline-offset: -2px;
}

.file-head > .eyebrow {
  flex: 1;
  min-width: 0;
  padding: 0;
}

/* Two thin strokes rotated into a chevron, so the shape is ours and turns with the fold. Closed points
   at the name; open points down at what it opened, which is the way every list of this kind behaves. */
.file-arrow {
  flex: none;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--np-muted);
  border-bottom: 1.5px solid var(--np-muted);
  transform: rotate(-45deg);
  transform-origin: 60% 60%;
  transition: transform 160ms cubic-bezier(0.4, 0, 0.2, 1);
}

.file-group[open] > .file-head .file-arrow {
  transform: rotate(45deg);
}

@media (prefers-reduced-motion: reduce) {
  .file-arrow {
    transition: none;
  }
}

/* The count stays readable folded or not: it is the only thing a closed group says about itself. */
.file-count {
  flex: none;
  min-width: 22px;
  padding: 1px 6px;
  border: 1px solid var(--np-rule-strong);
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  text-align: center;
  color: var(--np-muted);
}

.file-new-count {
  flex: none;
  padding: 1px 7px;
  background: var(--np-good-quiet);
  color: var(--np-good);
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.04em;
}

/* On the row itself, beside the name rather than off in a column, so it reads as part of the title. */
.file-new {
  display: inline-block;
  margin-left: 8px;
  padding: 0 6px;
  background: var(--np-good-quiet);
  color: var(--np-good);
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  font-weight: 400;
  letter-spacing: 0.04em;
  vertical-align: 1px;
}

.file-group:not([open]) > .file-row {
  display: none;
}

.file-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 10px 16px;
  background: none;
  border: 0;
  border-left: 2px solid transparent;
  text-align: left;
  color: var(--np-text);
  cursor: pointer;
}

.file-row:hover {
  background: var(--np-sunken);
}

.file-row.on {
  background: var(--np-accent-quiet);
  border-left-color: var(--np-accent);
}

/* The chosen row sits on a tinted panel, and in dark that tint takes the muted facts to 4.2:1. The row
   you are reading is also the one that has earned the darker ink. */
.file-row.on .file-facts {
  color: var(--np-text);
}

.file-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 38px;
  border: 1px solid var(--np-rule-strong);
  background: var(--np-sunken);
  font-family: var(--np-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--np-muted);
}

.file-text {
  min-width: 0;
}

.file-text strong {
  display: block;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-head);
  overflow-wrap: anywhere;
}

.file-facts {
  display: block;
  font-size: var(--t-meta);
  color: var(--np-muted);
  overflow-wrap: anywhere;
}

.file-view {
  min-width: 0;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
}

.file-view-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px 24px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border-bottom: 1px solid var(--np-rule);
}

.file-view-head strong {
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-head);
}

.file-acts {
  display: flex;
  gap: 20px;
  margin: 0;
}

.file-acts a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--t-small);
  color: var(--np-accent);
}

/* ---- The PDF itself ------------------------------------------------------- */
.pdf-preview {
  background: var(--np-sunken);
}

.pdf-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--np-rule);
  font-size: var(--t-small);
  color: var(--np-muted);
}

.pdf-toolbar button {
  min-height: 36px;
  padding: 6px 14px;
  background: var(--np-panel);
  border: 1px solid var(--np-field-edge);
  border-radius: var(--r);
  color: var(--np-accent);
  font-size: var(--t-small);
  cursor: pointer;
}

/* Muted rather than faint. A disabled control is exempt from the contrast rules and still has to be
   read — "Previous" at 3.13:1 is a word you cannot make out, which is not the same as one you cannot
   press. The blue against grey is what says which it is. */
.pdf-toolbar button:disabled {
  color: var(--np-muted);
  border-color: var(--np-rule);
  cursor: default;
}

.pdf-canvas-wrap {
  display: grid;
  place-items: start center;
  padding: 16px;
  overflow: auto;
}

.pdf-canvas-wrap canvas {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--np-rule);
  /* A PDF page is drawn for white paper and is white whatever scheme the portal is in. */
  background: var(--np-plate);
}

.sr-only-brand {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- The quotation -------------------------------------------------------- */
.quote-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 16px;
  align-items: start;
}

.quote-main {
  min-width: 0;
}

.quote-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0 40px;
  margin: 0 0 20px;
}

.quote-facts dt {
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.quote-facts dd {
  margin: 2px 0 0;
  font-size: var(--t-spec);
  color: var(--np-head);
}

.quote-table {
  width: 100%;
  margin: 0 0 24px;
  border-collapse: collapse;
  font-size: var(--t-small);
}

.quote-table caption {
  padding-bottom: 8px;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
  text-align: left;
}

.quote-table th,
.quote-table td {
  padding: 10px 0;
  border-bottom: 1px solid var(--np-rule);
  text-align: left;
  font-weight: 400;
  color: var(--np-text);
}

.quote-table td {
  width: 1%;
  padding-left: 24px;
  white-space: nowrap;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--np-head);
}

.quote-sum th,
.quote-sum td {
  border-bottom: 0;
  font-weight: 500;
  color: var(--np-head);
}

.extras {
  margin: 0;
  padding: 0;
  border: 0;
}

.extras legend {
  padding: 0 0 8px;
  font-family: var(--np-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--np-muted);
}

.extras .note {
  margin-bottom: 12px;
}

.extra {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--np-rule);
  cursor: pointer;
}

.extra + .extra {
  border-top: 0;
}

.extra:hover {
  background: var(--np-sunken);
}

/* Closed once the quotation is answered: what was ticked stays ticked, and the row stops inviting. */
.extra.closed {
  cursor: default;
}

.extra.closed:hover {
  background: none;
}

.extra input {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
}

.extra-text {
  min-width: 0;
  font-size: var(--t-small);
}

.extra-text strong {
  display: block;
  font-weight: 500;
  color: var(--np-head);
}

.extra-text span {
  display: block;
  margin-top: 2px;
  color: var(--np-muted);
  font-variant-numeric: tabular-nums;
}

.quote-total {
  position: sticky;
  top: 16px;
  padding: 20px;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
  border-top: 2px solid var(--np-accent);
}

.total-figure {
  margin: 8px 0 16px;
  font-size: 30px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--np-head);
  font-variant-numeric: tabular-nums;
}

.total-parts {
  margin: 0 0 12px;
  font-size: var(--t-small);
}

.total-parts > div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 7px 0;
  border-top: 1px solid var(--np-rule);
}

.total-parts dt {
  color: var(--np-muted);
}

.total-parts dd {
  margin: 0;
  color: var(--np-head);
  font-variant-numeric: tabular-nums;
}

/* The website order's aside carries headings and wants a spec-sized line with room above it, not a
   section heading. Scoped to .quote-total, the class it shares with a quotation's aside. */
.quote-total > h2 {
  margin: 24px 0 8px;
  font-size: var(--t-spec);
  font-weight: 500;
  color: var(--np-head);
}

/* A postcode or an unbroken street name in a narrow column has nowhere to break. */
.quote-total > p {
  overflow-wrap: anywhere;
}

/* ---- A website order summary -----------------------------------------------
   An order has no quotation, so its own summary stands where a quotation would, as a block of its own
   that the page order can move. Nothing sits beside it, so it keeps a column measure rather than
   stretching a four-figure sum and a delivery address the full width of the page. */
.order-summary > .order-total {
  max-width: 420px;
}
/* ---- What else Northpoint does --------------------------------------------
   This sits under the projects rather than over them. The customer came for their own job; this is what
   they find once they have it, which is the order that makes it worth reading rather than worth
   scrolling past. */

/* The one suggestion, which is the only part of this section that knows anything about the reader. It
   gets the accent edge the project cards have, because it is the same kind of object: a thing about your
   building, not a thing about ours. */
.next-step {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
  padding: 20px 24px;
  background: var(--np-accent-quiet);
  border: 1px solid var(--np-rule);
  border-left: 2px solid var(--np-accent);
  color: var(--np-text);
  text-decoration: none;
}

.next-step:hover {
  border-color: var(--np-accent);
}

.next-step strong {
  font-size: var(--t-section);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--np-head);
}

.next-step:hover strong {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.next-why {
  max-width: 68ch;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.svc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  gap: 16px;
}

.svc {
  display: flex;
  flex-direction: column;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
  color: var(--np-text);
  text-decoration: none;
}

.svc:hover {
  border-color: var(--np-accent);
}

/* The photograph is the only image on the customer's screens, so it is held to a shape rather than
   left to whatever the file is, and it sits under a hairline like everything else here. */
.svc img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-bottom: 1px solid var(--np-rule);
}

.svc strong {
  padding: 14px 16px 0;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-head);
}

.svc:hover strong {
  color: var(--np-accent);
}

.svc > span {
  padding: 2px 16px 16px;
  font-size: var(--t-meta);
  color: var(--np-muted);
}

/* The three that are a service rather than a product, and the reason somebody comes back: a list, not
   three more photographs. */
.svc-more {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 32px;
  margin: 16px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--np-rule);
  list-style: none;
}

.svc-more li {
  padding: 8px 0;
}

.svc-more a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--np-accent);
}

.svc-more span {
  display: block;
  font-size: var(--t-meta);
  color: var(--np-muted);
}

/* ---- Who to ring ---------------------------------------------------------- */
.contact {
  padding: 24px;
  background: var(--np-panel);
  border: 1px solid var(--np-rule);
}

.contact-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px 32px;
  flex-wrap: wrap;
  margin: 10px 0 12px;
}

.contact-row strong {
  display: block;
  font-size: var(--t-section);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--np-head);
}

.contact-row p {
  margin: 2px 0 0;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.contact-ways {
  display: flex;
  gap: 8px 24px;
  flex-wrap: wrap;
}

.contact-ways a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  color: var(--np-accent);
}

/* ---- The foot ------------------------------------------------------------- */
.client-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  flex-wrap: wrap;
  padding: 20px 24px;
  border-top: 1px solid var(--np-rule);
  font-size: var(--t-small);
  color: var(--np-muted);
}

.client-foot a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

.client-foot p {
  display: flex;
  gap: 8px 20px;
  flex-wrap: wrap;
  margin: 0;
}

.client-foot .mono {
  letter-spacing: 0.08em;
}

/* ---- Narrower ------------------------------------------------------------- */
@media (max-width: 980px) {
  /* The rail goes above the document rather than beside it: at this width a 296px column leaves the
     drawing too narrow to read, and the drawing is what they came for. */
  .files-layout,
  .quote-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .quote-total {
    position: static;
  }

  .stages {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 16px 12px;
  }
}

@media (max-width: 640px) {
  .client-bar,
  .promo,
  .client-notice,
  .client-foot {
    padding-inline: 16px;
  }

  .client-main {
    padding: 28px 16px 56px;
  }

  /* Three lines for a marketing strip is not "less intrusive". The label and the descriptive clause go;
     the name of the thing and a way to the rest of them stay, on one line. */
  .promo {
    gap: 8px 16px;
  }

  .promo-label,
  #promo-link span {
    display: none;
  }

  /* The link is now two words on its own line, and a thumb still has to land on it. */
  #promo-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .client-main > * + * {
    margin-top: 32px;
  }

  .proj-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .proj-card {
    padding: 20px 18px;
  }

  .next-step {
    padding: 18px;
  }

  .svc-more a {
    min-height: 44px;
  }

  /* A thumb, not a pointer. */
  .file-acts a,
  .contact-ways a,
  .promo .promo-all {
    min-height: 44px;
  }

  .file-row {
    min-height: 60px;
  }

  .pdf-canvas-wrap {
    padding: 10px;
  }

  .stages {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .stages li {
    padding: 10px 0;
    border-top: 1px solid var(--np-rule);
  }

  .stages li.now {
    border-top-width: 2px;
  }
}

/* ---- The 3D walkthrough --------------------------------------------------- */
/* Filed with the documents and drawn like one, so the rail's tile carries the only difference: a
   walkthrough is a place, not a file, and it says so before it is opened. */
.file-icon.walk {
  border-color: var(--np-accent);
  color: var(--np-accent);
  background: var(--np-panel);
}

/* 16:9, because that is the shape Twinmotion renders and anything else letterboxes the scene twice.
   Height is capped so the panel cannot push the quotation off a laptop screen. */
.walk-frame {
  aspect-ratio: 16 / 9;
  max-height: 70vh;
  background: var(--np-sunken);
}

.walk-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.file-view .note {
  margin: 0;
  padding: 10px 16px;
  border-top: 1px solid var(--np-rule);
  font-size: var(--t-meta);
  color: var(--np-muted);
}

/* The host is not one the portal may frame. Nobody is told about a policy; they are given the link. */
.walk-out {
  display: grid;
  gap: 16px;
  justify-items: start;
  padding: 24px 16px;
}

.walk-out p {
  margin: 0;
  max-width: 54ch;
  font-size: var(--t-small);
  color: var(--np-text);
}

/* ---- Accept or decline ---------------------------------------------------- */
/* A block of its own, put where the page order puts it, usually under the quotation. An edge along
   the top rather than a box: it is an answer to the page above it, not a panel beside it. The section
   already keeps its distance from the block before, so the edge sits at the top of it. */
.decide {
  border-top: 1px solid var(--np-rule-strong);
  padding-top: 24px;
  display: grid;
  gap: 14px;
  justify-items: start;
}

.decide-total {
  margin: 0;
  font-size: var(--t-body);
  font-variant-numeric: tabular-nums;
  color: var(--np-head);
}

.decide h3 {
  margin: 0;
  max-width: 34ch;
  font-size: var(--t-section);
  font-weight: 500;
  color: var(--np-head);
}

.decide-note,
.decide-after,
.decide-reason {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.decide-said {
  margin: 0;
  font-size: var(--t-body);
  color: var(--np-text);
}

.decide-said strong {
  font-weight: 500;
  color: var(--np-head);
}

.decide-reason {
  color: var(--np-text);
}

.decide-say {
  display: grid;
  gap: 6px;
  width: 100%;
  max-width: 480px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.decide-say em {
  font-style: normal;
  color: var(--np-muted);
}

.decide-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.decide-state:empty {
  display: none;
}

.decide-state {
  margin: 0;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* Answered. The colour is the whole signal, so it is an edge and a word, not a badge. */
.decide.done {
  border-top-color: var(--np-accent);
}

.decide.done.declined {
  border-top-color: var(--np-rule-strong);
}

/* ---- An answer that has been given -----------------------------------------
   The question stops being a form. What is left is the fact: what was asked, what was said, and when.
   The card keeps its shape and changes its edge: redline is a thing waiting on the reader, and a
   question they have answered is not waiting on anybody. Green settles it, and the words under the
   answer say the same, so the colour is not the only teller. */
.asked.answered {
  background: none;
  border-left-color: var(--np-good);
}

.ask-said {
  margin: 0;
}

.ask-said .ask-prompt {
  display: block;
  margin: 0 0 6px;
}

.ask-value {
  margin: 0 0 6px;
  max-width: 62ch;
  font-size: var(--t-section);
  line-height: 1.3;
  color: var(--np-head);
}

.ask-when,
.decide-final,
.var-final {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-small);
  color: var(--np-muted);
}

/* Being sure. The answer is final and there is no way back from this page, so the button that gives it
   says what it is about to do before it does it. */
.decide-sure {
  display: grid;
  gap: 12px;
  justify-items: start;
  width: 100%;
  max-width: 560px;
  padding: 14px 16px;
  border: 1px solid var(--np-rule-strong);
  background-color: var(--np-sunken);
}

.decide-sure p {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-small);
  color: var(--np-text);
}

/* ---- Variations -----------------------------------------------------------
   Work priced after the quotation was accepted. Each one is its own card because each one is its own
   agreement: the customer answers them separately and may say yes to one and no to the next. They sit
   under the quotation and are marked off from it, so nothing here can be read as a change to the
   figure that was agreed. */
.variations {
  border-top: 1px solid var(--np-rule-strong);
  padding-top: 24px;
  display: grid;
  gap: 14px;
  justify-items: start;
}

.variations h2 {
  margin: 0;
  font-size: var(--t-section);
  font-weight: 500;
  color: var(--np-head);
}

.variations > .note,
.var-waiting {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.var-list {
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  display: grid;
  gap: 16px;
}

.var {
  display: grid;
  gap: 10px;
  justify-items: start;
  padding: 16px 18px;
  border: 1px solid var(--np-rule);
  border-left: 3px solid var(--np-rule-strong);
  background-color: var(--np-panel);
}

.var.accepted {
  border-left-color: var(--np-accent);
}

.var.asking {
  border-left-color: var(--np-head);
}

.var-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}

.var-head .eyebrow {
  margin: 0;
}

.var-price {
  margin: 0;
  font-size: var(--t-section);
  font-variant-numeric: tabular-nums;
  color: var(--np-head);
}

.var-price small {
  font-size: var(--t-small);
  color: var(--np-muted);
}

.var h3 {
  margin: 0;
  max-width: 44ch;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--np-head);
}

.var-detail,
.var-reason,
.var-said {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.var-said {
  font-size: var(--t-body);
  color: var(--np-text);
}

.var-said strong {
  font-weight: 500;
}

.var-doc {
  margin: 0;
}

.var-say {
  display: grid;
  gap: 6px;
  width: 100%;
  max-width: 480px;
  font-size: var(--t-small);
  color: var(--np-muted);
}

.var-say em {
  font-style: normal;
  color: var(--np-muted);
}

.var-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.var-state:empty {
  display: none;
}

.var-total {
  margin: 0;
  font-size: var(--t-body);
  font-variant-numeric: tabular-nums;
  color: var(--np-head);
}

/* ---- The password a private scene asks for --------------------------------
   Above the frame, because that is where the question appears. A line, not a panel: one short fact and
   a way to take it with you. */
.walk-pass {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0;
  padding: 12px 16px;
  border-bottom: 1px solid var(--np-rule);
  background: var(--np-sunken);
  font-size: var(--t-small);
  color: var(--np-text);
}

.walk-pass code {
  padding: 3px 8px;
  border: 1px solid var(--np-rule-strong);
  background: var(--np-panel);
  color: var(--np-head);
  font-size: var(--t-small);
  letter-spacing: 0.04em;
  /* One click takes the whole password, so the Copy button failing is not the end of it. */
  user-select: all;
}

.walk-copied {
  font-size: var(--t-meta);
  color: var(--np-muted);
}

/* ---- A touch screen ----------------------------------------------------------- */
/* On a touch screen the bar's links and the foot's links are a thumb's height, 44px, as the portal's own
   screens are (R1-052); a mouse keeps the tighter 40 and 24. The privacy notice is the customer page that
   uses this sheet. */
@media (pointer: coarse) {
  .client-nav > a,
  .client-nav .nav-menu > summary,
  .client-foot a {
    min-height: 44px;
  }
}
