/* adicot.com — engineering calculators
   No build step, no framework. Tokens first, then layout, then components. */

:root {
  --bg: #ffffff;
  --surface: #f7f5f2;
  --fg: #1d1b19;
  --muted: #6b645c;
  --line: #e6e1da;
  /* Form control borders need 3:1 against the field, per WCAG 1.4.11. --line is
     1.30:1 on white, which is why the inputs were disappearing into the page.
     This is 3.69:1 on --bg and 3.39:1 on --surface. */
  --field-line: #8c847a;
  /* Brand orange, sampled from the master logo. Decorative only — it is 2.78:1
     on white, so it must never carry text or be the sole indicator of state. */
  --brand: #ef7c00;
  /* Interactive color: the brand hue at full saturation, darkened (not dulled
     toward brown) just enough to clear WCAG AA on white, on --surface and on
     --accent-soft. Run check-contrast.mjs if you change it. */
  --accent: #bf4d00;
  --accent-fg: #ffffff;
  --accent-soft: #fef4ea;
  /* The Related Calculators box, tinted so it reads as separate from the results
     panel (--surface). --accent clears AA on it, as on --accent-soft. */
  --surface-alt: #fef4ea;
  /* Filled buttons use the bright brand orange with near-black bold text
     (6.77:1), which reads cleaner than white on the darkened --accent. Links cannot do
     this: brand orange as text on white is only 2.78:1. */
  --btn: #ef7c00;
  --btn-hover: #ff8f1f;
  --btn-fg: #14110e;
  /* Fill for a required field that is still empty, so what is left to enter
     stands out from what has been entered. Text 15.5:1, border 3.33:1. */
  --need: #fff3d1;
  --warn: #8a5200;
  --maxw: 1400px;
  --prose: 92ch;
  --radius: 10px;
  color-scheme: light;
}


*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  overflow-wrap: break-word;
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--btn); color: var(--btn-fg); padding: 10px 16px; border-radius: 0 0 var(--radius) 0;
}
.skip:focus { left: 0; }

.muted { color: var(--muted); }

/* ---------- header ---------- */

.site-header { border-bottom: 1px solid var(--line); background: var(--bg); }

.bar {
  max-width: var(--maxw); margin: 0 auto; padding: 14px 16px;
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
.brand-logo {
  display: block; height: 52px; width: auto;
  /* the master art carries its own white plate, so give it a tidy edge */
  background: #fff; border-radius: 7px;
}
/* A little bigger where the full nav fits beside it. */
@media (min-width: 901px) { .brand-logo { height: 64px; } }

.navtoggle, .navbtn { display: none; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- search ---------- */

.sitesearch { position: relative; margin-left: auto; flex: 0 1 200px; min-width: 150px; }
.sitesearch input[type="search"] {
  width: 100%; font: inherit; font-size: 14px;
  padding: 7px 12px; border: 1px solid var(--field-line); border-radius: 8px;
  background: var(--bg); color: var(--fg); -webkit-appearance: none; appearance: none;
}
.sitesearch input::placeholder { color: var(--muted); }
.sitesearch input:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}

.searchresults {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; right: 0;
  list-style: none; margin: 0; padding: 4px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(16,25,35,.14);
  max-height: 62vh; overflow-y: auto;
}
.searchresults li { margin: 0; }
.searchresults a {
  display: flex; align-items: baseline; gap: 8px;
  padding: 8px 10px; border-radius: 7px;
  text-decoration: none; color: var(--fg); font-size: 14px;
}
.searchresults a:hover,
.searchresults li[aria-selected="true"] a { background: var(--accent-soft); color: var(--accent); }
.searchresults strong { font-weight: 600; }
.sr-kind { margin-left: auto; font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.searchresults li[aria-selected="true"] .sr-kind { color: var(--accent); }
.sr-none { padding: 10px; font-size: 14px; color: var(--muted); }

.mainnav { margin-left: 12px; }
.mainnav ul { list-style: none; display: flex; gap: 4px; margin: 0; padding: 0; flex-wrap: wrap; }
.mainnav a {
  display: block; padding: 7px 9px; border-radius: 7px;
  text-decoration: none; color: var(--fg); font-size: 14px; font-weight: 500;
}
.mainnav a:hover { background: var(--surface); }
.mainnav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); }

/* ---------- layout ---------- */

main { max-width: var(--maxw); margin: 0 auto; padding: 32px 16px 64px; }

.page-head { margin-bottom: 24px; }
.page-head h1 { font-size: clamp(26px, 4.2vw, 40px); line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 6px; }
.subtitle, .meta, .crumb { color: var(--muted); margin: 0; font-size: 15px; }
.crumb { margin-bottom: 10px; font-size: 14px; }

.prose { max-width: var(--prose); }
.prose h2 { font-size: 23px; letter-spacing: -0.01em; margin: 34px 0 10px; }
.prose h3 { font-size: 19px; margin: 26px 0 8px; }
.prose h4, .prose h5, .prose h6 { font-size: 16px; margin: 20px 0 6px; }
.prose p { margin: 0 0 14px; }
.prose ul, .prose ol { margin: 0 0 16px; padding-left: 22px; }
.prose li { margin: 4px 0; }
.prose img { max-width: 100%; height: auto; border-radius: var(--radius); }
/* YouTube videos in blog posts: 16:9, full width up to a readable size. */
.video-embed { aspect-ratio: 16 / 9; width: 100%; max-width: 720px; margin: 1.5rem 0; }
.video-embed iframe { width: 100%; height: 100%; border: 0; border-radius: var(--radius); }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 28px 0; }
.prose blockquote {
  margin: 16px 0; padding: 2px 0 2px 16px;
  border-left: 3px solid var(--line); color: var(--muted);
}
.prose code {
  font: 0.92em ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  background: var(--surface); padding: 1px 5px; border-radius: 5px;
}
.prose pre {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px; overflow-x: auto;
}
.prose pre code { background: none; padding: 0; }
.prose table { border-collapse: collapse; width: 100%; margin: 16px 0; font-size: 15px; }
.prose th, .prose td { border: 1px solid var(--line); padding: 7px 10px; text-align: left; }
.prose th { background: var(--surface); }
/* A wide reference table scrolls on its own on a phone, never the page. */
.table-scroll { overflow-x: auto; }
.table-scroll > table { min-width: 34rem; }
/* In a narrow results panel a table uses the width it has, in a smaller type. */
.cf-results .table-scroll > table { min-width: 0; font-size: 13.5px; }
.cf-results .ref-table :is(th, td) { padding: 5px 6px; }
.ref-table td { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ref-table thead th:not(:first-child) { text-align: right; }

/* ---------- calculator embed ---------- */

.calc {
  margin: 0 0 30px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;            /* SpreadsheetConverter output is always light */
  padding: 8px;
  overflow-x: auto;
}
.calc iframe {
  display: block; border: 0; max-width: 100%;
  /* width/height come from the original embed dimensions */
}
.calc-meta { margin: 8px 4px 2px; font-size: 13.5px; }

/* ---------- hand-written calculator: duct size ---------- */

/* No white box and no fixed pixel size, unlike the SpreadsheetConverter embeds
   above: this one is ours, so it takes the page's own colours and reflows. */
.calc-inline { margin: 0 0 30px; }
/* In the flex row it would otherwise shrink to its content and leave the rest of
   the column empty. It grows into everything the related sidebar does not use,
   and the sidebar drops underneath once there is less than 640px for it. */
.calc-layout > .calc-inline { flex: 1 1 640px; min-width: 0; }

.calcform {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
@media (min-width: 720px) {
  /* Inputs give up width before the results do, so the results never get squeezed. */
  .calcform { grid-template-columns: minmax(16rem, 26rem) minmax(18rem, 1fr); gap: 40px; }
}

.cf-noscript {
  margin: 0 0 18px; padding: 12px 14px;
  border: 1px solid var(--field-line); border-radius: var(--radius);
  background: var(--surface); font-size: 14.5px;
}

/* ---- inputs ---- */
.cf-inputs { margin: 0; }
.cf-inputs .field { margin: 0 0 16px; }
.cf-inputs label { font-size: 14.5px; font-weight: 600; }
.cf-inputs .field > label { display: block; margin: 0 0 4px; }

/* The unit belongs to the label, but it is not the name of the field, so it is
   set apart from it rather than read as part of it. */
.cf-u { font-weight: 400; color: var(--muted); }
.cf-u::before { content: "("; }
.cf-u::after { content: ")"; }

.calcform input[type="number"], .calcform input[type="text"],
.calcform select {
  width: 100%; font: inherit; font-size: 15px;
  padding: 7px 10px; border: 1px solid var(--field-line); border-radius: 8px;
  background: var(--bg); color: var(--fg);
  -webkit-appearance: none; appearance: none;
}
.calcform select {
  /* restore the dropdown arrow that appearance:none removes */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 34px;
}
.calcform :is(input, select):focus-visible {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}
/* Required and still empty: shaded until it is filled in. background-color, not
   the shorthand, so the select keeps its drawn arrow. */
.calcform :is(input[type="number"], select):required:invalid { background-color: var(--need); }
.calcform input::placeholder { color: var(--muted); font-style: italic; }  /* italic, so a hint is not mistaken for a value already entered */

.cf-radios {
  margin: 0 0 16px; padding: 0; border: 0;
  display: flex; flex-wrap: wrap; gap: 4px 16px;
}
.cf-radios legend {
  padding: 0; margin: 0 0 6px; font-size: 14.5px; font-weight: 600;
  /* a legend is not a flex item, so it needs the full row to itself */
  float: left; width: 100%;
}
.cf-radios label {
  display: flex; align-items: center; gap: 7px;
  font-weight: 400; font-size: 14.5px; cursor: pointer;
}
.cf-radios input[type="radio"] { accent-color: var(--accent); width: 16px; height: 16px; margin: 0; }
.cf-stack { flex-direction: column; gap: 6px; }

/* Finger-sized rows on touch, where a 16px radio is not a target. */
@media (pointer: coarse) {
  .cf-radios label { min-height: 44px; }
  .calcform input[type="number"], .calcform select { padding-block: 12px; }
}

.cf-actions { margin: 14px 0 0; }
.cf-actions .btn { cursor: pointer; font: inherit; font-weight: 700; font-size: 15px; }
.dc-spec { display: inline-block; margin: 6px 0 0; font-size: 13.5px; }

/* Short fields pack two or three to a row instead of one per line (two on a
   phone), so a calculator needs far less scrolling. Everything that is not a
   plain field (radio groups, headings, paired fields, hints, buttons) keeps the
   full width, as does a field that carries its own hint or a searchable list.
   Fields line up on their inputs, so a label that wraps does not push its
   neighbour's box out of line. On a wide screen inputs and results share the
   width equally. */
:is(.cf-inputs, .cf-inputs .cf-stream) {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 12px 14px; align-items: end;
}
@media (min-width: 720px) {
  .calcform { grid-template-columns: minmax(20rem, 1fr) minmax(20rem, 1fr); gap: 28px; }
  :is(.cf-inputs, .cf-inputs .cf-stream) { grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: 12px 16px; }
}
@media all {
  :is(.cf-inputs, .cf-inputs .cf-stream) > :not(.field),
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:has(.hint, textarea, [role="combobox"], select.cf-wide) { grid-column: 1 / -1; }
  :is(.cf-inputs, .cf-inputs .cf-stream) > :is(.field, .cf-radios, .cf-stream, .cf-actions) { margin: 0; }
  :is(.cf-inputs, .cf-inputs .cf-stream) > .cf-pair > .field { margin: 0; }
  .cf-inputs .cf-stream > legend { margin-bottom: 6px; }
  .cf-inputs > .cf-stream:not(:first-child) { margin-top: 8px; }
  /* Pairs keep entering and leaving side by side. Only a pair with one field
     hidden joins the flow, so its lone field can share a row with the next. */
  :is(.cf-inputs, .cf-inputs .cf-stream) > .cf-pair:not(.field, .ps-states):not(:has(> :not(.field))):has(> .field[hidden]) { display: contents; }
}
/* Hints beside what they explain, where the inputs have the room (the wide
   layout): a hint inside a field sits to the right of its label and input, and
   a hint that follows a single field moves up beside it. A hint after a group of
   fields explains the group, so it keeps the full width underneath, and so does
   the hint of a searchable list, which needs the whole row for its names. A hint
   marked .cf-group explains a group even when it follows one field. It
   depends on the input column's own width, not the screen's: a column too
   narrow for two fields a row keeps every hint underneath. */
.cf-inputs, .cf-inputs .cf-stream { container-type: inline-size; }
@container (min-width: 400px) {
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:has(> .hint):not(:has([role="combobox"], select.cf-wide)) {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); column-gap: 20px; align-items: center;
  }
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:has(> .hint):not(:has([role="combobox"], select.cf-wide)) > :not(.hint) { grid-column: 1; }
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:has(> .hint):not(:has([role="combobox"], select.cf-wide)) > .hint { grid-column: 2; grid-row: 1 / span 4; margin: 0; }
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:not(:has(> .hint)):has(+ p.hint:not(.cf-group)) { grid-column: 1; }
  :is(.cf-inputs, .cf-inputs .cf-stream) > .field:not(:has(> .hint)) + p.hint:not(.cf-group) { grid-column: 2 / -1; align-self: center; margin: 0; }
}

/* ---- results ---- */
.cf-results {
  margin: 0; padding: 18px 20px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}
.cf-results h2 {
  margin: 0 0 10px; font-size: 13px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted);
}
.cf-summary { margin: 0; font-size: 16px; line-height: 1.5; }
/* A yes/no answer that leads the results (the infiltration page's pressurization
   check): plain when the answer is fine, in the warning colours when it is not. */
.cf-verdict {
  margin: 0 0 12px; padding: 8px 12px; border: 1px solid var(--line); border-left: 4px solid var(--field-line);
  border-radius: 8px; background: var(--bg); font-size: 15px; font-weight: 600; line-height: 1.45;
}
.cf-verdict.is-over { border-color: var(--warn); background: var(--need); color: var(--fg); }

.cf-table { width: 100%; border-collapse: collapse; margin: 12px 0 0; }
.cf-table th, .cf-table td { padding: 5px 0; border-bottom: 1px solid var(--line); text-align: left; }
.cf-table tr:last-child :is(th, td) { border-bottom: 0; }
.cf-table th { font-weight: 400; font-size: 14.5px; }
/* Figures line up digit over digit, so two runs can be compared at a glance. */
.cf-table td.cf-num {
  font-variant-numeric: tabular-nums; font-weight: 600; font-size: 15px;
  text-align: right; padding-left: 14px; white-space: nowrap;
}
.cf-table td.cf-unit { font-size: 13.5px; color: var(--muted); padding-left: 10px; width: 1%; white-space: nowrap; }
/* Low / average / high columns (the cooling load estimate). */
.cf-table thead th { font-size: 12.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.cf-table thead th.cf-num { text-align: right; padding-left: 14px; }

.cf-note { margin: 14px 0 0; font-size: 13.5px; color: var(--muted); line-height: 1.55; }

/* Two or three short fields side by side, such as length and width, or an air
   stream's flow, dry bulb and wet bulb. */
.cf-pair, .cf-trio { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.cf-trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
:is(.cf-pair, .cf-trio) label { display: block; margin: 0 0 4px; font-size: 14px; }
/* Two-state psychrometric inputs: entering and leaving air side by side, each
   with its own "Given"; stacked when the column is too narrow for both. */
.ps-states { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.ps-state { border: 0; margin: 0; padding: 0; min-width: 0; }
.ps-state legend { padding: 0; margin: 0 0 8px; font-size: 14px; font-weight: 700; }
.ps-state select { width: 100%; }
.ps-states + .hint { margin: 8px 0 16px; }

/* Bilinear interpolation: the inputs laid out as the 3 x 3 grid they describe. */
.bi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 0 0 8px; }
.bi-cell { display: grid; gap: 4px; font-size: 13.5px; font-weight: 600; align-content: end; }
.bi-cell input { width: 100%; font: inherit; font-weight: 400; font-size: 15px; padding: 8px 10px;
  border: 1px solid var(--field-line); border-radius: 8px; background: var(--bg); color: var(--fg); }
.bi-cell input:required:invalid { background-color: var(--need); }
.bi-head { color: var(--muted); }
.bi-result { align-self: stretch; display: grid; place-items: center; min-height: 44px; border-radius: 8px;
  background: var(--accent-soft); color: var(--accent); font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* A diagram that goes with the result, such as the duct the calculation describes. */
.cf-figure { display: block; width: 100%; height: auto; margin: 16px 0 0; border-radius: 8px; background: #fff; }

/* One air stream, or any group of fields that belong together, under a heading. */
.cf-stream { margin: 0 0 18px; padding: 0; border: 0; }
.cf-stream legend { padding: 0; margin: 0 0 8px; font-size: 14.5px; font-weight: 600; }
.cf-stream .hint { margin: 6px 0 0; }

/* The row that repeats what was typed in, so the answer rows stand out from it. */
.cf-table tr.is-given :is(th, td) { color: var(--muted); }
.cf-table tr.is-given th::after { content: " (given)"; font-size: 12.5px; }
/* The answer most people came for: a tinted band with a larger figure, so it
   stands out from the supporting rows around it. */
.cf-table tr.cf-key :is(th, td) { font-weight: 700; background: var(--accent-soft); border-bottom-color: transparent; }
.cf-table tr.cf-key th { padding-left: 10px; border-radius: 8px 0 0 8px; }
.cf-table tr.cf-key td:last-child { padding-right: 10px; border-radius: 0 8px 8px 0; }
.cf-table tr.cf-key td.cf-num { font-size: 18px; }
.bi-result { font-weight: 700; }
/* A calculator whose answer is a sentence rather than a table row. */
.cf-answerline { padding: 8px 12px; border-radius: 8px; background: var(--accent-soft); font-weight: 700; }
.cf-note > span { display: block; }
.cf-note > span + span { margin-top: 6px; }

.cf-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.cf-copied { font-size: 13.5px; color: var(--muted); }

/* ---------- print ---------- */

/* An engineer printing a calculator wants the inputs, the answer and the method,
   not the site around them. */
@media print {
  .site-header, .site-footer, .nlband, .related, .to-top, .cf-actions, .skip { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  main { max-width: none; padding: 0; }
  .calc-layout { display: block; }
  .calcform { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; break-inside: avoid; }
  .cf-results { background: none; border-color: #999; }
  .calcform :is(input[type="number"], select) { background: none !important; border-color: #999; }
  a { color: inherit; text-decoration: none; }
}

/* ---------- calculator + related, side by side ---------- */

/* flex-wrap rather than a media query: a 980px embed leaves no room for the
   sidebar and drops it below, a 533px one keeps it alongside. Each page decides
   for itself from its own embed width. */
.calc-layout { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px 26px; margin: 0 0 30px; }
.calc-layout > .calc { flex: 0 1 auto; max-width: 100%; margin: 0; }
.calc-layout > .related { flex: 1 1 210px; max-width: 280px; margin: 0; }
/* Boxed like the results panel when it sits beside a calculator. The row-of-chips
   version under a wide embed stays unboxed; see .is-wide below. */
.calc-layout:not(.is-wide) > .related {
  padding: 18px 14px 12px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-alt);
}
.calc-layout:not(.is-wide) > .related h2 { padding-left: 6px; }
/* Hovering a link on the panel background would otherwise not show. */
.calc-layout:not(.is-wide) .related a:hover { background: var(--bg); }

.related h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 10px; }
.related ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.related a {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border: 1px solid transparent; border-radius: 8px;
  font-size: 14px; text-decoration: none; color: var(--fg);
}
.related a:hover { border-color: var(--line); background: var(--surface); color: var(--accent); }
.rel-icon { width: 24px; height: 24px; flex: 0 0 24px; object-fit: contain; }
.rel-icon-blank { display: block; }

/* If the sidebar has wrapped under a wide calculator it has the full width, so
   lay the links out in a row instead of a tall single column. */
@media (min-width: 700px) {
  .calc-layout > .related:only-child,
  .calc-layout > .related { min-width: 0; }
}

/* ---------- calculator index ---------- */

.jump { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 18px; }
.jump a {
  display: block; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 14px; text-decoration: none; color: var(--fg); background: var(--surface);
}
.jump a:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.star {
  display: inline-block; width: 13px; height: 13px; flex: 0 0 13px; vertical-align: -1px;
  background: #f5b91a;
  clip-path: polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%);
}

.favonly-input { position: absolute; opacity: 0; pointer-events: none; }
.favonly-label {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 7px 14px; margin: 0 0 26px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 14px;
}
.favonly-label:hover { border-color: var(--accent); }
.favonly-input:focus-visible + .favonly-label { outline: 2px solid var(--accent); outline-offset: 2px; }
.favonly-input:checked + .favonly-label { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.favonly-input:checked ~ .catgroups .card:not(.is-fav) { display: none; }
.favonly-input:checked ~ .catgroups .catgroup:not(:has(.is-fav)) { display: none; }

/* favorites bar */
.favbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 26px; }
.favonly-label { margin: 0; }
.favreset {
  font: inherit; font-size: 13.5px; color: var(--accent);
  background: none; border: 0; padding: 4px 2px; cursor: pointer; text-decoration: underline;
}
.favreset:hover { text-decoration-thickness: 2px; }
.favhint { font-size: 13px; color: var(--muted); }
.favempty {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 14px 16px; margin: 0 0 26px; color: var(--muted);
}

/* the star: a static span without JS, a real button with it */
.card-star {
  position: absolute; top: 11px; right: 11px;
  width: 15px; height: 15px; flex: 0 0 15px;
}
button.card-star {
  width: 30px; height: 30px; padding: 0; top: 4px; right: 4px;
  background: none; border: 0; cursor: pointer; border-radius: 50%;
  display: grid; place-items: center;
}
button.card-star::before {
  content: ""; display: block; width: 15px; height: 15px;
  background: #cdc6bd;                       /* unstarred: a visible outline-ish gray */
  clip-path: polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%);
  transition: background .12s ease;
}
.card.is-fav button.card-star::before { background: #f5b91a; }
button.card-star:hover { background: var(--accent-soft); }
button.card-star:hover::before { background: #f5b91a; }
.card.is-fav button.card-star:hover::before { background: #b9832c; }
button.card-star:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* touch needs a 44px target; the glyph stays the same size */
@media (pointer: coarse) {
  button.card-star { width: 44px; height: 44px; top: 0; right: 0; }
  .card-top strong { padding-right: 34px; }
}
/* without JS an unstarred card shows no star at all */
.card:not(.is-fav) span.card-star { display: none; }

.catgroup { margin: 0 0 42px; scroll-margin-top: 16px; }
.catgroup h2 {
  font-size: 15px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
  margin: 0 0 14px; padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.groupnote { margin: -6px 0 14px; font-size: 13.5px; color: var(--muted); max-width: var(--prose); }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(226px, 1fr)); gap: 10px; list-style: none; margin: 0; padding: 0; }
.cards .card { margin: 0; }
.cards .card { position: relative; }
.cards .card-link {
  display: flex; flex-direction: column; gap: 4px; height: 100%;
  padding: 13px 15px; border: 1px solid var(--line); border-radius: var(--radius);
  text-decoration: none; color: var(--fg); background: var(--surface);
}
.cards .card-link:hover { border-color: var(--accent); background: var(--accent-soft); }
.cards .card-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.card-top { display: flex; align-items: center; gap: 9px; }
.card-icon {
  width: 34px; height: 34px; flex: 0 0 34px; object-fit: contain;
}
.card-top strong { flex: 1 1 auto; }
.card-top strong { font-size: 15px; font-weight: 600; line-height: 1.3; padding-right: 22px; }
.card-desc { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.badge {
  position: absolute; top: -7px; right: 10px;
  background: #c0392b; color: #fff; font-size: 10px; font-weight: 700;
  letter-spacing: 0.05em; padding: 2px 7px; border-radius: 4px;
}

/* ---------- helpful links ---------- */

.linksection { margin-top: 56px; padding-top: 28px; border-top: 2px solid var(--line); scroll-margin-top: 16px; }
.linksection > h2 { font-size: 24px; margin: 0 0 4px; }
.linksection > .subtitle { margin-bottom: 26px; }
.linkgroup { margin: 0 0 26px; }
.linkgroup h3 {
  font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin: 0 0 10px;
}
.linklist { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 7px; list-style: none; margin: 0; padding: 0; }
.linklist a {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); text-decoration: none; color: var(--fg); font-size: 14px;
}
.linklist a:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.ext { margin-left: auto; color: var(--muted); font-size: 12px; }
.linklist a:hover .ext { color: var(--accent); }

/* ---------- blog ---------- */

.postlist { list-style: none; margin: 0; padding: 0; }
.postlist li { padding: 20px 0; border-bottom: 1px solid var(--line); }
.postlist h2 { font-size: 20px; margin: 0 0 5px; }
.postlist h2 a { color: var(--fg); text-decoration: none; }
.postlist h2 a:hover { color: var(--accent); }
.postlist p { margin: 5px 0 0; color: var(--muted); font-size: 15px; }
.postlist .meta { font-size: 13.5px; }

.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; padding: 0; margin: 0 0 28px; }
.chips a {
  display: block; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 14px; text-decoration: none; color: var(--fg); background: var(--surface);
}
.chips a:hover { border-color: var(--accent); color: var(--accent); }

.post-nav { margin-top: 40px; padding-top: 18px; border-top: 1px solid var(--line); }

.notice {
  border: 1px solid var(--line); border-left: 3px solid var(--warn);
  background: var(--surface); border-radius: var(--radius);
  padding: 14px 16px; margin: 0 0 24px; max-width: var(--prose);
}

/* ---------- footer ---------- */

.site-footer { border-top: 1px solid var(--line); background: var(--surface); }
.cols {
  max-width: var(--maxw); margin: 0 auto; padding: 36px 16px 8px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 28px;
}
.cols h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 0 0 10px; }
.cols ul { list-style: none; margin: 0; padding: 0; }
.cols li { margin: 5px 0; }
.cols a { font-size: 14.5px; }
.cols p { margin: 0 0 8px; font-size: 14.5px; }
.legal {
  max-width: var(--maxw); margin: 0 auto; padding: 18px 16px 30px;
  color: var(--muted); font-size: 13px; border-top: 1px solid var(--line);
}

/* ---------- small screens ---------- */

/* Six nav items no longer fit beside the logo and search below ~900px, so the
   menu button takes over there rather than letting the bar wrap to two rows. */
@media (max-width: 900px) {
  .navbtn {
    display: block; margin-left: auto; padding: 7px 13px;
    border: 1px solid var(--line); border-radius: 7px;
    font-size: 14px; cursor: pointer; user-select: none;
  }
  .sitesearch { order: 3; flex: 1 1 100%; margin: 10px 0 0; }
  .mainnav { display: none; width: 100%; margin-left: 0; order: 4; }
  .navtoggle:checked ~ .mainnav { display: block; }
  .mainnav ul { flex-direction: column; gap: 0; }
  .mainnav a { padding: 10px 8px; border-radius: 0; border-bottom: 1px solid var(--line); }
}

/* ---------- engineering services ---------- */

.eyebrow {
  font-size: 12px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 10px;
}

/* The measure belongs on the prose, not on the section: the headline sets its
   own line breaks and needs the room, while the lede still wants ~42rem to stay
   readable. Capping the whole hero made the longest headline line wrap into a
   fourth. */
.svc-hero { max-width: none; margin: 0 0 40px; }
.svc-hero .lede { max-width: 42rem; }
.svc-hero h1 { font-size: clamp(28px, 5vw, 44px); line-height: 1.12; letter-spacing: -0.02em; margin: 0 0 14px; }
/* One span per line of the headline. Each break is intended, so it is marked up
   rather than left to the browser; the text still reads as one heading. */
.svc-headline span { display: block; }
.lede { font-size: 18px; line-height: 1.55; color: var(--fg); margin: 0 0 18px; }
.segments { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 22px; }
.segments span {
  font-size: 13px; padding: 4px 12px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
}

.btn {
  display: inline-block; padding: 10px 18px; border-radius: 8px;
  background: var(--btn); color: var(--btn-fg); text-decoration: none; font-weight: 700; font-size: 15px;
}
.btn:hover { background: var(--btn-hover); text-decoration: none; }
.btn-quiet { background: var(--surface); color: var(--fg); border: 1px solid var(--line); }
.btn-quiet:hover { background: var(--surface); border-color: var(--accent); color: var(--accent); }
.cta-row { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }

.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px;
  margin: 0 0 52px; padding: 22px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.stats b { display: block; font-size: 30px; line-height: 1.1; letter-spacing: -0.02em; }
.stats span { display: block; font-size: 13px; color: var(--muted); margin-top: 4px; }
.stats small { display: block; font-size: 11.5px; color: var(--muted); margin-top: 6px; line-height: 1.4; }

.svc-list { margin: 0 0 52px; }
.svc-list h2, .pricing h2, .projects h2, .licensing h2, .crosslink h2 {
  font-size: 22px; letter-spacing: -0.01em; margin: 0 0 8px;
}
.sub { color: var(--muted); max-width: var(--prose); margin: 0 0 22px; }

.svc-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.svc-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; background: var(--surface); }
.svc-card.is-primary { background: var(--bg); border-color: color-mix(in srgb, var(--accent) 34%, var(--line)); }
.svc-n { font-size: 12px; color: var(--muted); letter-spacing: 0.08em; }
.svc-card h3 { font-size: 18px; margin: 4px 0 8px; }
.svc-card h3 a { text-decoration: none; }
.svc-card h3 a:hover { text-decoration: underline; }
.svc-card p { margin: 0 0 10px; font-size: 14.5px; }
.svc-links { font-size: 14px; color: var(--muted); }
.stamp {
  display: inline-block; font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft); border-radius: 4px; padding: 2px 8px;
}
.stamp-line { margin: 0 0 10px; }

/* Deliberately not a card. It sat on --surface with a --line border and the
   same radius as the service cards directly above it, so it read as a fifth
   card rather than the call to action it is. The accent wash and the rule down
   the side say "different kind of block" before the heading is read. Contrast
   on --accent-soft checked in both themes: body 16.2:1 light, 13.2:1 dark;
   muted 5.2:1 and 7.4:1. */
.pricing {
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-left: 4px solid var(--accent);
  border-radius: 4px var(--radius) var(--radius) 4px;
  padding: 26px 28px; margin: 0 0 52px;
}
.pricing h2 { font-size: 25px; }
.pricing p { max-width: var(--prose); }

.projects { margin: 0 0 52px; }
.project { border-top: 1px solid var(--line); padding: 22px 0 6px; }
.project h3 { font-size: 19px; margin: 0 0 8px; }
.project p { max-width: var(--prose); }
.facts { display: flex; flex-wrap: wrap; gap: 26px; margin: 14px 0 0; }
.facts div { margin: 0; }
.facts dt { font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); }
.facts dd { margin: 3px 0 0; font-size: 15px; font-weight: 600; }

.licensing { margin: 0 0 52px; }
.states { list-style: none; display: flex; flex-wrap: wrap; gap: 7px; padding: 0; margin: 0 0 14px; }
.states li { border: 1px solid var(--line); border-radius: 7px; padding: 5px 11px; background: var(--surface); font-size: 14px; }
.states abbr { text-decoration: none; border: 0; font-weight: 600; }

.crosslink { border-top: 1px solid var(--line); padding-top: 24px; }
.crosslink p { max-width: var(--prose); }

.svc-cta { margin: 40px 0 0; padding: 22px; border: 1px solid var(--line);
  border-left: 3px solid var(--accent); border-radius: var(--radius); background: var(--surface); }
.svc-cta h2 { font-size: 18px; margin: 0 0 6px; }
.svc-cta p { max-width: var(--prose); }
.svc-other { margin-top: 36px; padding-top: 18px; border-top: 1px solid var(--line); }
.svc-other h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 10px; }
.svc-other ul { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; padding: 0; margin: 0; }
.svc-other li a, .svc-other li span {
  display: block; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 14px; background: var(--surface); text-decoration: none; color: var(--fg);
}
.svc-other li span { color: var(--muted); }
.svc-other li a:hover { border-color: var(--accent); color: var(--accent); }

.mainnav .nav-cta {
  background: var(--btn); color: var(--btn-fg); font-weight: 700;
}
.mainnav .nav-cta:hover { background: var(--btn-hover); }



/* ---------- gated-service dialog ---------- */

.stamp-quiet { background: var(--surface); color: var(--muted); border: 1px solid var(--line); }
.svc-card.is-gated h3 a { color: var(--fg); }
.svc-card.is-gated h3 a:hover { color: var(--accent); }

.gate {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 24px; max-width: 30rem; width: calc(100% - 32px);
  background: var(--bg); color: var(--fg);
  box-shadow: 0 18px 50px rgba(16, 25, 35, .22);
}
.gate::backdrop { background: rgba(16, 25, 35, .45); }
.gate h2 { font-size: 19px; margin: 0 0 8px; }
.gate p { margin: 0 0 16px; }
.gate-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }
.gate-actions .btn { cursor: pointer; font: inherit; font-weight: 700; font-size: 15px; }

/* ---------- dark theme ----------
   Site-wide and chosen by the visitor, not by which section they are in.
   Applied via data-theme="dark" on <html>, set before first paint by the inline
   script in base.njk, so it is a token swap only — no component needs a second
   set of rules.

   Contrast measured against #262624 (check-contrast.mjs):
     body text  #f0eeec  13.10:1      muted  #b8b4ad  7.34:1
     accent     #ef7c00   5.45:1
   On dark the real brand orange clears AA, so it is used directly rather than
   the darkened variant the light theme needs. */

/* Warm near-black, not the blue-tinted one this started with. The greys carry a
   little red and drop a little blue so they sit against the brand orange rather
   than fighting it. Every value below was checked with check-contrast.mjs. */
:root[data-theme="dark"] {
  --bg: #262624;
  --surface: #2b2b28;
  --fg: #f0eeec;
  --muted: #b8b4ad;
  --line: #3d3d39;
  --field-line: #7a7a71;   /* 3.50:1 on --bg, 3.05:1 on --surface */
  --brand: #ef7c00;
  --accent: #ef7c00;
  --accent-fg: #262624;
  --accent-soft: #2f2319;
  --surface-alt: #2f2319;
  --btn: #ef7c00;
  --btn-hover: #ff8f1f;
  --btn-fg: #14110e;
  --need: #342d1f;   /* text 11.8:1, --field-line 3.15:1 */
  --warn: #e2ab5e;
  color-scheme: dark;
}

/* Dark pages are served logo-dark.png, which is transparent-backed white art —
   so drop the white plate the light logo needs. */
.logo-dark { display: none; }
:root[data-theme="dark"] .logo-light { display: none; }
:root[data-theme="dark"] .logo-dark { display: block; }
/* the dark artwork is transparent-backed, so it needs no white plate */
:root[data-theme="dark"] .brand-logo { background: none; border-radius: 0; box-shadow: none; }

/* Lift panels off the page rather than relying on borders alone. */
:root[data-theme="dark"] .searchresults { box-shadow: 0 12px 34px rgba(0, 0, 0, .55); }
:root[data-theme="dark"] .gate { box-shadow: 0 18px 50px rgba(0, 0, 0, .6); }
:root[data-theme="dark"] .gate::backdrop { background: rgba(0, 0, 0, .6); }

/* Segments and the state list are statements of fact, not controls. They were
   pill-shaped with a border and fill, which reads as a row of buttons; set as
   plain separated text instead. */
.segments { display: block; color: var(--muted); font-size: 15.5px; margin: 0 0 22px; }
.segments span {
  display: inline; padding: 0; border: 0; border-radius: 0;
  background: none; color: inherit;
}
.segments span:not(:last-child)::after { content: " · "; white-space: pre; opacity: .7; }

.states { display: block; margin: 0 0 14px; }
.states li {
  display: inline; padding: 0; border: 0; border-radius: 0;
  background: none; font-size: 15.5px; color: var(--fg);
}
.states li:not(:last-child)::after { content: " · "; white-space: pre; color: var(--muted); }
.states abbr { font-weight: 400; text-decoration: none; border: 0; cursor: help; }

/* ---------- about ---------- */

.about-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 22px; margin: 26px 0 34px; }
.about-facts dd { margin: 3px 0 0; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.about-facts .fact-note { font-size: 13px; font-weight: 400; color: var(--muted); line-height: 1.45; margin-top: 4px; }

.portrait { margin: 0 0 22px; max-width: 260px; float: right; padding: 0 0 18px 24px; }
.portrait img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
.portrait figcaption { margin-top: 10px; font-size: 13.5px; color: var(--muted); line-height: 1.45; }
@media (max-width: 620px) { .portrait { float: none; max-width: 220px; padding: 0 0 6px; } }

.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 8px 32px; clear: both; }
.two-col h3 { font-size: 15px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 26px 0 8px; }
.two-col ul { margin: 0; }

/* ---------- contact form ---------- */

.contact-form { max-width: 34rem; margin: 26px 0 24px; }
.field { margin: 0 0 18px; }
.field label { display: block; font-size: 14.5px; font-weight: 600; margin: 0 0 6px; }
.req { color: var(--accent); font-weight: 400; }
.opt { color: var(--muted); font-weight: 400; font-size: 13px; }

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form select,
.contact-form textarea {
  width: 100%; font: inherit; font-size: 15px;
  padding: 9px 12px; border: 1px solid var(--field-line); border-radius: 8px;
  background: var(--bg); color: var(--fg);
  -webkit-appearance: none; appearance: none;
}
.contact-form select {
  /* restore the dropdown arrow that appearance:none removes */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 34px;
}
.contact-form textarea { resize: vertical; min-height: 7em; }
.contact-form :is(input, select, textarea):focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}
/* :user-invalid, not :invalid — an untouched required field is invalid from the
   moment it renders, which would paint the form red before anyone typed. */
.contact-form :is(input, textarea, select):user-invalid { border-color: #b3261e; }
.contact-form :is(input, textarea, select):user-invalid:focus { outline-color: #b3261e; }

.hint { margin: 6px 0 0; font-size: 13px; color: var(--muted); }
.form-actions { margin: 24px 0 0; }
.form-actions .btn { cursor: pointer; font: inherit; font-weight: 700; font-size: 15px; border: 0; }

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Form and photo side by side. The page body is normally capped at --prose for
   reading comfort; this layout needs the full column, so widen just this page. */
.prose:has(.contact-layout) { max-width: none; }
.contact-intro, .contact-note { max-width: var(--prose); }
.contact-note { margin-top: 30px; color: var(--muted); }

.contact-layout {
  display: grid; gap: 34px; align-items: start;
  grid-template-columns: minmax(0, 34rem) minmax(0, 1fr);
  margin: 26px 0 0;
}
.contact-main .contact-form { margin-top: 0; }
.contact-figure { margin: 0; }
.contact-figure img {
  display: block; width: 100%; height: auto; border-radius: var(--radius);
}
@media (max-width: 860px) {
  .contact-layout { grid-template-columns: 1fr; gap: 26px; }
  /* photo above the form on narrow screens, so the page does not open on a
     wall of inputs */
  .contact-figure { order: -1; max-width: 32rem; }
}

/* Wide embeds leave no room beside them, so their related list runs full width
   underneath as a row of chips rather than a tall narrow column. */
.calc-layout.is-wide > .related { flex: 1 1 100%; max-width: none; }
.calc-layout.is-wide .related ul { flex-direction: row; flex-wrap: wrap; gap: 6px; }
.calc-layout.is-wide .related a {
  border-color: var(--line); background: var(--surface); border-radius: 999px; padding: 6px 13px 6px 9px;
}
.calc-layout.is-wide .related a:hover { border-color: var(--accent); }

/* ---------- back to top ---------- */

/* Smooth anchor jumps site-wide, but only for people who have not asked for
   less motion. Scrolling nine screens in one animated sweep is a real nausea
   trigger. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* Focus lands on <main> after the jump; it is a scroll target, not a control,
   so it should not draw a focus ring. */
#main:focus { outline: none; }

.to-top {
  position: fixed; right: 20px; bottom: 20px; z-index: 30;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 15px; border-radius: 999px;
  background: var(--btn); color: var(--btn-fg);
  font-size: 14px; font-weight: 600; text-decoration: none;
  box-shadow: 0 6px 20px rgba(16, 25, 35, .25);
  /* visibility, not just opacity, so it leaves the tab order while hidden */
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.to-top.is-visible { opacity: 1; visibility: visible; transform: none; }
.to-top:hover { background: var(--btn-hover); text-decoration: none; }
.to-top:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .to-top { transition: none; } }
@media (max-width: 620px) { .to-top { right: 14px; bottom: 14px; } }

/* ---------- home: split panes ---------- */

/* overflow-x: clip (not hidden) so the full-bleed panes below can escape the
   content column without creating a horizontal scrollbar, and without turning
   the body into a scroll container. */
html, body { overflow-x: clip; }

.home-h1 {
  font-size: clamp(26px, 4.4vw, 40px); line-height: 1.15; letter-spacing: -0.02em;
  max-width: 20ch; margin: 8px 0 34px;
}

.split {
  /* break out of main's centred column to the viewport edges */
  margin-inline: calc(50% - 50vw);
  display: grid; grid-template-columns: 1fr 1fr;
}
.split-pane { padding: 60px clamp(20px, 4vw, 52px); display: flex; flex-direction: column; }
/* Outer edges line up with the site's content column (--maxw), so the
   pane text sits under the nav rather than out at the viewport edge. */
.split-follow { padding-left: max(20px, calc(50vw - var(--maxw) / 2 + 16px)); }
.split-invert { padding-right: max(20px, calc(50vw - var(--maxw) / 2 + 16px)); }
.split-pane h2 { font-size: clamp(20px, 2.4vw, 26px); letter-spacing: -0.01em; margin: 0 0 12px; }
.split-pane p { max-width: 34ch; margin: 0 0 18px; }
.split-pane p:last-child { margin: auto 0 0; padding-top: 10px; }

/* One pane follows the current theme, the other is always its opposite, so the
   page reads as black-and-white split in either mode. */
.split-follow { background: var(--bg); color: var(--fg); }

/* The dark pane re-declares the theme tokens locally, so the button, links and
   muted text inside it adapt without a second set of rules. */
.split-invert {
  --bg: #262624; --surface: #2b2b28; --fg: #f0eeec; --muted: #b8b4ad;
  --line: #3d3d39; --accent: #ef7c00; --accent-fg: #262624; --accent-soft: #2f2319;
  background: var(--bg); color: var(--fg);
}

@media (max-width: 720px) {
  .split { grid-template-columns: 1fr; }
  .split-pane { padding: 40px 20px; }
  .split-pane p:last-child { margin-top: 0; }
}

/* ---------- theme toggle ---------- */

/* Last item in the nav, after Get a Quote. It shows where a press takes you:
   a moon and "Dark" in light mode, a sun and "Light" in dark mode. */
.themetoggle {
  display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 10px;
  cursor: pointer; background: none; border: 1px solid var(--line); border-radius: 7px;
  color: var(--fg); font: inherit; font-size: 14px; font-weight: 500; white-space: nowrap;
}
.themetoggle svg { width: 16px; height: 16px; flex: 0 0 auto; }
.themetoggle:hover { border-color: var(--accent); color: var(--accent); }
.themetoggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.themetoggle .icon-sun { display: none; }
:root[data-theme="dark"] .themetoggle .icon-sun { display: block; }
:root[data-theme="dark"] .themetoggle .icon-moon { display: none; }
/* Same 44px minimum the favourite stars get on touch. */
@media (pointer: coarse) { .themetoggle { height: 44px; } }
.mainnav .nav-theme { display: flex; align-items: center; margin-left: 4px; }
@media (max-width: 900px) {
  .mainnav .nav-theme { margin: 8px 0 0; }
}

/* ---------- dark mode: assets that are black line art ---------- */

/* The calculator icons and related-list icons are black drawings on transparent.
   They vanish on a dark background, so invert them there. */
:root[data-theme="dark"] .card-icon,
:root[data-theme="dark"] .rel-icon,
:root[data-theme="dark"] .svc-icon {
  filter: invert(1) brightness(1.6) contrast(0.85);
}

/* SpreadsheetConverter output is always light, so the embed keeps its white
   panel in both themes rather than sitting in a dark frame. */
:root[data-theme="dark"] .calc { background: #fff; }

/* In dark mode the inverted pane becomes the light one, so the home page stays a
   black-and-white split whichever theme is chosen. */
:root[data-theme="dark"] .split-invert {
  --bg: #ffffff; --surface: #f7f5f2; --fg: #1d1b19; --muted: #6b645c;
  --line: #e6e1da; --brand: #ef7c00; --accent: #bf4d00; --accent-fg: #ffffff;
  --accent-soft: #fef4ea;
  background: var(--bg); color: var(--fg);
}

/* ---------- calculator updates signup ---------- */


/* Name and email side by side with the button, collapsing to stacked rows when
   there is no room. The button is a grid item, not inside a field, so it sits on
   the input baseline rather than below the labels. */
.nlform-fields {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 12px; align-items: end;
}
.nlform .field { margin: 0; }
.nlform input[type="text"],
.nlform input[type="email"] {
  width: 100%; font: inherit; font-size: 15px;
  padding: 9px 12px; border: 1px solid var(--field-line); border-radius: 8px;
  background: var(--bg); color: var(--fg);
  -webkit-appearance: none; appearance: none;
}
.nlform :is(input):focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}
/* :user-invalid, not :invalid — see the contact form note. */
.nlform input:user-invalid { border-color: #b3261e; }
.nlform input:user-invalid:focus { outline-color: #b3261e; }
.nlform .form-actions { margin: 0; }
.nlform .form-actions .btn { cursor: pointer; font: inherit; font-weight: 600; font-size: 15px; border: 0; }

.nlform-fallback { margin: 0; font-size: 14.5px; color: var(--muted); }

/* Full-width variant on /calculator-updates, where the form is the point of the
   page rather than a footer aside. */
.nlform-wide { max-width: 34rem; margin: 22px 0 30px; }

@media (max-width: 760px) {
  .nlform-fields { grid-template-columns: 1fr; align-items: stretch; }
  .nlform .form-actions .btn { width: 100%; }
}

/* ---------- service promo cards ---------- */

/* Two-up on desktop, stacked on a phone. The images carry their own type, so
   they are given room rather than squeezed into a sidebar. */
.svc-promos {
  display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
  margin: 0 0 44px; max-width: 1000px;
}
.svc-promos a {
  display: block; border-radius: 12px; overflow: hidden;
  border: 1px solid var(--line); background: var(--surface);
}
.svc-promos img { display: block; width: 100%; height: auto; }
.svc-promos a:hover { border-color: var(--accent); }
.svc-promos a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (max-width: 720px) { .svc-promos { grid-template-columns: 1fr; } }

/* ---------- licence list ---------- */

.licences {
  list-style: none; margin: 0 0 20px; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px 16px;
}
.licences li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 9px 13px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
}
.licences b { font-size: 15px; font-weight: 600; }
.licences span {
  font-size: 13px; color: var(--muted); white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}


/* Service card mark. Small and identical in treatment across all five cards:
   three of the services have no photograph that is honestly ours, so none of
   them use one rather than mixing photos with drawn panels. */
.svc-top { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.svc-icon { flex: 0 0 auto; width: 34px; height: 34px; object-fit: contain; }


/* ---------- header mega menus ---------- */

.nav-has-menu { position: relative; display: flex; align-items: center; }
/* The disclosure sits tight against its link so the pair reads as one item. */
.mainnav .nav-has-menu > a { padding-right: 4px; }
.nav-disclose {
  display: grid; place-items: center; width: 22px; height: 30px;
  padding: 0; margin: 0; border: 0; background: none; cursor: pointer; color: var(--fg);
  border-radius: 6px;
}
.nav-disclose:hover { color: var(--accent); }
.nav-disclose:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chev {
  width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
}
.nav-has-menu.is-open .chev { transform: rotate(225deg) translate(-2px, -2px); }

.megamenu {
  position: absolute; z-index: 60; top: calc(100% + 10px); left: 0;
  min-width: 300px; padding: 10px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 14px 36px rgba(16, 25, 35, .16);
}
:root[data-theme="dark"] .megamenu { box-shadow: 0 16px 40px rgba(0, 0, 0, .6); }
.megamenu[hidden] { display: none; }
.megamenu ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
/* Two columns once there are eight categories to show. */
.megamenu-wide { min-width: 460px; }
.megamenu-wide ul { grid-template-columns: 1fr 1fr; }
.megamenu li { margin: 0; }
.megamenu li a {
  display: block; padding: 8px 10px; border-radius: 8px;
  text-decoration: none; color: var(--fg); font-size: 14.5px; font-weight: 600;
}
.megamenu li span { display: block; padding: 0 10px 6px; font-size: 12.5px; color: var(--muted); }
.megamenu li a:hover { background: var(--accent-soft); color: var(--accent); }
.megamenu .mm-all { grid-column: 1 / -1; border-top: 1px solid var(--line); margin-top: 6px; padding-top: 6px; }
.megamenu .mm-all a { color: var(--accent); }

/* On a phone the nav is already a stacked panel, so the menus open inline
   rather than floating over it. */
@media (max-width: 900px) {
  .megamenu {
    position: static; min-width: 0; box-shadow: none;
    border: 0; border-left: 2px solid var(--line); border-radius: 0;
    margin: 2px 0 6px 10px; padding: 2px 0 2px 8px;
  }
  .megamenu-wide ul { grid-template-columns: 1fr; }
  .nav-has-menu { flex-wrap: wrap; }
  .nav-has-menu > .megamenu { flex: 1 0 100%; }
}

/* Same 44px minimum the favourite stars and the theme toggle get on touch. */
@media (pointer: coarse) { .nav-disclose { width: 44px; height: 44px; } }

/* ---------- post listing ---------- */

.postlist li.has-thumb {
  display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 20px; align-items: start;
}
.post-thumb { display: block; border-radius: 10px; overflow: hidden; border: 1px solid var(--line); }
.post-thumb img { display: block; width: 100%; height: auto; }
.post-thumb:hover { border-color: var(--accent); }
.post-body > :first-child { margin-top: 0; }
/* Below this the picture would be wider than the text beside it. */
@media (max-width: 640px) {
  .postlist li.has-thumb { grid-template-columns: 96px minmax(0, 1fr); gap: 14px; }
}

/* Three bottom spacings used to stack at the end of a page: the last
   section's own margin, main's bottom padding, and a 60px margin on the
   footer. That came to 177px of blank between the last panel and the footer
   band. Main's padding is the one kept. */
main > :last-child { margin-bottom: 0; }

/* Service page banner. Sits between the page head and the prose. */
.svc-banner { display: block; margin: 0 0 30px; border-radius: var(--radius); overflow: hidden; }
.svc-banner img { display: block; width: 100%; height: auto; aspect-ratio: 34 / 10; object-fit: cover; }
@media (max-width: 620px) { .svc-banner img { aspect-ratio: 16 / 9; } }

/* Calculator Updates sits apart from the categories above it in the menu: it is
   a different kind of destination, not a ninth category. */
.megamenu .mm-updates {
  grid-column: 1 / -1; border-top: 1px solid var(--line); margin-top: 6px; padding-top: 6px;
}


/* Calculator Updates in the header. Outlined, not filled: Get a Quote is the
   primary action and two solid buttons would compete rather than rank. */
.mainnav .nav-sub {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 11px; border-radius: 7px;
  border: 1px solid var(--field-line); color: var(--fg);
  text-decoration: none; font-size: 14px; font-weight: 500; white-space: nowrap;
}
.nav-sub svg { width: 15px; height: 15px; flex: 0 0 auto; color: var(--accent); }
.nav-sub:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.nav-sub:hover svg { color: inherit; }

/* ---------- calculator updates band ---------- */

/* One row above the footer columns: the copy on the left, the field and button
   on the right. Deliberately short. An earlier version carried a picture and
   two paragraphs and stood about 250px tall on every page. */
.nlband { border-bottom: 1px solid var(--line); background: var(--surface); }
.nlband-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 18px 16px;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: 24px; align-items: center;
}
.nlband-copy h2 { font-size: 18px; margin: 0 0 3px; }
.nlband-copy p { margin: 0; font-size: 14.5px; color: var(--muted); }
@media (max-width: 760px) {
  .nlband-inner { grid-template-columns: 1fr; gap: 12px; }
}

/* Kitchen hood calculator: one row per appliance; the hood style diagram sits under the results. */
.ck-row { display: grid; grid-template-columns: minmax(0, 1fr) 7.5rem auto; gap: 10px; align-items: end; }
.ck-row + .ck-row { margin-top: 12px; }
.ck-row .ck-remove { margin-bottom: 2px; }
.ck-row select { width: 100%; min-width: 0; }
.ck-figure { margin: 18px 0 0; }
.ck-figure img { display: block; width: 100%; max-width: 480px; height: auto; border-radius: 8px; background: #fff; }
.ck-figure figcaption { margin-top: 6px; font-size: 13.5px; color: var(--muted); max-width: 480px; }
.ck-notes { padding-left: 1.2em; }
.ck-notes li + li { margin-top: 6px; }
@media (max-width: 520px) {
  .ck-row { grid-template-columns: minmax(0, 1fr) auto; }
  .ck-row .field:first-child { grid-column: 1 / -1; }
}

/* Transfer duct sizer: the grille list's heading inside the results. */
.tx-h3 { margin: 22px 0 0; font-size: 12.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }

/* Diffuser sizer: the catalog rows of the sizes that fit, the column in use shaded. */
.ds-table { min-width: 560px; font-size: 13.5px; }
.ds-table th, .ds-table td { padding: 5px 6px; }
.ds-table thead th { white-space: nowrap; }
.ds-table tr.ds-first > * { border-top: 2px solid var(--line); }
.ds-table tbody th { font-weight: 600; vertical-align: top; min-width: 7rem; }
.ds-table .ds-col { background: color-mix(in srgb, var(--btn) 14%, transparent); }
.ds-table td.cf-num { font-weight: 400; font-size: 13.5px; padding-left: 6px; }
.ds-type { display: flex; gap: 14px; align-items: center; margin-top: 16px; }
.ds-type img { flex: none; width: 110px; height: auto; border-radius: 6px; background: #fff; }
.ds-table thead th { text-transform: none; letter-spacing: 0; font-size: 13px; }

/* Bilinear interpolation: the method graph sits under the result. */
.bi-graph { margin: 16px 0 0; }
.bi-graph img { display: block; width: 100%; max-width: 520px; height: auto; border-radius: 8px; background: #fff; }

/* ---------- crack-method infiltration: opening rows, building ---------- */
.cm-row, .cm-bldg { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) auto; gap: 10px; align-items: end; }
.cm-bldg { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cm-row + .cm-row { margin-top: 12px; }
.cm-fitdesc { margin-bottom: 18px; }
/* An input, or a stack of inputs, with its explanation to the right. */
.cm-side { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: 6px 22px; align-items: center; }
.cm-side[hidden] { display: none; }
.cm-side > .hint, .cm-side > p { margin: 0; }
.cm-side .field { margin: 0; }
.cm-stack { display: grid; gap: 10px; }
.cm-stack :is(input, select), .cm-side > .field :is(input, select) { width: 100%; min-width: 0; }
.cf-stream > .cm-side + .cm-side { margin-top: 6px; }
@media (max-width: 600px) {
  .cm-side { grid-template-columns: minmax(0, 1fr); }
}
.cm-row .cm-remove { margin-bottom: 2px; }
.cm-row input, .cm-bldg input, .cm-bldg select { width: 100%; min-width: 0; }
.cm-bldg + .cm-bldg, .cf-stream > .field + .cm-bldg { margin-top: 12px; }
.cf-stream .hint + .field, .cf-stream .hint + .cm-bldg { margin-top: 14px; }
/* Pressurization results: a second block under the infiltration results. */
.cf-subhead { margin: 22px 0 6px; font-size: 16px; }
@media (max-width: 480px) {
  .cm-bldg { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cm-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cm-row .cm-remove { grid-column: 1 / -1; justify-self: start; }
}

/* ---------- heat pump balance point: paired inputs, chart ---------- */
.bp-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-items: end; margin-top: 12px; }
.bp-pair input { width: 100%; min-width: 0; }
.bp-hint { margin-bottom: 6px; }
.bp-chart { margin: 18px 0 0; }
.bp-chart svg { display: block; width: 100%; max-width: 560px; height: auto; }
.bp-chart figcaption { margin-top: 6px; font-size: 13.5px; color: var(--muted); max-width: 560px; }
.bp-grid { stroke: var(--line); stroke-width: 1; }
.bp-tick { fill: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.bp-axis { fill: var(--fg); font-size: 12.5px; }
.bp-load { fill: none; stroke: var(--fg); stroke-width: 2.5; }
.bp-cap { fill: none; stroke: var(--brand); stroke-width: 2.5; stroke-dasharray: 7 4; }
.bp-gap { stroke: var(--warn); stroke-width: 6; stroke-linecap: butt; opacity: .55; }
.bp-dot { fill: var(--accent); stroke: var(--bg); stroke-width: 2; }
.bp-legend { fill: var(--bg); stroke: var(--line); }

/* ---------- searchable drop-down (combobox.js) over a <select> ---------- */
.cb { position: relative; }
.calcform .cb-input { width: 100%; padding-right: 40px; }
.cb-toggle {
  position: absolute; right: 4px; top: 4px; bottom: 4px; width: 34px;
  display: grid; place-items: center; border: 0; border-radius: 6px;
  background: transparent; color: var(--muted); cursor: pointer;
}
.cb-toggle:hover { color: var(--fg); background: var(--surface); }
.cb-list {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
  max-height: min(60vh, 360px); overflow-y: auto; margin: 0; padding: 4px 0; list-style: none;
  background: var(--bg); border: 1px solid var(--field-line); border-radius: 8px;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.14);
}
.cb-group {
  position: sticky; top: -4px; padding: 8px 12px 4px; background: var(--bg);
  font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
.cb-option { padding: 7px 12px 7px 20px; cursor: pointer; font-size: 15px; line-height: 1.35; }
.cb-option[aria-selected="true"] { font-weight: 600; }
.cb-option:hover, .cb-option.is-active { background: var(--accent-soft); color: var(--fg); }
.cb-empty { padding: 10px 12px; color: var(--muted); font-size: 14px; }
@media (pointer: coarse) { .cb-option { padding-block: 11px; } }
.as-notes { padding-left: 1.2em; }
.as-notes li + li { margin-top: 6px; }
.cf-table tr.cf-wrap td.cf-num { white-space: normal; text-align: right; }

/* ---------- CLTD by hour ---------- */
.cl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 10px; align-items: end; margin-top: 12px; }
.cl-grid input { width: 100%; min-width: 0; }
.cl-hourly-h { margin-top: 26px !important; }
.cl-hours th, .cl-hours td { padding: 5px 8px; text-align: right; font-variant-numeric: tabular-nums; font-size: 14px; white-space: nowrap; }
.cl-hours thead th { text-transform: none; letter-spacing: 0; font-weight: 600; font-size: 12.5px; color: var(--muted); border-bottom: 1px solid var(--line); }
.cl-hours th:first-child, .cl-hours td:first-child { text-align: left; }
.cl-hours .is-peak { font-weight: 700; color: var(--accent); }

/* ---------- linear interpolation: point grid, graph ---------- */

.cf-stream .li-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.cf-stream .li-grid input { width: 100%; min-width: 0; }
.li-chart { margin: 16px 0 0; }
.li-chart svg { display: block; width: 100%; max-width: 520px; height: auto; }
svg .li-grid { stroke: var(--line); stroke-width: 1; }
.li-tick { fill: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.li-axis { fill: var(--fg); font-size: 13px; font-style: italic; }
.li-ext { stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 5 4; }
.li-line { stroke: var(--fg); stroke-width: 2.5; }
.li-guide { stroke: var(--accent); stroke-width: 1.2; stroke-dasharray: 3 3; }
.li-known { fill: var(--bg); stroke: var(--fg); stroke-width: 2; }
.li-point { fill: var(--accent); stroke: var(--bg); stroke-width: 2; }

/* ---------- About page ---------- */
/* A page that sets proseWide in its front matter uses the full content width. */
.prose.prose-wide { max-width: none; }
.ab-ico { width: 22px; height: 22px; flex: 0 0 22px; color: var(--accent); }
.prose .ab-lede { font-size: 18px; line-height: 1.6; margin: 0 0 24px; }
.ab-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.prose-wide h2 { display: flex; align-items: center; gap: 10px; }
.prose-wide h2::before { content: ""; width: 6px; height: 1.1em; border-radius: 3px; background: var(--brand); }

/* Key figures: boxed, with an accent rule on top. */

/* Text beside a card; the card drops underneath on narrow screens. */
.ab-split { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 34px; align-items: start; margin: 0 0 34px; }
.ab-split-rev { grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); }
.ab-text { max-width: 78ch; }
.ab-text > h2:first-child { margin-top: 0; }
@media (max-width: 860px) { .ab-split, .ab-split-rev { grid-template-columns: minmax(0, 1fr); } }

/* Licensure tile map. */
.ab-mapcard { margin: 0; padding: 16px 16px 12px; }
.ab-cardhead { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 13px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.ab-map { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 3px; }
.ab-tile { aspect-ratio: 1; display: grid; place-items: center; border-radius: 4px; background: var(--bg); border: 1px solid var(--line);
  font-size: clamp(8px, 1.1vw, 11px); font-weight: 600; color: var(--muted); }
.ab-tile.is-on { background: var(--brand); border-color: var(--brand); color: var(--btn-fg); }
.ab-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin: 12px 0 0; font-size: 12.5px; color: var(--muted); }
.ab-key { display: inline-block; width: 12px; height: 12px; border-radius: 3px; background: var(--bg); border: 1px solid var(--line); vertical-align: -1px; margin-right: 4px; }
.ab-key.is-on { background: var(--brand); border-color: var(--brand); }

/* Process: numbered steps joined by arrows (stacked on phones). */
.ab-flow { list-style: none; padding: 0; margin: 6px 0 14px; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 26px; counter-reset: none; }
.prose .ab-flow li { margin: 0; }
.ab-step { position: relative; display: flex; flex-direction: column; gap: 6px; padding: 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.ab-step strong { font-size: 15.5px; }
.ab-step > span:last-child { font-size: 14px; color: var(--muted); line-height: 1.5; }
.ab-stepno { position: absolute; top: 12px; right: 14px; font-size: 26px; font-weight: 800; color: var(--line); line-height: 1; }
.ab-step:not(:last-child)::after { content: "→"; position: absolute; right: -21px; top: 50%; transform: translateY(-50%); font-size: 18px; font-weight: 700; color: var(--brand); }
@media (max-width: 860px) {
  .ab-flow { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .ab-step:not(:last-child)::after { content: "↓"; right: auto; left: 50%; top: auto; bottom: -21px; transform: translateX(-50%); }
}
.ab-flownote { display: flex; align-items: center; gap: 10px; margin: 0 0 34px; padding: 10px 14px; border-radius: 8px; background: var(--accent-soft); font-size: 15px; }

/* Principal: photo card with credential chips, and a timeline. */
.ab-profile { margin: 0; padding: 14px; }
/* A small portrait at its own 4:5 proportions, not a full-width photo. */
.ab-profile img { display: block; width: 160px; max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 0 0 1px var(--line); }
.ab-profile figcaption { margin: 12px 0 0; font-size: 15px; line-height: 1.4; }
.ab-profile figcaption span { display: block; font-size: 13.5px; color: var(--muted); }
.ab-chips { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-wrap: wrap; gap: 6px; }
.prose .ab-chips li { margin: 0; padding: 4px 10px; border-radius: 999px; background: var(--accent-soft); border: 1px solid var(--line); font-size: 12.5px; font-weight: 600; }
.ab-subhead { margin: 6px 0 12px; }
.ab-timeline { list-style: none; padding: 0 0 0 4px; margin: 0 0 36px; position: relative; max-width: 80ch; }
.ab-timeline::before { content: ""; position: absolute; left: 110px; top: 6px; bottom: 6px; width: 2px; background: var(--line); }
.prose .ab-timeline li { position: relative; display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 34px; margin: 0; padding: 7px 0; }
.ab-timeline li::before { content: ""; position: absolute; left: 101px; top: 13px; width: 12px; height: 12px; border-radius: 50%; background: var(--bg); border: 2px solid var(--field-line); }
.ab-timeline li.is-firm::before { background: var(--brand); border-color: var(--brand); }
.ab-when { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; color: var(--muted); }
.ab-timeline li.is-firm .ab-when { color: var(--accent); }

/* Five things: a card grid. */
.ab-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); gap: 14px; margin: 0 0 34px; }
.ab-point { padding: 16px; }
.prose .ab-point h3 { margin: 10px 0 6px; font-size: 16px; }
.prose .ab-point p { margin: 0; font-size: 14.5px; color: var(--muted); line-height: 1.55; }

/* The closing call to action. */
.ab-cta { padding: 22px 24px; border-radius: var(--radius); background: var(--surface-alt); border: 1px solid var(--line); margin: 0 0 10px; }
.prose .ab-cta h2 { margin-top: 0; }
.ab-ctabtns { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }

/* ---------- quote ----------
   The instant-quote form (src/pages/quote.njk, driven by assets/js/quote.js).
   Fields, buttons, notices and the honeypot use the shared classes above; this
   section adds only the tiles, steps, working bar and summaries. */

.quote { max-width: 1024px; }
.quote [hidden] { display: none !important; }
.quote-progress { height: 3px; background: var(--line); margin: 0 0 24px; }
.quote-progress__fill { height: 100%; width: 50%; background: var(--brand); transition: width .3s ease; }

.quote .h2 { font-size: clamp(26px, 4.2vw, 40px); line-height: 1.15; letter-spacing: -0.02em; margin: 6px 0 0; }
.quote h2:not(.h2) { font-size: 21px; margin: 6px 0 0; }
.quote h3 { font-size: 18px; margin: 28px 0 0; }
.quote__body { max-width: 70ch; margin: 14px 0 0; }
.quote .lede { margin-top: 14px; }
.quote .muted { font-size: 14px; }
.quote__pick { font-style: italic; font-weight: 600; margin: 28px 0 12px; }
.quote__label { font-size: 14.5px; font-weight: 600; margin: 28px 0 0; }
.quote__notice { margin: 18px 0 0; }
.quote__group { margin-top: 44px; padding-top: 32px; border-top: 1px solid var(--line); }
.quote .field { max-width: 34rem; margin: 22px 0 0; }

.quote :is(input[type="text"], input[type="email"], select, textarea) {
  width: 100%; font: inherit; font-size: 15px;
  padding: 9px 12px; border: 1px solid var(--field-line); border-radius: 8px;
  background: var(--bg); color: var(--fg);
}
.quote textarea { resize: vertical; }
.quote input[type="file"] { font: inherit; font-size: 14.5px; color: var(--fg); }
.quote :is(input, select, textarea):focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.quote .is-invalid { border-color: #b3261e; background: var(--need); }

.step { display: none; }
.step.is-active { display: block; }
.quote__error { margin: 22px 0 0; font-weight: 600; color: var(--accent); }
.quote .form-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--line); }
/* .form-actions .btn drops the border, which the quiet button needs. */
.quote .btn-quiet { border: 1px solid var(--line); }

/* Submitting can run close to a minute. The bar creeps toward each stage
   rather than reporting real progress, and the stripe keeps moving so the
   page never reads as frozen. */
.working { margin-top: 22px; max-width: 520px; }
.working__bar { height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; }
.working__fill {
  height: 100%; width: 0;
  background: var(--brand)
    linear-gradient(110deg, rgba(255,255,255,.4) 25%, transparent 25%, transparent 50%,
                    rgba(255,255,255,.4) 50%, rgba(255,255,255,.4) 75%, transparent 75%)
    repeat 0 0 / 22px 22px;
  animation: working-stripe 900ms linear infinite;
  transition: width .6s ease;
}
@keyframes working-stripe { to { background-position: 22px 0; } }
.working__text { margin: 12px 0 0; font-weight: 600; }
.working .muted { margin: 4px 0 0; }

.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.tile {
  display: flex; flex-direction: column; gap: 6px; text-align: left;
  padding: 22px; border: 1px solid var(--field-line); border-radius: var(--radius);
  background: var(--bg); color: var(--fg); font: inherit; cursor: pointer;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.tile:hover { border-color: var(--accent); }
.tile.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); background: var(--accent-soft); }
.tile__title { font-weight: 600; font-size: 16px; line-height: 1.35; }
.tile__sub { font-size: 14px; line-height: 1.45; color: var(--muted); }

.estimate { margin-top: 26px; padding: 24px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.estimate__amount { margin: 10px 0 8px; font-size: clamp(40px, 5vw, 60px); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }

.quote .summary__head { margin: 36px 0 0; text-transform: uppercase; letter-spacing: .08em; font-size: 14px; }
.summary { margin: 12px 0 0; }
.summary__row { display: flex; justify-content: space-between; gap: 24px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.summary__row dt { color: var(--muted); flex: 0 0 auto; }
.summary__row dd { margin: 0; text-align: right; font-weight: 600; overflow-wrap: anywhere; }
.summary__row--total { border-bottom: 0; border-top: 2px solid var(--fg); }
.summary__row--total :is(dt, dd) { font-size: 19px; font-weight: 700; color: var(--fg); }

@media (max-width: 750px) {
  .tiles { grid-template-columns: 1fr; }
  .tile { padding: 18px; }
  .quote .form-actions .btn { width: 100%; text-align: center; }
  .summary__row { flex-direction: column; gap: 2px; }
  .summary__row dd { text-align: left; }
}
@media (prefers-reduced-motion: reduce) {
  .quote-progress__fill, .working__fill { transition: none; animation: none; }
}
