/* ==========================================================================
   Contractor Trade, components.css
   Buttons, cards, badges, forms, tables, dialogs, and the patterns specific
   to this product: trust panel, engagement slots, quote lines.

   Geometry rule: anything you press is a capsule; anything that holds content
   is a generously rounded rectangle. Both come from tokens.css.
   ========================================================================== */

/* ==========================================================================
   Brand mark
   ========================================================================== */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}
/* The short icon, the house with CT cut out of it, where only a mark
   fits: sized by its height. */
.brand-mark { display: inline-flex; align-items: center; flex-shrink: 0; }
/* A member's logo at the head of their quotation and agreement (12 Oct). */
.doc-logo { display: block; max-height: 48px; max-width: 180px; object-fit: contain; margin-bottom: var(--s3); }
.brand-mark svg { display: block; height: 32px; width: auto; }
.brand-mark-lg svg { height: 40px; }

.brand-text { min-width: 0; display: flex; align-items: center; }
/* The logo (17 Oct, supplied): the house and ContractorTrade, drawn rather
   than typed, sized by this font size: 1em is 100 of its units, so its
   letters stand .67em tall. The name's ink is this colour, the house and
   Trade the accent; dark grounds set --brand-text-a and -b to white and the
   light accent. */
.brand-name {
  display: inline-flex;
  align-items: baseline;
  font-family: var(--brand-font, var(--display));
  font-size: var(--fs-20);
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  color: var(--brand-text-a, var(--text));
}
.brand-word { display: block; flex: none; width: 9.1em; height: 1.398em; overflow: visible; }
/* DIRECT follows the name on the same line (2 Oct, the user: "inline
   only"): small tracked capitals in Figtree, the open face closest to the
   logo's lettering, in the name's ink (white on a dark ground), standing on
   the letters' line, .15em above the house's floor. The same in the public
   site, sign-in, footer and application rail; the public header's is
   larger (--brand-c-size, its share of the name's size). */
.brand-c {
  --brand-c-size: .58;
  position: relative;
  top: calc(-.15em / var(--brand-c-size));
  margin-left: .4em;
  font-size: calc(var(--brand-c-size) * 1em);
  font-weight: 700;
  letter-spacing: .12em;
  line-height: 1;
  text-transform: uppercase;
  color: var(--brand-text-a, var(--text));
}
/* The whole logo a step smaller (8 Oct, the user: "make logo font a bit
   small"; again 12 Oct, "make a bit small": 0.84, was 0.92), DIRECT with
   it, wherever it is drawn. */
.brand .brand-text { zoom: .84; }


/* ==========================================================================
   Buttons, capsules
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  height: 40px;
  padding: 0 var(--s5);
  border: 1px solid transparent;
  border-radius: var(--r-btn);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-14);
  font-weight: 550;
  letter-spacing: -.005em;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease-out);
}
.btn svg { width: 17px; height: 17px; flex-shrink: 0; }
.btn:active:not(:disabled) { transform: translateY(1px); }

/* Filled actions use the darker action member of the active theme; the mark
   and small signals can keep the brighter identity accent. */
.btn-primary {
  background: var(--accent-fill);
  color: var(--accent-on);
  border-color: var(--accent-fill);
}
/* Hover and press stay in that theme's declared family. */
.btn-primary:hover:not(:disabled) { background: var(--accent-fill-hover); border-color: var(--accent-fill-hover); }
.btn-primary:active:not(:disabled) { background: var(--accent-fill-active); border-color: var(--accent-fill-active); }

/* Secondary: the ordinary alternative to a primary action. */
.btn-default {
  border-color: var(--border-strong);
  background: var(--surface);
  color: var(--text);
}
.btn-default:hover:not(:disabled) {
  background: var(--surface-2);
  border-color: var(--text-3);
}

/* Subtle: the quiet treatment for back controls and supporting tools. It is
   visible before hover, then moves one neutral step darker. */
.btn-subtle { background: var(--surface-2); color: var(--text-2); }
.btn-subtle:hover:not(:disabled) { background: var(--surface-3); color: var(--text); }

.btn-danger { background: var(--critical); color: var(--brand-on); }
.btn-danger:hover:not(:disabled) { filter: brightness(1.08); }

.btn-lg { height: 52px; padding: 0 var(--s7); font-size: var(--fs-16); font-weight: 600; }
.btn-lg svg { width: 19px; height: 19px; }
.btn-sm { height: 32px; padding: 0 var(--s4); font-size: var(--fs-13); }
.btn-sm svg { width: 15px; height: 15px; }
.btn-icon { width: 40px; padding: 0; }
.btn-icon.btn-sm { width: 32px; }
.btn-icon.btn-lg { width: 52px; }
.btn-block { width: 100%; }

.btn:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; }
.btn[aria-busy="true"] { pointer-events: none; opacity: .7; }

/* Segmented group of buttons, still a capsule overall. */
.btn-group {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-btn);
}
.btn-group .btn {
  height: 32px;
  padding: 0 var(--s4);
  border: none;
  background: transparent;
  color: var(--text-2);
  box-shadow: none;
}
.btn-group .btn:hover { background: transparent; color: var(--text); }
.btn-group .btn[aria-pressed="true"] {
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--sh-1);
}
.btn-group .btn-icon { width: 34px; padding: 0; }

@media (max-width: 860px) {
  .btn { min-height: var(--tap-min); }
  .btn-sm { min-height: 38px; height: 38px; }
  .btn-icon { width: var(--tap-min); }
}

/* ==========================================================================
   Cards & panels
   ========================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.card-pad { padding: var(--s6); }
.card-pad-sm { padding: var(--s5); }
.card-hover { transition: border-color var(--t-fast) var(--ease); }
.card-hover:hover { border-color: var(--border-strong); }

/* A card that carries the brand wash: used sparingly, for the one panel on
   a page that should be looked at first. */
.card-brand { background: var(--surface-brand); border-color: var(--border); }

.card-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s5) var(--s6);
  border-bottom: 1px solid var(--border);
}
.card-head .h3 { min-width: 0; }
/* In a sidebar the title and its badge do not both fit on one line, and the
   title was wrapping to two while the badge stayed pinned beside it. The
   badge drops underneath instead, which is the shape that has room. */
.card-head > .grow { min-width: 160px; }
.card-head-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }
.card-foot {
  padding: var(--s4) var(--s6);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  display: flex;
  align-items: center;
  gap: var(--s3);
}
.card-section { padding: var(--s6); border-top: 1px solid var(--border); }
.card-section:first-child { border-top: none; }

.panel-inset {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s4);
}

/* ==========================================================================
   Badges, chips, pills
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 var(--s3) 0 var(--s2);
  border-radius: var(--r-btn);
  font-size: var(--fs-11);
  font-weight: 600;
  letter-spacing: .005em;
  white-space: nowrap;
  background: var(--neutral-soft);
  color: var(--neutral-ink);
}
.badge svg { width: 12px; height: 12px; }
.badge-lg { height: 28px; font-size: var(--fs-12); padding: 0 var(--s4) 0 var(--s3); }

.badge-good     { background: var(--good-soft);     color: var(--good-ink); }
.badge-warning  { background: var(--warning-soft);  color: var(--warning-ink); }
.badge-serious  { background: var(--serious-soft);  color: var(--serious-ink); }
.badge-critical { background: var(--critical-soft); color: var(--critical-ink); }
.badge-neutral  { background: var(--neutral-soft);  color: var(--neutral-ink); }
.badge-accent   { background: var(--accent-soft);   color: var(--accent-ink); }

/* `info` is the IN PROGRESS tone: matching, sent, viewed, awaiting signature.
   It is an alias of the accent, not a sixth colour: blue already means
   "working on it", and giving that its own hue would be one more thing for a
   user to learn for no extra meaning. */
.badge-info     { background: var(--accent-soft);   color: var(--accent-ink); }
.badge-brand    { background: var(--brand); color: var(--brand-on); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  height: 34px;
  padding: 0 var(--s4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-btn);
  background: var(--surface);
  font-size: var(--fs-13);
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.chip svg { width: 14px; height: 14px; }
.chip:hover { border-color: var(--text-3); color: var(--text); background: var(--surface-2); }
.chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on);
}
/* A filter chip, once picked, carries a tick, so what is on reads at a
   glance. Only on filters (chip-tick) and checkbox chips, not on chips that
   act as tabs, where one is always the current one. */
.chip-tick[aria-pressed="true"]::before,
.chip-check:has(input:checked)::before {
  content: '';
  width: 12px; height: 12px; flex-shrink: 0;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
.has-reveal { position: relative; }
.has-reveal .input { padding-right: 44px; }
.input-reveal { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); width: 34px; height: 34px;
  border: 0; border-radius: var(--r-icon-btn); background: none; color: var(--text-3); display: inline-flex; align-items: center;
  justify-content: center; cursor: pointer; }
.input-reveal:hover { color: var(--text); background: var(--surface-2); }
button.step { border: 0; background: none; font: inherit; color: inherit; cursor: pointer; padding: 0; }
button.step:hover .step-num { box-shadow: 0 0 0 2px var(--accent); }
.chip-count { font-size: var(--fs-12); color: var(--text-3); font-variant-numeric: tabular-nums; }
.chip.chip-empty { color: var(--text-3); border-style: dashed; }
.chip-static { cursor: default; border-color: var(--border); background: var(--surface-2); }
.chip-static:hover { border-color: var(--border); color: var(--text-2); background: var(--surface-2); }
.chip-lg { height: 42px; padding: 0 var(--s5); font-size: var(--fs-14); }


kbd {
  font-family: var(--font);
  font-size: var(--fs-11);
  font-weight: 600;
  padding: 2px 6px;
  min-width: 20px;
  display: inline-block;
  text-align: center;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-3);
}

/* ==========================================================================
   Avatars and tiles
   ========================================================================== */
.avatar {
  width: 36px; height: 36px;
  border-radius: var(--r-full);
  /* The colour arrives with the tint; only the width and style live here, so
     one place decides how thick every placeholder edge is. */
  border: 1px solid transparent;
  display: grid;
  place-items: center;
  font-size: var(--fs-12);
  font-weight: 650;
  flex-shrink: 0;
  overflow: hidden;
  user-select: none;
}
.avatar-sm { width: 26px; height: 26px; font-size: var(--fs-11); }

/* An icon button carrying a status dot. The two places that needed this were
   each writing the same four inline properties. */
.has-dot { position: relative; }
.has-dot .dot { position: absolute; top: 7px; right: 7px; }
.avatar-lg { width: 52px; height: 52px; font-size: var(--fs-16); }
.avatar-xl { width: 80px; height: 80px; font-size: var(--fs-24); }
/* A picture, when there is one, fills the same circle or square; without
   one, the initials stay. */
.avatar img, .logo-tile img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
.avatar.has-img, .logo-tile.has-img { background: var(--surface); border-color: var(--border); overflow: hidden; }
/* The account button in the header: the person, as a round picture. */
.account-button { display: inline-flex; padding: 0; border: 0; background: none; border-radius: var(--r-full); cursor: pointer; }
.account-button .avatar { width: 38px; height: 38px; }
.account-button:hover .avatar { box-shadow: 0 0 0 2px var(--border); }
.account-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.avatar-stack { display: flex; }
.avatar-stack .avatar { margin-left: -10px; box-shadow: 0 0 0 2px var(--surface); }
.avatar-stack .avatar:first-child { margin-left: 0; }

/* A company gets a rounded square, not a circle. A company is not a person,
   and that difference should be readable at a glance in a mixed list. */
.logo-tile {
  width: 48px; height: 48px;
  border-radius: var(--r-logo);
  border: 1px solid transparent;
  display: grid;
  place-items: center;
  font-size: var(--fs-14);
  font-weight: 750;
  letter-spacing: -.03em;
  flex-shrink: 0;
  overflow: hidden;
}
.logo-tile img { width: 100%; height: 100%; object-fit: cover; }
.logo-tile-lg { width: 64px; height: 64px; font-size: var(--fs-20); border-radius: var(--r-logo-lg); }
.logo-tile-sm { width: 36px; height: 36px; font-size: var(--fs-12); border-radius: var(--r-logo-sm); }

/* Icon tiles are circles: it is the friendliest shape at small sizes and it
   keeps them visibly distinct from company logos. */
/* Icon tiles are circles: it is the friendliest shape at small sizes and it
   keeps them visibly distinct from company logos, which are rounded squares.

   The fill is a hairline-bordered white rather than a blue wash. The wash
   was --accent-soft, which is a very pale blue, and half of these tiles sit
   on a --surface-2 band, which is a very pale grey. Two near-whites stacked
   read as a smudge, and the icon inside them lost its edge. White with a
   border holds on any surface, and the accent stays on the icon where it
   carries meaning. */
.icon-tile {
  width: 40px; height: 40px;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--accent);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
/* On a tinted band the border does the separating, so it steps up a shade. */
.band .icon-tile { border-color: var(--border-strong); }
.icon-tile svg { width: 19px; height: 19px; }
.icon-tile-lg { width: 52px; height: 52px; }
.icon-tile-lg svg { width: 23px; height: 23px; }
.icon-tile-sm { width: 30px; height: 30px; }
.icon-tile-sm svg { width: 15px; height: 15px; }

/* A tile carrying a status keeps its wash, because there the colour is the
   information. Each one also drops the hairline, so a status tile and a
   plain one never look like the same object in two colours. */
.icon-tile-accent   { background: var(--accent-soft);   color: var(--accent-ink);   border-color: transparent; }
.icon-tile-info     { background: var(--accent-soft);   color: var(--accent-ink);   border-color: transparent; }
.icon-tile-good     { background: var(--good-soft);     color: var(--good-ink);     border-color: transparent; }
.icon-tile-warning  { background: var(--warning-soft);  color: var(--warning-ink);  border-color: transparent; }
.icon-tile-serious  { background: var(--serious-soft);  color: var(--serious-ink);  border-color: transparent; }
.icon-tile-critical { background: var(--critical-soft); color: var(--critical-ink); border-color: transparent; }
.icon-tile-neutral  { background: var(--neutral-soft);  color: var(--neutral-ink);  border-color: transparent; }
.icon-tile-solid    { background: var(--accent); color: var(--accent-on); border-color: transparent; }
.icon-tile-dark     { background: var(--brand);  color: var(--brand-on);  border-color: transparent; }

/* ==========================================================================
   Forms
   ========================================================================== */
.field { display: flex; flex-direction: column; gap: 7px; }
.field-label {
  font-size: var(--fs-13);
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.field-label .req { color: var(--critical); font-weight: 400; }
.field-hint { font-size: var(--fs-12); color: var(--text-3); line-height: 1.45; }
.field-error {
  font-size: var(--fs-12);
  color: var(--critical-ink);
  display: flex;
  align-items: center;
  gap: 5px;
}
.field-error svg { width: 13px; height: 13px; flex-shrink: 0; }

.input, .select, .textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px var(--s4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-14);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--text-3); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--text-3); }
/* One edge, and a halo that is clearly a halo. At 4px of a 26% blue the
   ring had a hard enough edge of its own to read as a second border around
   the first one. Three pixels of a lighter wash sits under the border
   instead of competing with it, and it is still an unmistakable focus
   state, which is the thing it is actually for. */
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--ring-soft);
}
.textarea { min-height: 104px; resize: vertical; line-height: var(--lh-normal); }
.select {
  appearance: none;
  padding-right: var(--s8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s4) center;
  background-size: 16px;
  cursor: pointer;
}
/* A pill input, for search fields where the capsule language should carry. */
.input-pill { border-radius: var(--r-btn); padding-left: var(--s5); padding-right: var(--s5); }

[aria-invalid="true"].input, [aria-invalid="true"].select, [aria-invalid="true"].textarea {
  border-color: var(--critical);
}
[aria-invalid="true"].input:focus {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--critical) 20%, transparent);
}

.input-group { position: relative; display: flex; align-items: center; }
.input-group .input { padding-left: var(--s9); }
.input-group .input-icon {
  position: absolute;
  left: var(--s4);
  color: var(--text-3);
  pointer-events: none;
  display: flex;
}
.input-group .input-icon svg { width: 17px; height: 17px; }
.input-prefix {
  position: absolute;
  left: var(--s4);
  font-size: var(--fs-14);
  color: var(--text-2);
  pointer-events: none;
}
.input-group.has-prefix .input { padding-left: 30px; }
.input-group.has-suffix .input { padding-right: 34px; padding-left: var(--s3); }
.input-suffix {
  position: absolute;
  right: 12px;
  color: var(--text-3);
  font-size: var(--fs-13);
  pointer-events: none;
}

@media (max-width: 860px) {
  .input, .select, .textarea { font-size: var(--fs-16); } /* 16px stops iOS zoom */
}

/* Checkbox & radio */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer;
  min-height: var(--tap-min);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.check:hover { border-color: var(--border-strong); background: var(--surface-2); }
.check input { margin-top: 2px; accent-color: var(--accent); width: 17px; height: 17px; flex-shrink: 0; }
.check:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
/* Title and description are two lines, never run together. */
.check-title { display: block; font-size: var(--fs-13); font-weight: 600; }
.check-desc { display: block; font-size: var(--fs-12); color: var(--text-2); line-height: 1.45; }
.check-plain { border: none; padding: 0; min-height: 0; background: none; }
.check-plain:hover { background: none; border: none; }
.check-plain:has(input:checked) { background: none; border: none; }

/* Switch */
.switch { display: inline-flex; align-items: center; gap: var(--s3); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 42px; height: 24px;
  border-radius: var(--r-full);
  background: var(--border-strong);
  position: relative;
  transition: background var(--t-fast) var(--ease);
  flex-shrink: 0;
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 20px; height: 20px;
  border-radius: var(--r-full);
  background: var(--brand-on);
  box-shadow: var(--sh-1);
  transition: transform var(--t-fast) var(--ease-out);
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(18px); }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 4px var(--ring); }

/* Segmented control, a capsule of capsules. */
.segmented {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-btn);
}
/* A segment may be a button or a link. Where the choice belongs in the URL,
   because it should survive a refresh and be shareable, it is a link and
   carries aria-selected; where it is local state it is a button with
   aria-pressed. Both look the same, because to the person using it they are
   the same control. */
.segmented button,
.segmented .seg {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 var(--s4);
  border-radius: var(--r-btn);
  font-size: var(--fs-13);
  font-weight: 550;
  color: var(--text-2);
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.segmented button:hover,
.segmented .seg:hover { color: var(--text); }
.segmented button[aria-pressed="true"],
.segmented .seg[aria-selected="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--sh-1);
}

/* ==========================================================================
   Tabs, pill tabs rather than an underline row
   ========================================================================== */
/* A page with many tabs (a job has nine) fits them on a laptop; where it
   cannot, a thin scrollbar says there are more rather than hiding them. */
.tabs {
  display: flex;
  gap: var(--s1);
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: 2px;
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  height: 38px;
  padding: 0 var(--s3);
  border-radius: var(--r-btn);
  font-size: var(--fs-13);
  font-weight: 550;
  color: var(--text-2);
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tab:hover { background: var(--surface-2); color: var(--text); }
.tab[aria-selected="true"] {
  background: var(--brand);
  color: var(--brand-on);
  font-weight: 600;
}
.tab .count {
  font-size: var(--fs-11);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  padding: 1px 7px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  color: var(--text-2);
}
.tab[aria-selected="true"] .count {
  background: color-mix(in srgb, var(--brand-on) 18%, transparent);
  color: var(--brand-on);
}

/* Secondary navigation for sections inside a page. It keeps the same quiet
   selected-row language as the main rail at a smaller scale. */
.with-subnav {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: var(--s6);
  align-items: start;
}
.with-subnav > :last-child { min-width: 0; }
.subnav {
  position: sticky;
  top: calc(var(--topbar-h) + var(--s5));
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.subnav-link {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  min-height: 38px;
  padding: 0 var(--s3);
  border-radius: var(--r-btn);
  color: var(--sidebar-ink);
  font-size: var(--fs-13);
  font-weight: 500;
  text-align: left;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.subnav-link:hover { background: var(--sidebar-hover); color: var(--sidebar-ink-hi); }
.subnav-link[aria-current="page"] {
  background: var(--sidebar-active);
  color: var(--sidebar-ink-hi);
  font-weight: 600;
}
.subnav-link svg { flex: 0 0 auto; opacity: .7; }
.subnav-link .count {
  display: grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: auto;
  border-radius: var(--r-full);
  background: var(--brand);
  color: var(--brand-on);
  font-size: var(--fs-11);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1100px) {
  .with-subnav { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .subnav {
    position: static;
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: thin;
  }
  .subnav-link { width: auto; flex: 0 0 auto; padding-inline: var(--s3); }
}

/* ==========================================================================
   Tables, reflow to cards on mobile
   ========================================================================== */
.table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
}
.table-scroll { overflow-x: auto; }

/* A queue is read top to bottom, and a header that scrolls away takes the
   meaning of every column with it. Anything longer than a screenful gets a
   scroll box of its own and a header that stays put. */
.table-scroll-y {
  max-height: min(62vh, 640px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.table-scroll-y table.data thead th {
  position: sticky;
  top: 0;
  z-index: 2;
}
table.data { width: 100%; font-size: var(--fs-13); }
table.data caption {
  text-align: left;
  padding: var(--s4) var(--s6);
  font-size: var(--fs-12);
  color: var(--text-3);
}
/* A caption inside a card whose head already names the table is a second
   heading in different words, and it drew a band of empty space between the
   head and the column headings. It stays in the markup, because it is the
   table's accessible name and a screen reader announces it before the rows,
   and it comes out of the visual flow because the card head is already
   saying it to everybody else. */
.card-head ~ .table-scroll table.data caption,
.card-head ~ .table-wrap table.data caption,
.card-head ~ table.data caption {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
table.data th {
  text-align: left;
  font-size: var(--fs-11);
  font-weight: 650;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-3);
  background: var(--surface-2);
  padding: var(--s3) var(--s6);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
table.data td {
  padding: var(--s4) var(--s6);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr.clickable { cursor: pointer; }
table.data tbody tr:hover { background: var(--surface-2); }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
/* Money, counts and dates line up on the decimal or they are not a column. */
table.data td.mono, table.data .tnum { font-variant-numeric: tabular-nums; }
table.data tfoot td {
  background: var(--surface-2);
  font-weight: 600;
  border-top: 1px solid var(--border-strong);
  border-bottom: none;
}

@media (max-width: 860px) {
  table.data.reflow thead { display: none; }
  table.data.reflow tbody tr {
    display: block;
    padding: var(--s4) var(--s5);
    border-bottom: 1px solid var(--border);
  }
  table.data.reflow tbody td {
    display: flex;
    justify-content: space-between;
    gap: var(--s4);
    padding: 3px 0;
    border: none;
    text-align: right;
  }
  table.data.reflow tbody td::before {
    content: attr(data-label);
    font-size: var(--fs-12);
    font-weight: 550;
    color: var(--text-3);
    text-align: left;
    flex-shrink: 0;
  }
  table.data.reflow tbody td.cell-title {
    display: block;
    text-align: left;
    font-weight: 650;
    font-size: var(--fs-14);
    margin-bottom: var(--s2);
  }
  table.data.reflow tbody td.cell-title::before { display: none; }
}

/* ==========================================================================
   Meters & gauges
   ========================================================================== */
.meter {
  height: 8px;
  background: var(--ord-track);
  border-radius: var(--r-full);
  overflow: hidden;
}
.meter-fill {
  height: 100%;
  background: var(--accent);
  border-radius: var(--r-full);
  transition: width var(--t-slow) var(--ease-out);
}
.meter-lg { height: 11px; }
.meter-sm { height: 6px; }
.meter-good     .meter-fill { background: var(--good); }
.meter-info     .meter-fill { background: var(--accent); }
.meter-warning  .meter-fill { background: var(--warning); }
.meter-serious  .meter-fill { background: var(--serious); }
.meter-critical .meter-fill { background: var(--critical); }

/* Four-segment qualitative gauge: credit, payment history. A word always
   accompanies it; the segments alone never carry the meaning. */
/* Label, then value, then the bars: three stacked rows that all share one
   left edge, so nothing has to line up with a wrapped heading. */
.gauge-block { display: flex; flex-direction: column; gap: var(--s1); }
.gauge-value { font-size: var(--fs-15); font-weight: 650; color: var(--text); line-height: 1.2; }
.gauge { display: flex; gap: 4px; margin-top: var(--s1); }
.gauge span {
  flex: 1 1 0;
  height: 8px;
  border-radius: var(--r-full);
  background: var(--surface-3);
}
.gauge span.on { background: var(--accent); }
.gauge-neutral span.on  { background: var(--border-strong); }
.gauge-good span.on     { background: var(--good); }
.gauge-info span.on     { background: var(--accent); }
.gauge-warning span.on  { background: var(--warning); }
.gauge-serious span.on  { background: var(--serious); }
.gauge-critical span.on { background: var(--critical); }

/* Star rating */
.stars { display: inline-flex; align-items: center; gap: 1px; color: var(--warning); }
.stars svg { width: 15px; height: 15px; fill: currentColor; }
.stars .off { color: var(--surface-3); }
.rating-pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-14); white-space: nowrap; }
.rating-pill b { font-weight: 650; font-variant-numeric: tabular-nums; }
.rating-star { display: inline-flex; color: var(--warning); }
.rating-star svg { width: 16px; height: 16px; fill: currentColor; }
.rating-count { font-size: var(--fs-13); color: var(--text-3); }
.rating-pill.is-new { font-size: var(--fs-12); font-weight: 600; color: var(--text-2); background: var(--surface-2);
  padding: 3px 10px; border-radius: var(--r-btn); }
.tag { display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border-radius: var(--r-btn);
  background: var(--surface-2); color: var(--text-2); font-size: var(--fs-12); font-weight: 500; white-space: nowrap; }
.rating-value { font-weight: 650; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Status dot + list rows
   ========================================================================== */
.dot {
  width: 8px; height: 8px;
  border-radius: var(--r-full);
  background: var(--text-3);
  flex-shrink: 0;
}
.dot-good { background: var(--good); }
.dot-warning { background: var(--warning); }
.dot-serious { background: var(--serious); }
.dot-critical { background: var(--critical); }
.dot-accent { background: var(--accent); }
.dot-info { background: var(--accent); }

.list { display: flex; flex-direction: column; }
.list-row {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s6);
  border-bottom: 1px solid var(--border);
  min-height: 64px;
  transition: background var(--t-fast) var(--ease);
}
.list-row:last-child { border-bottom: none; }
/* A row whose content runs to several lines: top-aligned and taller, but the
   horizontal inset never changes. Four screens used to set `padding: var(--s5)`
   inline to get the top alignment, which also pulled the inset in by 4px and
   left those rows visibly out of line with the card head above them. */
.list-row-block { align-items: flex-start; padding-block: var(--s5); }

/* A card given room to breathe: a feature panel rather than a list. Named,
   because it was being done with an inline padding override in three places
   and could drift a step at a time. */
.card-pad-lg { padding: var(--s7); }
a.list-row:hover, .list-row.clickable:hover, button.list-row:hover { background: var(--surface-2); }
.list-row.unread { background: var(--accent-soft); }
.list-row.unread:hover { background: var(--accent-soft-2); }

.stat { display: flex; flex-direction: column; gap: 4px; }
.stat-label { font-size: var(--fs-12); color: var(--text-2); font-weight: 550; }
.stat-value {
  font-size: var(--fs-30);
  font-weight: 680;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.stat-value-sm { font-size: var(--fs-20); letter-spacing: -.025em; }
.stat-note { font-size: var(--fs-12); color: var(--text-3); }

/* A tick in front of a list item. One accent token keeps every use aligned. */
.ok-mark { display: inline-flex; color: var(--accent); flex-shrink: 0; }
.no-mark { display: inline-flex; color: var(--text-3); flex-shrink: 0; }

/* ==========================================================================
   Empty states, skeletons, spinner
   ========================================================================== */
.empty {
  text-align: center;
  padding: var(--s10) var(--s5);
  max-width: 32rem;
  margin: 0 auto;
}
.empty .icon-tile { margin: 0 auto var(--s5); }
.empty-title { font-size: var(--fs-16); font-weight: 650; margin-bottom: var(--s2); letter-spacing: -.015em; }
.empty-body { font-size: var(--fs-14); color: var(--text-2); line-height: 1.6; }
.empty .btn { margin-top: var(--s6); }

.skeleton {
  background: var(--surface-2);
  animation: pulse 1.4s var(--ease) infinite;
  border-radius: var(--r-md);
  height: 12px;
}

.spinner {
  width: 16px; height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--r-full);
  animation: spin .6s linear infinite;
  opacity: .7;
}
.is-loading { opacity: .55; pointer-events: none; transition: opacity var(--t-fast) var(--ease); }

/* ==========================================================================
   Dialogs, sheets, menus, toasts
   ========================================================================== */
.overlay-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--scrim);
  display: grid;
  place-items: center;
  padding: var(--s5);
  animation: fade-in var(--t-fast) var(--ease) both;
  overflow-y: auto;
}
.dialog {
  width: 100%;
  max-width: 540px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
  animation: scale-in var(--t-med) var(--ease-out) both;
  max-height: calc(100vh - var(--s8));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.dialog-lg { max-width: 780px; }
.dialog-xl { max-width: 1020px; }
.dialog-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
  padding: var(--s6) var(--s6) var(--s4);
  flex-shrink: 0;
}
.dialog-head .grow { min-width: 0; }
.dialog-body { padding: 0 var(--s6) var(--s6); overflow-y: auto; flex: 1 1 auto; }
.dialog-foot {
  display: flex;
  gap: var(--s3);
  justify-content: flex-end;
  flex-wrap: wrap;
  padding: var(--s5) var(--s6);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  flex-shrink: 0;
}
/* A dialog that carries its own actions in the body, like the project
   wizard, passes no buttons. The footer was still drawing its padding, its
   border and its grey, which left a dead band across the bottom of the
   dialog under the buttons that were doing the work. */
.dialog-foot:empty { display: none; }

@media (max-width: 640px) {
  .overlay-scrim { padding: 0; align-items: flex-end; }
  .dialog {
    max-width: none;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    max-height: 92vh;
    animation: rise-in var(--t-med) var(--ease-out) both;
  }
  .dialog-foot { padding-bottom: calc(var(--s5) + env(safe-area-inset-bottom)); }
  .dialog-foot .btn { flex: 1 1 auto; }
}

.menu {
  position: absolute;
  z-index: 70;
  min-width: 210px;
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  animation: scale-in var(--t-fast) var(--ease-out) both;
}
.menu-item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) var(--s3);
  min-height: 38px;
  border-radius: var(--r-sm);
  font-size: var(--fs-13);
  font-weight: 500;
  color: var(--text);
  text-align: left;
  transition: background var(--t-fast) var(--ease);
}
.menu-item:hover { background: var(--surface-2); }
.menu-item svg { width: 16px; height: 16px; color: var(--text-3); flex-shrink: 0; }
.menu-item.danger { color: var(--critical-ink); }
.menu-item.danger svg { color: var(--critical); }
.menu-sep { height: 1px; background: var(--border); margin: var(--s2) 0; }
.menu-head { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s3); max-width: 300px; }
.menu-head-text { display: grid; gap: 1px; min-width: 0; line-height: 1.3; }
.menu-head-text strong { font-size: var(--fs-14); font-weight: 600; color: var(--text); }
.menu-head-text span {
  font-size: var(--fs-12); color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu-label {
  padding: var(--s2) var(--s3) 4px;
  font-size: var(--fs-11);
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.toast-stack {
  position: fixed;
  bottom: var(--s6);
  left: 50%;
  transform: translateX(-50%);
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  width: min(460px, calc(100vw - var(--s8)));
}
.toast {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s5);
  background: var(--brand);
  color: var(--brand-on);
  border-radius: var(--r-btn);
  box-shadow: var(--sh-3);
  font-size: var(--fs-13);
  animation: rise-in var(--t-med) var(--ease-out) both;
}
.toast .grow { min-width: 0; }
.toast button {
  color: inherit;
  font-weight: 650;
  font-size: var(--fs-12);
  text-decoration: underline;
  text-underline-offset: 2px;
  flex-shrink: 0;
}
.toast-good svg     { color: var(--ord-2); }
.toast-warning svg  { color: var(--warning-vivid); }
.toast-critical svg { color: var(--critical-vivid); }
@media (max-width: 860px) { .toast-stack { bottom: calc(var(--bottom-nav-h) + var(--s4)); } }

/* Command palette */
.palette {
  width: 100%;
  max-width: 600px;
  margin-top: 9vh;
  align-self: flex-start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
  overflow: hidden;
  animation: scale-in var(--t-med) var(--ease-out) both;
}
/* The palette is a panel, not a dialog: the field and the list run edge to
   edge and carry their own inset, so the body contributes none. */
.palette .dialog-body { padding: 0; }
.palette-empty { padding: var(--s4); }

.palette-input {
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  height: 60px;
  padding: 0 var(--s6);
  font-size: var(--fs-16);
  background: transparent;
}
.palette-input:hover { border-color: var(--border); }
.palette-input:focus {
  outline: none;
  box-shadow: none;
  border-color: var(--border);   /* .input:focus would turn this hairline blue */
}
.palette-list { max-height: 52vh; overflow-y: auto; padding: var(--s2); }
.palette-item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) var(--s4);
  border-radius: var(--r-md);
  text-align: left;
  font-size: var(--fs-14);
  min-height: 44px;
}
.palette-item[data-active="true"] { background: var(--accent-soft); color: var(--accent-ink); }
.palette-item svg { width: 17px; height: 17px; color: var(--text-3); flex-shrink: 0; }
.palette-item[data-active="true"] svg { color: var(--accent); }
.palette-item .hint { margin-left: auto; font-size: var(--fs-11); color: var(--text-3); }

/* ==========================================================================
   Touch targets

   WCAG 2.2 AA and the scope map both ask for at least 44px on anything you
   tap. Several controls sit below it: a 32px small button, a 34px chip, a
   38px menu row, and they are right at those sizes with a mouse.

   So the rule keys off the pointer, not the viewport. `pointer: coarse` is
   the actual condition: a control is too small because a thumb is blunt, not
   because a window is narrow. A phone in landscape gets the larger targets;
   a touchscreen laptop gets them too; a 900px browser window on a desktop
   does not need them and does not get them.
   ========================================================================== */
@media (pointer: coarse) {
  .btn, .btn-sm, .btn-icon,
  .menu-item, .tab, .chip,
  .rail-link, .palette-item { min-height: var(--tap-min); }
  .btn-icon { min-width: var(--tap-min); }
  .check { min-height: var(--tap-min); }
}

/* ==========================================================================
   Product-specific patterns
   ========================================================================== */

/* --- Profile head: the picture, the name, the line, the actions -------- */
.profile-head { display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap; margin-bottom: var(--s5); }
.profile-head-pic .logo-tile, .profile-head-pic .avatar { width: 88px; height: 88px; font-size: var(--fs-24); }
.profile-head-pic .logo-tile { border-radius: var(--r-logo-profile); }
/* An edit page: its sections, and the one save for all of them, kept in
   reach at the foot of the screen. */
.edit-section { scroll-margin-top: calc(var(--topbar-h) + var(--s4)); }
.save-bar { position: sticky; bottom: 0; z-index: 5; display: flex; justify-content: flex-end; gap: var(--s3);
  padding: var(--s4) 0; background: var(--bg); }
.save-bar.is-stuck { box-shadow: 0 -1px 0 var(--border); }
.edit-section:has(+ .save-bar.is-stuck),
.builder:has(+ .save-bar.is-stuck) > .card:last-child {
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
@media (max-width: 860px) { .save-bar { bottom: var(--bottom-nav-h); } }
.picture-pick { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.picture-pick .logo-tile, .picture-pick .avatar { width: 72px; height: 72px; font-size: var(--fs-20); }

/* --- Work: a member's photos and videos, with captions ------------------- */
.work-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--s4); }
.work-item { margin: 0; display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--border);
  border-radius: var(--r-lg); background: var(--surface); overflow: hidden; }
.work-media { position: relative; background: var(--surface-2); }
.work-item img, .work-item video, .work-missing { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border: 0; border-radius: 0; background: var(--surface-2); display: block; }
.work-item video { background: var(--brand); }
.work-missing { display: grid; place-items: center; padding: var(--s3); font-size: var(--fs-12); color: var(--text-3); text-align: center; }

/* A stage's photos, on inspection and from the payments list (30 Sep). */
.stage-photos { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s3); }
.stage-photos figure { margin: 0; width: 112px; }
.stage-photos img, .stage-photos .work-missing {
  width: 112px; height: 112px; object-fit: cover; display: block;
  border-radius: var(--r-sm); border: 1px solid var(--border); background: var(--surface-2);
}
.stage-photos figcaption { font-size: var(--fs-11); color: var(--text-3); margin-top: 4px; overflow-wrap: anywhere; }
.work-item figcaption { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3);
  padding: var(--s3); font-size: var(--fs-13); }
.work-caption { display: block; color: var(--text); font-weight: 600; line-height: 1.35; }
.work-kind { display: block; margin-top: 2px; color: var(--text-3); font-size: var(--fs-12); }
.work-badges { position: absolute; top: var(--s2); left: var(--s2); display: flex; gap: var(--s1); flex-wrap: wrap; }
.work-badge { display: inline-flex; align-items: center; min-height: 24px; padding: 0 9px; border-radius: var(--r-btn);
  background: var(--brand); color: var(--brand-on); font-size: var(--fs-11); font-weight: 650; }
.work-grid-featured .work-item.is-cover { grid-column: span 2; }
.work-grid-featured .work-item.is-cover img,
.work-grid-featured .work-item.is-cover video,
.work-grid-featured .work-item.is-cover .work-missing { aspect-ratio: 16 / 9; }
@media (max-width: 620px) {
  .work-grid { grid-template-columns: minmax(0, 1fr); }
  .work-grid-featured .work-item.is-cover { grid-column: auto; }
}
.cover-pick { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: var(--s3); }
.cover-pick label { position: relative; cursor: pointer; }
.cover-pick input { position: absolute; opacity: 0; }
.cover-pick img, .cover-pick video, .cover-pick .cover-none { width: 100%; aspect-ratio: 21 / 9; object-fit: cover; border-radius: var(--r-sm); border: 2px solid var(--border); display: block; }
.cover-pick .cover-none { display: grid; place-items: center; font-size: var(--fs-12); color: var(--text-3); background: var(--surface-2); }
.cover-pick input:checked + img, .cover-pick input:checked + video, .cover-pick input:checked + .cover-none { border-color: var(--accent); }
.cover-pick input:focus-visible + img, .cover-pick input:focus-visible + video, .cover-pick input:focus-visible + .cover-none { outline: 2px solid var(--accent); outline-offset: 2px; }
.profile-cover-plain { aspect-ratio: 21 / 6; background: var(--lp-tint, var(--surface-2)); }

/* --- Fold: what is over for a list, under it, opened in place ---------- */
.fold > summary { list-style: none; cursor: pointer; display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2);
  font-size: var(--fs-14); font-weight: 600; color: var(--text-2); padding: var(--s2) 0; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary:hover { color: var(--text); }
.fold-chev { display: inline-flex; transition: transform var(--t-fast) var(--ease); }
.fold[open] .fold-chev { transform: rotate(90deg); }
.fold-note { font-weight: 400; margin-left: var(--s2); }

/* --- Trust panel ---------------------------------------------------------- */
/* A read-only grid of facts, "what professionals can see about you". Related
   to the trust rows but not the same thing: nothing here opens, because there
   is nothing underneath to show. It kept the trust panel's class names until
   that panel was rebuilt around disclosure rows and took them with it. */
.fact-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; background: var(--border); }
.fact-cell { background: var(--surface); padding: var(--s5) var(--s6); display: flex; gap: var(--s4); }
.fact-cell .grow { min-width: 0; }
.fact-note { font-size: var(--fs-12); color: var(--text-3); margin-top: 3px; line-height: 1.45; }
@media (max-width: 640px) { .fact-grid { grid-template-columns: minmax(0, 1fr); } }

/* Trust & verification: one row per check, openable for what is held.
   Rows are separated by the same hairline the rest of the interface uses, and
   every inset is a space token so the panel keeps the card's own rhythm. */
.trust-list { display: flex; flex-direction: column; }
/* --- A question and its answer --------------------------------------------
   Deliberately not `.trust-row`. That one indents its detail past an icon
   tile, and there is no tile here, so the answer lines up under the question
   it belongs to.
   ------------------------------------------------------------------------- */
.faq-row { border-top: 1px solid var(--border); }
.faq-row:first-child { border-top: none; }
.faq-q {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s6);
  cursor: pointer;
  list-style: none;
  min-height: var(--tap-min);
  font-size: var(--fs-14);
  font-weight: 600;
  color: var(--text);
  transition: background var(--t-fast) var(--ease);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover { background: var(--surface-2); }
.faq-q .grow { min-width: 0; }
.faq-q svg { color: var(--text-3); flex-shrink: 0; transition: transform var(--t-fast) var(--ease); }
.faq-row[open] .faq-q svg { transform: rotate(180deg); }
.faq-a {
  padding: 0 var(--s6) var(--s5);
  font-size: var(--fs-13);
  color: var(--text-2);
  line-height: var(--lh-normal);
  max-width: 62ch;
}

.trust-row { border-top: 1px solid var(--border); }
.trust-row:first-child { border-top: none; }

.trust-summary {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s6);
  cursor: pointer;
  list-style: none;
  min-height: var(--tap-min);
  transition: background var(--t-fast) var(--ease);
}
.trust-summary::-webkit-details-marker { display: none; }
.trust-summary:hover { background: var(--surface-2); }
.trust-head { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.trust-label { font-size: var(--fs-12); color: var(--text-2); font-weight: 550; }
.trust-value { font-size: var(--fs-14); font-weight: 650; }
.is-pending .trust-value { color: var(--text-3); font-weight: 500; }

/* The chevron is the only moving part, and it turns to match the disclosure. */
.trust-summary > .ico:last-child {
  color: var(--text-3);
  transition: transform var(--t-fast) var(--ease);
}
.trust-row[open] .trust-summary > .ico:last-child { transform: rotate(180deg); }

.trust-detail {
  padding: 0 var(--s6) var(--s5);
  /* Aligned to the label, not the tile, so the detail reads as part of the
     row above it rather than a new list. */
  padding-left: calc(var(--s6) + 30px + var(--s4));
}
.trust-line {
  /* A grid, not a row pushed apart. "Pass or fail only, no document image is
     stored" is longer than the column it sits in, so as a right-aligned flex
     child it wrapped into a ragged block floating off the right edge. Two
     columns, both left-aligned: the label has a width, the value wraps into
     the space that is left, and the left edge stays straight. */
  display: grid;
  grid-template-columns: minmax(0, 96px) minmax(0, 1fr);
  gap: var(--s2) var(--s4);
  padding: var(--s2) 0;
  font-size: var(--fs-12);
  border-top: 1px solid var(--border);
}
.trust-line:first-child { border-top: none; padding-top: 0; }
.trust-line > :first-child { color: var(--text-3); }
.trust-line > :last-child { color: var(--text); min-width: 0; }

@media (max-width: 640px) {
  .trust-summary { padding-inline: var(--s4); }
  .trust-detail { padding-inline: var(--s4) var(--s4); padding-left: var(--s4); }
}

/* --- Stepper (project wizard, quote builder) ------------------------------ */
.stepper { display: flex; align-items: center; gap: var(--s2); overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.stepper::-webkit-scrollbar { display: none; }
.step {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s4) var(--s2) var(--s2);
  border-radius: var(--r-btn);
  font-size: var(--fs-12);
  font-weight: 600;
  color: var(--text-3);
  white-space: nowrap;
  flex-shrink: 0;
}
.step-num {
  width: 24px; height: 24px;
  border-radius: var(--r-full);
  border: 1.5px solid var(--border-strong);
  display: grid;
  place-items: center;
  font-size: var(--fs-11);
  font-weight: 700;
  flex-shrink: 0;
}
.step[data-state="current"] { color: var(--accent-ink); background: var(--accent-soft); }
.step[data-state="current"] .step-num { border-color: var(--accent); background: var(--accent); color: var(--accent-on); }
.step[data-state="done"] { color: var(--text-2); }
.step[data-state="done"] .step-num { border-color: var(--accent); background: var(--accent); color: var(--accent-on); }
.step-sep { width: 16px; height: 2px; border-radius: var(--r-full); background: var(--border-strong); flex-shrink: 0; }

/* --- Timeline / activity -------------------------------------------------- */
.timeline { position: relative; padding-left: var(--s6); }
.timeline::before {
  content: "";
  position: absolute;
  left: 9px; top: 8px; bottom: 8px;
  width: 2px;
  border-radius: var(--r-full);
  background: var(--border);
}
.timeline-item { position: relative; padding-bottom: var(--s5); }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-item::before {
  content: "";
  position: absolute;
  left: calc(var(--s6) * -1 + 5px);
  top: 5px;
  width: 10px; height: 10px;
  border-radius: var(--r-full);
  background: var(--border-strong);
  box-shadow: 0 0 0 3px var(--surface);
}
.timeline-item.is-current::before { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.timeline-item.is-done::before { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* --- Messaging ------------------------------------------------------------ */
.thread { display: flex; flex-direction: column; gap: var(--s4); }
.bubble-row { display: flex; gap: var(--s3); align-items: flex-end; }
.bubble-row.mine { flex-direction: row-reverse; }
.bubble {
  max-width: min(74%, 480px);
  padding: var(--s4) var(--s5);
  border-radius: var(--r-lg);
  border-bottom-left-radius: var(--s2);
  font-size: var(--fs-14);
  line-height: 1.55;
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.bubble-row.mine .bubble {
  background: var(--brand);
  color: var(--brand-on);
  border-color: var(--brand);
  border-bottom-left-radius: var(--r-lg);
  border-bottom-right-radius: var(--s2);
}
.bubble-time { font-size: var(--fs-11); color: var(--text-3); margin-top: 4px; }
.bubble-row.mine .bubble-time { text-align: right; }
.bubble-masked {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--r-btn);
  background: var(--warning-soft);
  color: var(--warning-ink);
  font-size: var(--fs-12);
  font-weight: 650;
}

/* --- Portfolio ------------------------------------------------------------ */
.portfolio {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--s3);
}
.portfolio-item {
  aspect-ratio: 4 / 3;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  overflow: hidden;
  position: relative;
  background: var(--surface-2);
}
.portfolio-item .ph { width: 100%; height: 100%; display: block; }
.portfolio-item figcaption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: var(--s2) var(--s3);
  font-size: var(--fs-11);
  font-weight: 550;
  color: var(--brand-on);
  background: var(--media-label-bg);
}

/* --- Media -----------------------------------------------------------------
   An image slot with its aspect ratio locked. Until there is photography it
   renders as a clearly-labelled placeholder rather than a collapsed box, so
   the layout is already correct when the real picture arrives. */
.media {
  position: relative;
  border-radius: var(--r-lg);
  background: var(--surface-3);
  border: 1px solid var(--border);
  overflow: hidden;
  display: grid;
  place-items: center;
  color: var(--text-3);
}
.media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-1x1  { aspect-ratio: 1 / 1; }
.media-4x3  { aspect-ratio: 4 / 3; }
.media-3x2  { aspect-ratio: 3 / 2; }
.media-16x9 { aspect-ratio: 16 / 9; }
.media-21x9 { aspect-ratio: 21 / 9; }
/* A cover band. 21:9 is the ratio a member uploads at; it is not a licence to
   take 600px of a 1400px-wide page, which is what the bare ratio did, the
   company name sat below a screenful of empty grey. The band is capped at the
   height a real cover image occupies and the image crops into it. */
.media-cover {
  aspect-ratio: auto;
  height: clamp(132px, 15vw, 208px);
}
.media-flush { border-radius: 0; border: none; }
.media-note {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-size: var(--fs-12);
  font-weight: 600;
  letter-spacing: .01em;
  padding: var(--s4);
  max-width: 100%;
}
.media-note svg { flex-shrink: 0; opacity: .7; }
.media-note b { font-weight: 600; display: block; }
.media-note span {
  display: block;
  font-weight: 450;
  color: var(--text-3);
  font-size: var(--fs-11);
  margin-top: 1px;
}
/* A square slot is too narrow for a row: stack it there, and only there. */
.media-1x1 .media-note { flex-direction: column; text-align: center; gap: var(--s2); }
.media-caption {
  position: absolute;
  right: var(--s3);
  bottom: var(--s3);
  max-width: calc(100% - var(--s6));
  padding: 3px var(--s3);
  border-radius: var(--r-btn);
  background: var(--brand);
  color: var(--brand-on);
  font-size: var(--fs-11);
  font-weight: 600;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Banner / callout ----------------------------------------------------- */
.banner {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
  padding: var(--s5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  font-size: var(--fs-13);
  line-height: 1.55;
}
.banner .grow { min-width: 0; }
.banner > svg { flex-shrink: 0; margin-top: 1px; }

/* A banner stacked between two blocks needs its own gap, and used to have
   none: twenty-two views were writing an empty 20px div after one to push
   the next thing off it.

   Scoped to the three containers where banners are block siblings. Inside a
   `.col`, a `.grid` or a dialog body the gap is already the container's job,
   and a margin there would add to it. */
.page > * + .banner,
.section > * + .banner,
[data-tab-body] > * + .banner { margin-top: var(--s5); }

.page > .banner,
.section > .banner,
[data-tab-body] > .banner { margin-bottom: var(--s5); }
/* Neutral is the base banner: grey surface, hairline border, body ink. It is
   named explicitly rather than left to inherit, so a plain `banner` and a
   deliberately quiet `banner banner-neutral` cannot drift apart. */
.banner-neutral  { background: var(--surface-2);     border-color: var(--border);                                        color: var(--text-2); }
.banner-neutral .banner-title { color: var(--text); }
.banner-good     { background: var(--good-soft);     border-color: color-mix(in srgb, var(--good) 24%, transparent);     color: var(--good-ink); }
.banner-accent   { background: var(--accent-soft);   border-color: var(--accent-soft-2);                                 color: var(--accent-ink); }
.banner-info     { background: var(--accent-soft);   border-color: var(--accent-soft-2);                                 color: var(--accent-ink); }
.banner-warning  { background: var(--warning-soft);  border-color: color-mix(in srgb, var(--warning) 30%, transparent);  color: var(--warning-ink); }
.banner-serious  { background: var(--serious-soft);  border-color: color-mix(in srgb, var(--serious) 28%, transparent);  color: var(--serious-ink); }
.banner-critical { background: var(--critical-soft); border-color: color-mix(in srgb, var(--critical) 26%, transparent); color: var(--critical-ink); }
.banner-title { font-weight: 700; margin-bottom: 3px; }
.banner .btn { flex-shrink: 0; }

@media (max-width: 640px) {
  .banner { flex-wrap: wrap; }
  .banner > .grow { flex: 1 1 calc(100% - 32px); }
  .banner > .btn,
  .banner > .row:last-child {
    margin-left: calc(16px + var(--s4));
  }
  .banner > .row:last-child { flex: 1 1 calc(100% - 32px); flex-wrap: wrap; }
}

/* ==========================================================================
   Issued documents

   A deposit invoice, a payment schedule and a sign-off certificate are not
   screens, they are pieces of paper that happen to be shown on one. So they
   get the one place in the product that looks like paper: a white sheet, a
   ruled head, and nothing that could not be printed.
   ========================================================================== */
.doc-sheet {
  padding: var(--s5);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
}
.doc-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
  padding-bottom: var(--s4);
  border-bottom: 2px solid var(--brand);
}
.doc-title {
  font-family: var(--display);
  font-size: var(--fs-20);
  font-weight: 700;
  letter-spacing: var(--display-tr);
  color: var(--text);
}
.doc-sheet table.data { margin-top: var(--s4); }
.doc-sheet table.data td { padding: var(--s3) 0; }
.doc-sheet table.data th { background: transparent; padding: var(--s2) 0; }
.doc-sheet table.data tfoot td { background: transparent; padding-top: var(--s3); }

/* The programme legend. It cannot reuse .prog-bar, which is positioned
   inside a track and would collapse to nothing out here. */
.prog-key { display: inline-block; width: 12px; height: 12px; border-radius: var(--r-data); }
.prog-key.is-paid      { background: var(--good); }
.prog-key.is-signed    { background: var(--ord-4); }
.prog-key.is-waiting   { background: var(--warning); }
.prog-key.is-running   { background: var(--accent); }
.prog-key.is-scheduled { background: var(--ord-1); }

/* --- Picking an image ------------------------------------------------------
   A radio group that looks like the thing it is choosing. The input stays in
   the DOM for the keyboard and for form reading, and the tile shows the
   state instead of it. */
.pick-tile { position: relative; display: block; cursor: pointer; }
.pick-tile input { position: absolute; opacity: 0; pointer-events: none; }
.pick-tile img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 2px solid var(--border);
  transition: border-color var(--t-fast) var(--ease);
}
.pick-tile:hover img { border-color: var(--border-strong); }
.pick-tile input:checked + img { border-color: var(--accent); }
.pick-tile input:focus-visible + img { box-shadow: 0 0 0 3px var(--ring); }

/* --- A screened document ---------------------------------------------------
   The preview shows the copy the other party sees, with the removed lines
   blacked out. Showing the original here would quietly contradict the
   promise the upload dialog made. */
.doc-body { display: flex; flex-direction: column; gap: 10px; padding: var(--s5) 0; }

/* The document viewer: the real file, or a marked sample */
.docview { border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; background: var(--surface-2); }
.docview-frame { position: relative; min-height: 180px; display: flex; align-items: center; justify-content: center; padding: var(--s4); }
.docview-frame img { max-width: 100%; max-height: 420px; border-radius: var(--r-sm); display: block; }
.docview-frame iframe { width: 100%; height: 480px; border: 0; background: var(--surface); }
.docview-frame .doc-sheet { width: 100%; }
.docview-foot { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) var(--s4); border-top: 1px solid var(--border); background: var(--surface); }
.sample-sheet, .sample-id, .sample-selfie { position: relative; }
.sample-badge { position: absolute; top: 10px; right: 10px; font-size: var(--fs-11); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-2); border: 1px solid var(--border-strong); border-radius: var(--r-btn); padding: 2px 8px; background: var(--surface); }
.sample-id { display: flex; gap: var(--s4); align-items: center; width: 100%; max-width: 420px; padding: var(--s4);
  border: 1px solid var(--border-strong); border-radius: var(--r-sample); background: var(--surface); }
.sample-id-photo svg { width: 80px; height: 96px; display: block; }
.sample-selfie { display: flex; flex-direction: column; align-items: center; gap: var(--s2); padding: var(--s4); }
.sample-selfie-face svg { width: 120px; height: 144px; display: block; border-radius: 50% / 45%; }
.doc-line { display: block; height: 9px; border-radius: var(--r-data); background: var(--surface-3); }


/* --- Trade picker, project wizard -------------------------------------------
   A tick per trade rather than a dropdown, because a job is usually more
   than one trade and a dropdown says it is exactly one. */
.trade-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--s2);
}
.check-inline {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  cursor: pointer;
}
.check-inline:hover { border-color: var(--border-strong); }
.check-inline:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.check-inline input { flex-shrink: 0; }

/* --- Content manager --------------------------------------------------------
   One engine edits every managed type, so these are generic on purpose. A
   nested list is a collapsible block at the first level and a compact row
   at the second, which is the difference between a work template fitting on
   one screen and running to seven. Every gap is on the spacing scale. */
.cms-group { margin-top: var(--s6); }
.cms-group:first-of-type { margin-top: 0; }

.card-link { display: block; color: inherit; text-decoration: none;
             transition: border-color var(--t-fast) var(--ease); }
.card-link:hover { border-color: var(--border-strong); }
.cms-type { padding: var(--s5) var(--s6); }

.cms-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--s5);
  align-items: start;
  margin-top: var(--s5);
}
.cms-aside { display: flex; flex-direction: column; gap: var(--s4);
             position: sticky; top: calc(var(--topbar-h, 64px) + var(--s5)); }
@media (max-width: 1100px) {
  .cms-layout { grid-template-columns: minmax(0, 1fr); }
  .cms-aside { position: static; }
}

/* The form keeps its own rhythm, so a field never sits flush on the next. */
.cms-form { display: flex; flex-direction: column; gap: var(--s6); }

/* The clause as it will print: typography only, no box inside the box. */
.clause-preview { font-size: var(--fs-14); line-height: 1.7; color: var(--text-2); margin: 0; }
.clause-preview b { color: var(--text); font-weight: 650; }

.pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
             gap: var(--s2); }
.pick-grid .check-inline { padding: var(--s2) var(--s3); }
.pick-grid .check-title { font-size: var(--fs-13); }

/* --- nested lists ---------------------------------------------------------- */
.nest-list-head { display: flex; align-items: flex-start; gap: var(--s4); }
.nest-list-head .field-label .muted-2 { font-weight: 500; margin-left: var(--s1); }
.nest { display: flex; flex-direction: column; gap: var(--s3); margin-top: var(--s3); }
.nest-empty { margin: var(--s2) 0 0; }

.nest-n { width: 22px; height: 22px; border-radius: var(--r-full); background: var(--brand);
          color: var(--brand-on); display: grid; place-items: center;
          font-size: var(--fs-11); font-weight: 650; flex-shrink: 0; }
.nest-n-sm { width: 20px; height: 20px; background: var(--surface-2); color: var(--text-2);
             border: 1px solid var(--border); font-size: 10px; }

.icon-btn { width: 28px; height: 28px; border-radius: var(--r-sm); border: 1px solid transparent;
            background: var(--surface-2); color: var(--text-2); display: grid; place-items: center;
            cursor: pointer; flex-shrink: 0; }
.icon-btn:hover { background: var(--surface-3); color: var(--text); }
.nest-tools { display: flex; gap: 2px; flex-shrink: 0; }

/* First level: a collapsible block. */
.nest-block { border: 1px solid var(--border); border-radius: var(--r-md);
              background: var(--surface); overflow: hidden; }
.nest-block > summary { list-style: none; cursor: pointer; }
.nest-block > summary::-webkit-details-marker { display: none; }
.nest-head { display: flex; align-items: center; gap: var(--s3);
             padding: var(--s3) var(--s4); }
.nest-block[open] > .nest-head { border-bottom: 1px solid var(--border); background: var(--surface-2); }
.nest-title { display: flex; align-items: baseline; gap: var(--s2); min-width: 0;
              font-size: var(--fs-14); font-weight: 600; }
.nest-chev { color: var(--text-3); display: grid; transition: transform var(--t-fast) var(--ease); }
.nest-block[open] > summary .nest-chev { transform: rotate(180deg); }
.nest-body { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s5); }
.nest-fields { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4); }
@media (max-width: 640px) { .nest-fields { grid-template-columns: minmax(0, 1fr); } }

/* Second level: compact rows, the labels said once in a header. */
.nest-depth-2 .nest { gap: 0; margin-top: var(--s2);
                      border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; }
.nest-rows-head { display: grid; grid-template-columns: 20px minmax(0, 1fr) 160px 92px;
                  gap: var(--s3); padding: var(--s2) var(--s4);
                  background: var(--surface-2); border-bottom: 1px solid var(--border);
                  font-size: var(--fs-11); font-weight: 600; letter-spacing: .04em;
                  text-transform: uppercase; color: var(--text-3); }
.nest-line { display: flex; flex-direction: column; gap: var(--s2);
             padding: var(--s3) var(--s4); border-top: 1px solid var(--border); }
.nest-line:first-of-type, .nest-rows-head + .nest-line { border-top: 0; }
.nest-line-top { display: grid; grid-template-columns: 20px minmax(0, 1fr) 160px 92px;
                 gap: var(--s3); align-items: center; }
.nest-line > .field-compact { margin-left: calc(20px + var(--s3)); }
.field-compact { gap: 0; }
.field-compact .input, .field-compact .select { height: 36px; }
.field-compact .textarea { min-height: 0; font-size: var(--fs-13); }
@media (max-width: 860px) {
  .nest-rows-head { display: none; }
  .nest-line-top { grid-template-columns: 20px minmax(0, 1fr); }
  .nest-line-top > .field-compact:nth-of-type(2) { grid-column: 2; }
  .nest-line-top > .nest-tools { grid-column: 2; justify-self: end; }
  .nest-line > .field-compact { margin-left: 0; }
}

.w-full { width: 100%; }

/* The demo fill sits away from the confirm action, so nobody reaches for it
   by accident on the way to Save. */
.dialog-foot.has-fill [data-demo-fill] { margin-right: auto; order: -1; }

/* Read only means read only: the reference on a record that contracts
   already point at cannot change, and it should not look as though it can. */
.input[readonly], .textarea[readonly] {
  background: var(--surface-2);
  color: var(--text-2);
  cursor: default;
}
.input[readonly]:focus { box-shadow: none; border-color: var(--border); }

/* The posting page: its actions share the footer row with Fill for me.
   The steps stand beside the form as a list (29 Sep): seven no longer fit
   across it. On a narrow screen they are one line with a progress bar. */
.project-builder { max-width: 1120px; }
.project-builder-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--s6); align-items: start; }
.wizard-steps { position: sticky; top: calc(var(--topbar-h, 64px) + var(--s5)); }
.wizard-steps-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s1); }
.wizard-steps-list .step { width: 100%; padding: var(--s2) var(--s3) var(--s2) var(--s2); font-size: var(--fs-13); text-align: left; }
.wizard-steps-list button.step:hover { background: var(--surface-2); }
.wizard-steps-compact { display: none; }
.wizard-progress { flex-basis: 100%; height: 4px; border-radius: var(--r-full); background: var(--border); overflow: hidden; }
.wizard-progress span { display: block; height: 100%; border-radius: inherit; background: var(--accent); }
@media (max-width: 960px) {
  .project-builder-layout { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .wizard-steps { position: static; }
  .wizard-steps-list { display: none; }
  .wizard-steps-compact { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s2); }
}
.project-builder-card { overflow: hidden; }
.project-builder-body { padding: var(--s6); min-height: 420px; }
.project-builder-foot {
  display: flex; justify-content: flex-end; padding: var(--s5) var(--s6);
  border-top: 1px solid var(--border); background: var(--surface-2);
}
.project-builder-foot [data-wizard-actions] { width: 100%; justify-content: flex-end; flex-wrap: wrap; }
.project-builder-foot [data-demo-fill] { margin-right: auto; }
.part-row { display: grid; grid-template-columns: minmax(0, 1fr) 150px; gap: var(--s3); align-items: start; }
.file-drop {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
  padding: var(--s5); border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background: var(--surface-2);
}
.file-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3); border: 1px solid var(--border); border-radius: var(--r-md);
}
@media (max-width: 640px) {
  .part-row { grid-template-columns: minmax(0, 1fr); }
  .project-builder-body { padding: var(--s5); min-height: 0; }
  .project-builder-foot { padding: var(--s4) var(--s5); }
  .project-builder-foot [data-wizard-actions] .btn { flex: 1 1 auto; }
  .project-builder-foot [data-demo-fill] { flex-basis: 100%; margin-right: 0; }
}

/* The separators give way first, so every step fits the dialog. */
.stepper .step-sep { flex: 1 1 8px; min-width: 6px; width: auto; }
.stepper .step { padding-right: var(--s3); }

/* A chip that is a checkbox: the label and the control are one target. */
.chip-check { position: relative; height: 30px; padding: 0 var(--s3); }
.chip-check input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.chip-check:has(input:checked) { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
/* An area already carrying the most suppliers of one supply item. */
.chip-check.is-full { opacity: .45; cursor: not-allowed; text-decoration: line-through; }
.chip-check:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--ring); }

/* Postcode areas, grouped by region, in one scrolling box. */
.area-pick { max-height: 240px; overflow-y: auto; padding: var(--s3); border: 1px solid var(--border);
  border-radius: var(--r-md); display: flex; flex-direction: column; gap: var(--s3); }

/* --- The membership ID card (26 Sep): a real card's shape, 85.6 by 54 mm -- */
.id-card-pair { display: flex; flex-wrap: wrap; gap: var(--s5); }
.id-card { width: 100%; max-width: 428px; aspect-ratio: 85.6 / 54; border-radius: 16px; padding: 18px 20px;
  display: flex; flex-direction: column; gap: 12px; border: 1px solid var(--border); background: var(--surface); overflow: hidden; }
.id-card-front {
  --brand-mark-accent: var(--accent-on-dark);
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-on);
}
.id-card-top { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.id-card-brand { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; font-size: var(--fs-13); }
.id-card-brand .brand { pointer-events: none; }
.id-card-brand .brand-mark svg { height: 20px; }
.id-card-kind { font-size: var(--fs-11); letter-spacing: .06em; text-transform: uppercase; }
.id-card-body { display: flex; gap: 16px; flex: 1; min-height: 0; }
.id-card-photo { flex: 0 0 auto; width: 30%; aspect-ratio: 3 / 4; border-radius: 10px; overflow: hidden;
  background: var(--brand-on-wash); display: flex; align-items: center; justify-content: center; }
.id-card-photo img { width: 100%; height: 100%; object-fit: cover; }
.id-card-initials { font-size: var(--fs-24); font-weight: 600; }
.id-card-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.id-card-name { font-size: var(--fs-18); font-weight: 600; line-height: 1.2; }
.id-card-biz { font-size: var(--fs-13); }
.id-card-dl { margin: 10px 0 0; display: flex; flex-direction: column; gap: 6px; }
.id-card-dl dt { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; opacity: .8; }
.id-card-dl dd { margin: 0; font-size: var(--fs-13); font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 6px; }
.id-card-dl dd.is-muted, .id-card-dl .is-muted { opacity: .7; }
.id-card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; margin-top: 0; }
.id-card-back .id-card-dl dt { color: var(--text-3); opacity: 1; }
.id-card-pair.is-scaled .id-card { width: 428px; max-width: none; }
.id-card-back-top { display: flex; gap: 16px; align-items: flex-start; }
.id-card-back-top .id-card-grid { flex: 1; min-width: 0; }
.id-card-scan { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 9px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-3); }
.id-card-scan .qr { width: 84px; height: 84px; }
.qr svg { display: block; width: 100%; height: 100%; }
.id-card-verify { display: grid; gap: 2px; padding: 7px 10px; border-radius: 10px;
  background: var(--surface-2); font-size: var(--fs-12); line-height: 1.35; margin-top: auto; }
.id-card-verify-line { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 12px; min-width: 0; }
.id-card-verify-line span { overflow-wrap: anywhere; }
.id-card-verify strong { font-size: var(--fs-14); font-variant-numeric: tabular-nums; }
.id-card-link { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3);
  max-width: 428px; padding-top: var(--s4); border-top: 1px solid var(--border); }
.id-card-verify > span:first-child { color: var(--text-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.id-card-note { font-size: 10px; color: var(--text-3); line-height: 1.35; }
.cscs-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: 0 0 auto; box-shadow: 0 0 0 1.5px var(--brand-on); }
.cscs-dot[data-card="green"] { background: var(--cscs-green); }
.cscs-dot[data-card="blue"] { background: var(--cscs-blue); }
.cscs-dot[data-card="black"] { background: var(--cscs-black); }
.cscs-dot[data-card="gold"] { background: var(--cscs-gold); }
.cscs-dot[data-card="red"] { background: var(--cscs-red); }
@media print {
  /* The screen card, scaled to a real card's 85.6 by 54 mm: the same
     drawing, nothing cut; the QR prints at about 17 mm. */
  .id-card-print .id-card { width: 428px; height: 270px; max-width: none; aspect-ratio: auto; zoom: .7559;
    -webkit-print-color-adjust: exact; print-color-adjust: exact; break-inside: avoid; }
  .id-card-print .id-card-pair { gap: 8mm; }
}
