@charset "utf-8";
/* ==========================================================================
   WORDPRESS LAYER
   --------------------------------------------------------------------------
   Everything WordPress emits that the static build never had to handle:
   Contact Form 7, widget output, pagination, editor alignment classes.

   Additive only. Built from the DATUM tokens in site.css so it inherits the
   same palette, type and rhythm. Nothing here overrides a DATUM rule.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. CONTACT FORM 7 — mapped onto the .field / .input / .select design
   -------------------------------------------------------------------------- */

/* CF7 wraps each control in <p>; the design spaces on .field instead. */
.wpcf7 form > p,
.wpcf7 .flex-1 > div,
.wpcf7 .flex-2 > div { margin: 0 0 13px; }

.wpcf7 .flex-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 13px; }
@media (max-width: 560px) { .wpcf7 .flex-2 { grid-template-columns: 1fr; } }

.wpcf7 label { display: block; font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }

.wpcf7 input[type="text"], .wpcf7 input[type="email"], .wpcf7 input[type="tel"],
.wpcf7 input[type="url"], .wpcf7 input[type="date"], .wpcf7 input[type="number"],
.wpcf7 select, .wpcf7 textarea {
  width: 100%; background: var(--white); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--r);
  padding: 12px 14px; font-size: 15px; font-family: var(--body);
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.wpcf7 textarea { min-height: 118px; resize: vertical; }
.wpcf7 input:focus, .wpcf7 select:focus, .wpcf7 textarea:focus {
  outline: 0; border-color: var(--brand-400); box-shadow: 0 0 0 3px var(--brand-50);
}
.wpcf7 ::placeholder { color: var(--faint); opacity: 1; }

.wpcf7 input[type="submit"] {
  display: inline-flex; align-items: center; gap: 10px; cursor: pointer;
  font-family: var(--mono); font-size: 12.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  background: var(--brand-600); color: #fff; border: 1px solid transparent;
  border-radius: var(--r); padding: 15px 26px; width: 100%;
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.wpcf7 input[type="submit"]:hover { background: var(--brand-500); transform: translateY(-1px); }

/* Validation + response messages */
.wpcf7-not-valid-tip { display: block; margin-top: 6px; font-family: var(--mono);
  font-size: 11px; letter-spacing: .04em; color: var(--flag-ink); }
.wpcf7 .wpcf7-not-valid { border-color: var(--flag); }
.wpcf7 form .wpcf7-response-output {
  margin: 16px 0 0; padding: 12px 16px; border: 1px solid var(--rule);
  border-left: 3px solid var(--brand-400); border-radius: var(--r);
  font-size: 14.5px; background: var(--white);
}
.wpcf7 form.sent .wpcf7-response-output { border-left-color: var(--ok); }
.wpcf7 form.invalid .wpcf7-response-output,
.wpcf7 form.failed .wpcf7-response-output { border-left-color: var(--flag); }
.wpcf7-spinner { margin: 0 0 0 10px; }

/* reCAPTCHA v3 badge is hidden; the notice in the form covers the terms. */
.grecaptcha-badge { visibility: hidden; }

/* Forms on the brand-blue sidebar panel */
.sb-quote .wpcf7 label { color: rgba(255,255,255,.72); }
.sb-quote .wpcf7 input[type="text"], .sb-quote .wpcf7 input[type="email"],
.sb-quote .wpcf7 input[type="tel"], .sb-quote .wpcf7 input[type="url"],
.sb-quote .wpcf7 select, .sb-quote .wpcf7 textarea {
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.22); color: #fff;
}
.sb-quote .wpcf7 ::placeholder { color: rgba(255,255,255,.5); }
.sb-quote .wpcf7 input:focus, .sb-quote .wpcf7 select:focus, .sb-quote .wpcf7 textarea:focus {
  border-color: rgba(255,255,255,.5); box-shadow: 0 0 0 3px rgba(255,255,255,.1);
}
.sb-quote .wpcf7 input[type="submit"] { background: #fff; color: var(--brand-950); }
.sb-quote .wpcf7 input[type="submit"]:hover { background: var(--brand-50); }
.sb-quote .wpcf7 textarea { min-height: 92px; }
.sb-quote .wpcf7 form .wpcf7-response-output { background: rgba(255,255,255,.08);
  border-color: rgba(255,255,255,.22); color: #fff; }

/* --------------------------------------------------------------------------
   2. WIDGETS — Custom HTML widgets rendered as DATUM sidebar cards
   -------------------------------------------------------------------------- */

/* A plain <ul> inside a sidebar card gets the .sb-list treatment, and the mono
   index numbers are generated rather than typed into the widget. */
.sb-card ul { list-style: none; margin: 0; padding: 0; counter-reset: sbitem; }
.sb-card ul li { border-bottom: 1px solid var(--rule-2); margin: 0; }
.sb-card ul li:last-child { border-bottom: 0; }
.sb-card ul li a {
  display: flex; align-items: baseline; gap: 10px; padding: 11px 0;
  font-size: 14.8px; color: var(--ink-2);
  transition: color .16s var(--ease), padding-left .16s var(--ease);
}
.sb-card ul li a::before {
  counter-increment: sbitem; content: counter(sbitem, decimal-leading-zero);
  font-family: var(--mono); font-size: 10px; color: var(--brand-400); flex: none;
}
.sb-card ul li a:hover { color: var(--brand-600); padding-left: 5px; }
.sb-card ul li a:hover::before { color: var(--flag-ink); }
.sb-card ul li.current-menu-item a,
.sb-card ul li.current a { color: var(--brand-600); font-weight: 600; }

/* Contact-style widgets carry no numbering. */
.sb-card.is-plain ul li a::before { content: none; counter-increment: none; }

/* Widget images, text and the news list */
.sb-card img { border-radius: var(--r); }
.sb-card p { font-size: 15px; color: var(--ink-2); }
.sb-card p:last-child { margin-bottom: 0; }
.sb-posts { display: block; }

/* Footer widget columns keep their own nested wrappers from the old content. */
.footer-brand img { width: 190px; max-width: 100%; }
.footer-col ul,
.footer-col .footer-links ul,
.footer-col .footer-heading ul { list-style: none; margin: 0; padding: 0; }
.footer-brand .socials a svg { width: 17px; height: 17px; }

/* Icon-font leftovers in old widget markup would render as empty boxes. */
.footer-contact i,
.contact-list i,
.sb-card i[class^="la"],
.sb-card i[class*=" la"] { display: none; }

/* --------------------------------------------------------------------------
   3. PAGINATION
   -------------------------------------------------------------------------- */
.pager { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin-top: 44px; }
.pager .page-numbers {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 42px; height: 42px; padding: 0 14px;
  background: var(--white); border: 1px solid var(--rule); border-radius: var(--r);
  font-family: var(--mono); font-size: 12.5px; letter-spacing: .06em; color: var(--ink-2);
  transition: border-color .16s var(--ease), color .16s var(--ease), background .16s var(--ease);
}
.pager a.page-numbers:hover { border-color: var(--brand-300); color: var(--brand-600); }
.pager .page-numbers.current { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }
.pager .page-numbers.dots { border-color: transparent; background: transparent; }

/* --------------------------------------------------------------------------
   4. EDITOR + CORE CLASSES
   -------------------------------------------------------------------------- */
.screen-reader-text {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.entry .alignleft  { float: left;  margin: 6px 28px 20px 0; }
.entry .alignright { float: right; margin: 6px 0 20px 28px; }
.entry .aligncenter { display: block; margin-inline: auto; }
.entry .alignnone { margin-bottom: 20px; }
.entry .wp-caption { max-width: 100%; }
.entry .wp-caption-text { font-family: var(--mono); font-size: 11.5px; color: var(--faint); margin-top: 8px; }
@media (max-width: 620px) {
  .entry .alignleft, .entry .alignright { float: none; margin: 20px 0; }
}

.empty-state {
  padding: 44px; background: var(--white); border: 1px dashed var(--rule);
  border-radius: var(--r-lg); text-align: center; color: var(--muted);
}

.searchform { display: flex; gap: 10px; margin-bottom: 32px; flex-wrap: wrap; }
.searchform .input {
  flex: 1; min-width: 220px; background: var(--white);
  border: 1px solid var(--rule); border-radius: var(--r); padding: 12px 14px;
}

.page-links { font-family: var(--mono); font-size: 12.5px; margin-top: 28px; }

/* Layout variants used by the WordPress templates */
.layout--full { grid-template-columns: minmax(0, 1fr); }
.layout--project { padding-top: clamp(28px, 2vw + 14px, 44px); }

/* Hero image on a single article */
.post-hero { margin: 0 0 34px; }
.post-hero img { width: 100%; border-radius: var(--r-lg); }

/* Project page pieces.
   Scoped to .entry because site.css sets `.entry figure { margin: 32px 0 }` at
   the same specificity plus one — unscoped, the shot would inherit a top margin
   it does not want at the head of the column. */
.entry .project-shot { margin: 0 0 34px; }
.entry .project-shot img {
  display: block; width: 100%;
  border-radius: var(--r-lg); border: 1px solid var(--rule);
}
.project-gallery {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px; margin-top: 40px;
}
.project-gallery img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover;
  border-radius: var(--r); border: 1px solid var(--rule);
}
.project-visit { margin-top: 32px; }

/* Prev and next navigation, shared by posts and projects */
.pnav {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px;
  margin-top: 52px; padding-top: 32px; border-top: 1px solid var(--rule);
}
.pnav-link {
  display: block; padding: 18px 20px; background: var(--white);
  border: 1px solid var(--rule); border-radius: var(--r-lg);
  transition: border-color .18s var(--ease), box-shadow .2s var(--ease);
}
.pnav-link:hover { border-color: var(--brand-200); box-shadow: var(--shadow-sm); }
.pnav-link .k {
  display: block; font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint); margin-bottom: 6px;
}
.pnav-link .v { display: block; font-weight: 600; color: var(--ink); line-height: 1.35; }
.pnav-link--next { text-align: right; }

/* Contact page details list */
.contact-list { list-style: none; margin: 36px 0 0; padding: 0; border-top: 1px solid var(--rule); }
.contact-list li { display: flex; gap: 18px; padding: 14px 0; border-bottom: 1px solid var(--rule-2); }
.contact-list .lbl {
  flex: none; width: 74px; font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint); padding-top: 3px;
}

/* [page_hierarchy] — internal editorial tool, kept plain but legible */
.mfg-page-tree ul { list-style: none; margin: 0; padding-left: 20px; }
.mfg-page-tree > ul { padding-left: 0; }
.mfg-page-tree__item {
  margin-bottom: 12px; padding: 14px 16px; background: var(--white);
  border: 1px solid var(--rule); border-radius: var(--r);
}
.mfg-page-tree__badge {
  display: inline-block; margin-bottom: 6px; padding: 2px 8px;
  background: var(--brand-600); border-radius: var(--r-sm); color: #fff;
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
}
.mfg-page-tree__crumbs { display: block; font-family: var(--mono); font-size: 11px; color: var(--faint); }
.mfg-page-tree__title { display: block; font-weight: 600; color: var(--ink); }
.mfg-page-tree__url { display: block; font-size: 13px; word-break: break-all; }

/* --------------------------------------------------------------------------
   5. CONTACT MODAL
   -------------------------------------------------------------------------- */
.mfg-modal {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center;
  padding: 20px; background: rgba(8, 10, 14, .72); opacity: 0; visibility: hidden;
  transition: opacity .22s var(--ease), visibility .22s;
}
.mfg-modal.is-open { opacity: 1; visibility: visible; }
.mfg-modal[hidden] { display: none; }
.mfg-modal__dialog { position: relative; width: min(560px, 100%); max-height: 90vh; overflow-y: auto; }
.mfg-modal__close {
  position: absolute; top: 14px; right: 14px; width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .14); border: 0; border-radius: var(--r);
  color: #fff; font-size: 20px; line-height: 1; cursor: pointer;
}
.mfg-modal__close:hover { background: rgba(255, 255, 255, .26); }

/* --------------------------------------------------------------------------
   6. CORRECTIONS
   -------------------------------------------------------------------------- */

/*
 * `strong, b { color: var(--ink) }` also catches bold text inside solid
 * buttons, which renders the label dark-on-dark. The editor wraps most button
 * labels in <b>, so the colour has to be inherited back.
 */
.inner-page-button b,
.inner-page-button strong,
.btn b,
.btn strong,
.inner-page-cta-banner-content > p:last-child a b,
.inner-page-cta-banner-content > p:last-child a strong { color: inherit; }

/* Sidebar list items that are steps rather than links carry their own spacing,
   since the DATUM rules put the padding on the anchor. */
.sb-list > li > span:not(.idx) { display: block; }
.sb-list > li:not(:has(a)) {
  display: flex; align-items: baseline; gap: 10px; padding: 11px 0;
  font-size: 14.8px; color: var(--ink-2); line-height: 1.5;
}
.sb-list > li:not(:has(a)) .idx {
  font-family: var(--mono); font-size: 10px; color: var(--brand-400); flex: none;
}

/* Footer widget content supplies its own <h3>; DATUM styles the widget-title
   <h4>. Match them so migrated widgets read the same as authored columns. */
.footer-col h3 {
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: #fff; margin-bottom: 18px; padding-bottom: 12px;
  border-bottom: 1px solid rgba(255, 255, 255, .12);
}

/* The modal sits on a dark scrim, so the white panel needs a little lift. */
.mfg-modal .form-panel { box-shadow: var(--shadow-lg); }

/* --------------------------------------------------------------------------
   7. FORM REFINEMENTS
   -------------------------------------------------------------------------- */

/*
 * CF7 keeps its response box in the DOM at all times. With a border and padding
 * that reserved dead space under every form, so it only shows once the form has
 * something to say.
 */
.wpcf7 form .wpcf7-response-output { display: none; }
.wpcf7 form.sent .wpcf7-response-output,
.wpcf7 form.invalid .wpcf7-response-output,
.wpcf7 form.failed .wpcf7-response-output,
.wpcf7 form.aborted .wpcf7-response-output,
.wpcf7 form.spam .wpcf7-response-output,
.wpcf7 form.unaccepted .wpcf7-response-output,
.wpcf7 form.payment-required .wpcf7-response-output { display: block; }

.wpcf7 form > p:last-child,
.wpcf7 .flex-1:last-child > div:last-child { margin-bottom: 0; }

/* The hero panel sits beside the headline, so its message field stays compact. */
.hero .form-panel .wpcf7 textarea { height: 122px; min-height: 122px; }
.hero .form-panel { padding: 30px 28px; }

/* --------------------------------------------------------------------------
   8. CREDENTIAL STRIP — slimmer
   --------------------------------------------------------------------------
   The claims inherit mono + uppercase from the wrapper span, which is wide
   enough that every one of them wrapped to two lines and the qualifiers wrapped
   again under them — four lines of text in a strip that should read at a
   glance. Claims move to the display face in sentence case, which fits on one
   line at the same size, and the padding comes in to match.
   -------------------------------------------------------------------------- */
.cred {
  padding: 15px 20px 15px 0;
  margin-right: 20px;
  align-items: center;
  gap: 14px;
}

.cred .ic {
  width: 27px;
  height: 27px;
  margin-top: 0;
}

.cred .ic svg { stroke-width: 1.5; }

.cred b {
  font-family: var(--display);
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -.008em;
  text-transform: none;
  color: var(--ink);
  line-height: 1.3;
}

.cred > span > span {
  font-size: 9px;
  letter-spacing: .05em;
  margin-top: 3px;
  line-height: 1.35;
}

/* Below this the four columns are too narrow for one-line claims, so the strip
   goes two-up rather than cramping all four. */
@media (max-width: 1180px) and (min-width: 861px) {
  .cred { flex: 1 1 calc(50% - 20px); }
  .cred:nth-child(2n) { border-right: 0; margin-right: 0; padding-right: 0; }
  .cred:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
}

@media (max-width: 860px) {
  .cred { padding: 13px 0; }
}

/* --------------------------------------------------------------------------
   9. FAQ ACCORDION
   --------------------------------------------------------------------------
   faq.js drives the height in inline styles; this supplies the easing. Only
   applied once the script has marked the group, so with JS off the panels stay
   plain <details> and open instantly.
   -------------------------------------------------------------------------- */
.faq-accordion .faq-body {
  transition: height .3s var(--ease), padding-bottom .3s var(--ease);
  will-change: height;
}

@media (prefers-reduced-motion: reduce) {
  .faq-accordion .faq-body { transition: none; }
}

/* The marker rotates with the panel rather than snapping. */
.faq-item > summary::after { transition: transform .3s var(--ease); }

/* --------------------------------------------------------------------------
   10. PAGE HEADER IMAGE
   --------------------------------------------------------------------------
   mfg_page_header() sets --phead-img from the page's Banner Image field, a
   category's term meta, or the theme default. The overlay below is what keeps
   the white heading readable: near-solid on the left where the h1, deck and
   breadcrumbs sit, thinning to the right so the photograph still reads.

   Every banner is composed with its subject on the right for this reason. The
   gradient never drops below .70 because the spec strip spans the full width.
   -------------------------------------------------------------------------- */
.phead::before {
  background-image:
    radial-gradient(110% 130% at 88% -10%, rgba(67, 103, 198, .08) 0%, transparent 56%),
    linear-gradient(90deg, rgba(8, 10, 14, .96) 0%, rgba(8, 10, 14, .88) 55%, rgba(8, 10, 14, .70) 100%),
    var(--phead-img, none);
  background-color: var(--brand-950);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

/* Narrow screens crop hard to the centre, so lift the floor for legibility. */
@media (max-width: 720px) {
  .phead::before {
    background-image:
      linear-gradient(90deg, rgba(8, 10, 14, .96) 0%, rgba(8, 10, 14, .90) 100%),
      var(--phead-img, none);
  }
}

/* --------------------------------------------------------------------------
   11. SHORTCODE BACKGROUNDS
   --------------------------------------------------------------------------
   Hardcoded rather than editable — these are part of the design, not content.
   Each sits under an overlay heavy enough to hold its own text contrast.
   -------------------------------------------------------------------------- */

/* [showcta] — deep band. Its ::before is an empty layer in site.css. */
.inner-page-cta-banner.ctabg1::before {
  background-image:
    radial-gradient(80% 140% at 100% 0%, rgba(67, 103, 198, .06) 0%, transparent 62%),
    linear-gradient(0deg, rgba(8, 10, 14, .90), rgba(8, 10, 14, .90)),
    url("../img/band-dark-01.webp");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

/* [showcta2] — mid band. */
.inner-page-cta-banner.ctabg2::before {
  background-image:
    radial-gradient(80% 140% at 0% 100%, rgba(8, 10, 14, .6) 0%, transparent 60%),
    linear-gradient(0deg, rgba(18, 22, 29, .86), rgba(18, 22, 29, .86)),
    url("../img/band-mid-01.webp");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

/* [focus2] — dark panel, photograph weighted right behind white text. */
.inner-page-focus2::before {
  background-image:
    radial-gradient(90% 120% at 92% 4%, rgba(255, 255, 255, .13) 0%, transparent 58%),
    linear-gradient(90deg, rgb(18, 22, 29) 0%, rgba(18, 22, 29, .92) 50%, rgba(18, 22, 29, .72) 100%),
    url("../img/focus2-plate.webp");
  background-color: var(--brand-600);
  background-repeat: no-repeat;
  background-position: center right;
  background-size: cover;
}

/*
 * [focus3] — light panel. Its ::before and ::after are already spent on the
 * corner tick marks, so the duotone goes on the element itself.
 *
 * Held much further back than focus2. These panels can run very tall, and a
 * 16:9 image under `cover` over-zooms in a tall box until the pattern spreads
 * behind every line of copy — which dark text on a light ground tolerates far
 * less than white text on a dark one.
 */
.inner-page-focus3 {
  background-image:
    linear-gradient(90deg, rgb(238, 241, 248) 0%, rgba(238, 241, 248, .96) 45%, rgba(238, 241, 248, .84) 100%),
    url("../img/focus3-turned.webp");
  background-color: var(--brand-50);
  background-repeat: no-repeat;
  background-position: center right;
  background-size: cover;
}

/* --------------------------------------------------------------------------
   12. FIXED CORNER MARK
   --------------------------------------------------------------------------
   Pinned to the viewport, behind the content, on inner pages only. Inner pages
   render as .wrap.layout with no opaque section wrapper, so the body paper
   shows through and a z-index:-1 layer is visible. .phead sets isolation, so
   the mark correctly stops at the header.

   --mfg-corner is set per page by mfg_corner_mark_style(); with no image set
   the layer resolves to none and paints nothing.
   -------------------------------------------------------------------------- */
body:not(.home)::after {
  content: "";
  position: fixed;
  right: 0;
  bottom: 0;
  width: min(42vw, 560px);
  height: min(56vh, 620px);
  background-image: var(--mfg-corner, none);
  background-repeat: no-repeat;
  background-position: right bottom;
  background-size: contain;
  opacity: var(--mfg-corner-opacity, .5);
  pointer-events: none;
  z-index: -1;

  /*
   * The mark is a photograph on a rectangular canvas, so composited normally
   * its edge shows as a tonal step against the paper. The source images have
   * their backgrounds clipped to pure white, and multiplying by white is a
   * no-op — so the box disappears and only the part itself darkens the page,
   * whatever colour the surface underneath happens to be.
   */
  mix-blend-mode: multiply;
}

/* Bottom-right is where the mobile CTA and form land — pull it off entirely. */
@media (max-width: 1100px) {
  body:not(.home)::after { display: none; }
}

/* --------------------------------------------------------------------------
   13. BIO FIGURE
   --------------------------------------------------------------------------
   Portrait headshot set beside a biography, floated so the copy wraps. Sized
   as a fraction rather than a fixed width so it never crowds the measure on a
   narrow content column, and cleared by the next heading so a short bio cannot
   let the photo bleed into the section below.
   -------------------------------------------------------------------------- */
.entry .bio-figure {
  float: right;
  width: min(320px, 38%);
  margin: 4px 0 26px 14px;
  padding: 0;
}

.entry .bio-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-lg);
  border: 1px solid var(--rule);
}

/* A float that outlives its own section is the classic failure here. */
.entry .bio-figure + * { margin-top: 0; }
.entry h2 { clear: both; }

@media (max-width: 700px) {
  .entry .bio-figure {
    float: none;
    width: 100%;
    max-width: 300px;
    margin: 0 0 24px;
  }
}

/* --------------------------------------------------------------------------
   14. SIDEBAR CONTACT PANEL
   --------------------------------------------------------------------------
   The terminal block of a sidebar. It sits directly beneath .sb-quote, which is
   already brand-blue, so repeating that blue would make two panels read as one
   long block. This goes to the darkest ground in the ramp instead and takes the
   safety orange as a top edge — the same "flagged datum" role the accent plays
   everywhere else, not decoration.
   -------------------------------------------------------------------------- */
.sb-contact {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 26px 24px 24px;
  border-radius: var(--r-lg);
  background: var(--brand-950);
  box-shadow: var(--shadow-lg);
}

/* Depth, so it does not read as a flat rectangle against the paper. */
.sb-contact::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(85% 120% at 100% 0%, rgba(67, 103, 198, .11) 0%, transparent 60%),
    repeating-linear-gradient(to right, rgba(255, 255, 255, .04) 0 1px, transparent 1px 42px);
}

/* The flag. */
.sb-contact::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--flag);
}

.sb-contact .sb-head {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, .18);
}

.sb-contact-row {
  display: block;
  padding: 12px 0;
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}
.sb-contact-row:last-of-type { border-bottom: 0; }

.sb-contact-row .k {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--brand-300);
  margin-bottom: 5px;
}

/* Phone is the primary action here, so it carries the display face. */
.sb-contact-row .v {
  display: block;
  font-family: var(--display);
  font-size: 1.32rem;
  font-weight: 650;
  letter-spacing: -.01em;
  line-height: 1.2;
  color: #fff;
}

.sb-contact-row .v--sm {
  font-family: var(--body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0;
  word-break: break-word;
}

/* Beat `.entry a` and the sidebar link rules without !important. */
.sb .sb-contact a.sb-contact-row,
.sb-contact a.sb-contact-row {
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, .1);
  transition: border-color .16s var(--ease);
}
.sb-contact a.sb-contact-row:last-of-type { border-bottom: 0; }
.sb-contact a.sb-contact-row:hover { border-bottom-color: var(--flag); }
.sb-contact a.sb-contact-row:hover .v { color: var(--flag-lite); }

.sb-contact .sb-contact-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 20px;
  padding: 15px 20px;
  border-radius: var(--r);
  background: #fff;
  color: var(--brand-950);
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 0;
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.sb-contact .sb-contact-btn:hover {
  background: var(--brand-50);
  transform: translateY(-1px);
  color: var(--brand-950);
}

.sb-contact .sb-note {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .06em;
  color: rgba(255, 255, 255, .58);
  margin: 14px 0 0;
  line-height: 1.6;
  text-align: center;
}

/* --------------------------------------------------------------------------
   15. STICKY SIDEBAR CTA
   --------------------------------------------------------------------------
   Both classes here are added by sidebar-cta.js, never by the markup. With
   JavaScript off the contact panel stays an ordinary visible widget instead of
   an invisible one — the hiding is the enhancement, not the default.
   -------------------------------------------------------------------------- */

/*
 * `.layout` sets `align-items: start`, so the sidebar column is only as tall as
 * its widgets — a sticky child there has almost no travel and stalls partway
 * down a long article. Stretching the column to the grid row is what lets the
 * panel follow to the end.
 */
.sidebar.has-sticky-cta { align-self: stretch; }

.sb-contact.is-sticky-cta {
  position: sticky;
  top: 96px;              /* clears the 77px sticky site header */
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  /* Visibility waits out the fade on the way out, but switches immediately on
     the way in — and it is `visibility`, not `opacity` alone, so the links
     inside cannot be reached by keyboard while the panel is hidden. */
  transition:
    opacity .35s var(--ease),
    transform .35s var(--ease),
    visibility 0s linear .35s;
}

.sb-contact.is-sticky-cta.is-revealed {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity .35s var(--ease),
    transform .35s var(--ease),
    visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  .sb-contact.is-sticky-cta,
  .sb-contact.is-sticky-cta.is-revealed {
    transform: none;
    transition: opacity .01s linear, visibility 0s;
  }
}

/* Safety net: the script also drops the classes here, but if it has not run
   yet on a narrow load the panel must still render normally. */
@media (max-width: 900px) {
  .sidebar.has-sticky-cta { align-self: start; }
  .sb-contact.is-sticky-cta {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   16. SIDEBAR NEWS SCROLLER
   --------------------------------------------------------------------------
   One post at a time — image and title only — to break up a sidebar that is
   otherwise a column of text. Scroll-snap does the work, so it swipes on touch
   and scrolls with a trackpad without any JavaScript; news-scroller.js only
   adds the dots, and hides them from assistive tech since every slide is
   already a plain link in the track.
   -------------------------------------------------------------------------- */
.sb-scroller { margin: 0; }

.sb-scroller-track {
  display: flex;
  gap: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;             /* Firefox */
  -ms-overflow-style: none;          /* old Edge  */
}
.sb-scroller-track::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .sb-scroller-track { scroll-behavior: auto; }
}

.sb-slide {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
  display: block;
  border-bottom: 0;
}

.sb-slide-media {
  display: block;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--brand-50);
}

.sb-slide-media img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 0;
  transition: transform .45s var(--ease);
}
.sb-slide:hover .sb-slide-media img { transform: scale(1.04); }

.sb-slide .t {
  display: block;
  margin-top: 12px;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.38;
  color: var(--ink);
  transition: color .16s var(--ease);
}
.sb-slide:hover .t { color: var(--brand-600); }

/* Dots. Only rendered once the script has counted the slides. */
.sb-scroller-dots {
  display: flex;
  justify-content: center;
  gap: 7px;
  margin-top: 14px;
}
.sb-scroller-dots:empty { display: none; }

.sb-scroller-dots button {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--rule);
  cursor: pointer;
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.sb-scroller-dots button:hover { background: var(--brand-300); }
.sb-scroller-dots button.is-active {
  background: var(--flag);
  transform: scale(1.25);
}

/* --------------------------------------------------------------------------
   17. TYPE — RETIRE THE MONO FACE
   --------------------------------------------------------------------------
   The DATUM system set eyebrows, labels, breadcrumbs, spec strips and button
   text in IBM Plex Mono. That is dropped in favour of the body face.

   Done by redefining the token rather than rewriting rules: `--mono` is used in
   66 places across site.css and wp.css, and site.css is the client's static
   build and cannot be edited. Redefining here — wp.css loads after — reaches
   every one of them from a single line.

   The token keeps its name for that reason; renaming it would mean touching
   all 66 usages, half of them in a file that is deliberately frozen. Nothing
   else in the stylesheets sets a monospace family, and no code or pre rule
   depended on it, so nothing is left mis-set.

   Small uppercase labels lose the even digit widths mono gave them, so the
   places where figures stack and misalignment would show ask for tabular
   numerals explicitly below.
   -------------------------------------------------------------------------- */
:root {
  --mono: var(--body);
}

/* Only where digits actually appear and need even widths: the generated sidebar
   counters and nav indices, which stack vertically; the spec strip's values,
   which carry the launch year; and the pager, whose numbers sit in fixed-width
   boxes. The spec strip's *labels* are words, so they are not listed. */
.sb-card ul li a::before,
.submenu .idx,
.sb-list .idx,
.phead-spec .v,
.pager .page-numbers {
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   18. HERO BACKGROUND VIDEO
   --------------------------------------------------------------------------
   site.css gives `.hero::before` a final, fully opaque `var(--brand-950)` layer,
   which would hide anything behind it. When a video is present that layer is
   replaced with a scrim: near-solid on the left where the white h1, lede and
   stat row sit, thinning to the right, which the form panel covers anyway.

   Measured on the graded clip, the brightest pixel under the headline is 216.
   At the .86 the scrim gives there, white text lands around 11:1; at the .62 on
   the far right it is still about 6:1.
   -------------------------------------------------------------------------- */
.hero.has-video {
  /* Paints below the negative-z-index video, so hiding the video reveals it. */
  background-image: var(--hero-poster, none);
  background-size: cover;
  background-position: center;
}

.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -3;
  pointer-events: none;
  border: 0;
}

.hero.has-video::before {
  background:
    radial-gradient(120% 90% at 82% 8%, rgba(67, 103, 198, .08) 0%, transparent 58%),
    linear-gradient(90deg,
      rgba(8, 10, 14, .86) 0%,
      rgba(8, 10, 14, .74) 50%,
      rgba(8, 10, 14, .62) 100%);
}

/*
 * A looping clip is motion. Reduced-motion readers get the poster frame, which
 * is already painted on .hero underneath — so hiding the element is the whole
 * fix, with no script and no empty panel.
 */
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
}

/*
 * The video used to be hidden below 700px, to "save the download on the
 * connections least able to afford it". That reasoning was wrong, and measuring
 * it on live is what showed it: at a 390px viewport the element reported
 * `readyState: 4` with `currentSrc` set — `display: none` does not stop a
 * `<video>` with `autoplay` from fetching, so the phone downloaded all 1.93 MB
 * and then showed the poster instead. Hiding it cost the bandwidth and returned
 * nothing.
 *
 * (The file is 1.93 MB, not the 1.5 MB the old comment claimed — that figure
 * predates the regrade.)
 *
 * Since the clip arrives either way, showing it is strictly better than hiding
 * it. Checked at 390px: it plays, and object-fit: cover fills the 375x1375 hero
 * from a 1920x1080 source. That is a heavy crop to a central vertical slice,
 * which is what the old comment meant by "mostly cropped away" — but the
 * machining footage still reads, so the crop is a cost worth paying for motion
 * that has already been paid for in bytes.
 *
 * If the bandwidth is ever the real concern, the fix is to stop the fetch —
 * drop `autoplay`/`preload` for narrow viewports and call play() from a media
 * query match — not to hide an element that has already downloaded.
 *
 * The prefers-reduced-motion rule above has the same fetch-anyway flaw. Left as
 * it is: it is correct about what it shows, and that audience is small.
 */

/* --------------------------------------------------------------------------
   19. HERO HEIGHT
   --------------------------------------------------------------------------
   The hero ran taller than it needed to. The stat row is gone — cleared at the
   fields, since `mfg_hero_proof()` renders nothing when they are empty, and the
   same figures already appear in the stat band further down the page, more
   completely. So nothing was lost, only stopped from being said twice.

   The headline is trimmed about 12% from the site-wide h1 scale, scoped to the
   hero so page headers keep the original size.
   -------------------------------------------------------------------------- */
.hero h1 {
  font-size: clamp(1.95rem, 1.2rem + 2.3vw, 3.25rem);
}

/* --------------------------------------------------------------------------
   20. HOME PAGE REVISIONS
   -------------------------------------------------------------------------- */

/* --- Intro section, now on the deep ground ------------------------------- */

/* `.split` centres its columns; this one reads better with both starting at
   the same baseline, since the two sides are unequal in length. */
.split--top { align-items: start; }

/*
 * The right column is `.entry`, whose body and heading colours are built for
 * paper. site.css only re-colours bare `p` inside `.section--deep`, so the
 * entry's own rules would otherwise leave dark text on dark ground.
 */
.section--intro .entry,
.section--intro .entry p,
.section--intro .entry li { color: rgba(255, 255, 255, .82); }
.section--intro .entry h2,
.section--intro .entry h3,
.section--intro .entry h4,
.section--intro .entry strong,
.section--intro .entry b { color: #fff; }
/* Links here are covered by the dark-section rule in section 24. */
.section--intro .lede { color: rgba(255, 255, 255, .78); }

/* --- Services: wider, and the CTA as the eighth card --------------------- */

/*
 * The container stays the site-wide 1280; it is the *text* that runs full
 * width here. site.css caps `.section-head` at 760px, which suits a heading
 * sitting beside something else — but this one has the row to itself, and the
 * intro copy reads better across the whole measure than stacked in a column
 * half the section wide.
 */
.section--services .section-head { max-width: none; }
.section--services .section-head p { max-width: none; }

/*
 * The call to action closes the sequence rather than sitting beside it: the
 * numbering runs straight on from the services, and the card inverts so it
 * still reads as an endpoint and not an eighth service.
 */
.svc--cta {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
  position: relative;
  isolation: isolate;
}
.svc--cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(90% 130% at 100% 0%, rgba(255, 255, 255, .14) 0%, transparent 58%);
}
.svc--cta .tag { color: var(--flag-lite); }
.svc--cta h3 { color: #fff; }
.svc--cta p { color: rgba(255, 255, 255, .8); }
.svc--cta .go { color: #fff; }
.svc--cta:hover {
  background: var(--brand-500);
  border-color: var(--brand-500);
}

/* --- Stat band: icons and counters --------------------------------------- */

.stat-ic {
  display: block;
  width: 26px;
  height: 26px;
  margin-bottom: 16px;
  color: var(--brand-200);
}
.stat-ic svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Digits change width as they count, so hold the column steady. */
.stat .n[data-count] { font-variant-numeric: tabular-nums; }

/* --- Portfolio slider: three across, with arrows ------------------------- */

/*
 * Exactly three per view rather than the previous clamp, which left a sliver
 * of a fourth card visible at most widths. The gap has to come out of the
 * card width or the third one is pushed out of frame.
 */
.portfolio-item { flex: 0 0 calc((100% - 40px) / 3); }

@media (max-width: 940px) {
  .portfolio-item { flex: 0 0 calc((100% - 20px) / 2); }
}
@media (max-width: 620px) {
  .portfolio-item { flex: 0 0 100%; }
}

.portfolio-slider.has-arrows { position: relative; }

.pf-arrow {
  position: absolute;
  top: calc(50% - 46px);          /* centres on the image, not the caption */
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--white);
  color: var(--brand-600);
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), opacity .18s var(--ease);
}
.pf-arrow svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pf-arrow:hover {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}
.pf-arrow--prev { left: -18px; }
.pf-arrow--next { right: -18px; }

/* On narrow screens there is no room outside the track, so tuck them in. */
@media (max-width: 1360px) {
  .pf-arrow--prev { left: 8px; }
  .pf-arrow--next { right: 8px; }
}
@media (max-width: 620px) {
  .pf-arrow { display: none; }   /* swiping is the better affordance here */
}

/* --------------------------------------------------------------------------
   21. SECTION BACKGROUND ART
   --------------------------------------------------------------------------
   Photography set into a section rather than dropped on it as a picture. The
   art is exported with an alpha fade instead of a matched background colour:
   colour-matching a generated image never quite lands — this one arrived at
   rgb(9,24,55) against the section's rgb(3,16,48) — and the moment the section
   colour changed it would be wrong again. With alpha the section shows through
   directly and there is nothing to match.
   -------------------------------------------------------------------------- */
/*
 * Two layers. The first is a wash of the section's own colour laid over the
 * art — a background image cannot take `opacity`, and this keeps the strength
 * tunable here rather than baked into the file. Where there is no art it
 * composites the section colour over the section colour, so it costs nothing
 * and only the photograph is dimmed.
 *
 * The wash is graded rather than flat. At 110% the art reaches up behind the
 * heading, where a veil light enough to keep the photograph interesting left
 * white text at 2.8:1 against its brightest point — measured, not guessed. So
 * it runs heavy over the copy and releases below the button, which is the empty
 * corner the art was added to fill in the first place.
 */
.section--intro {
  background-image:
    /* Across: the spark trail runs the full width of the frame, and the right
       column's body copy sits over the far end of it. Dimming rather than
       masking keeps the trail continuous — sparks lose intensity with distance
       anyway, so this reads as depth rather than as a cut. */
    linear-gradient(
      to right,
      rgba(8, 10, 14, 0) 0%,
      rgba(8, 10, 14, 0) 40%,
      rgba(8, 10, 14, .58) 68%,
      rgba(8, 10, 14, .72) 100%
    ),
    /* Down: heavy over the heading, releasing into the corner below the button. */
    linear-gradient(
      to bottom,
      rgba(8, 10, 14, .86) 0%,
      rgba(8, 10, 14, .84) 55%,
      rgba(8, 10, 14, .82) 82%,
      rgba(8, 10, 14, .20) 100%
    ),
    url("../img/intro-sparks.webp");
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-position: center, center, left bottom;
  background-size: auto, auto, 70%;
}

/* Stacked layout: the columns are full width, so the art would sit under the
   copy rather than beside it. */
@media (max-width: 900px) {
  .section--intro { background-image: none; }
}

/* --------------------------------------------------------------------------
   22. STAT BAND
   --------------------------------------------------------------------------
   Was a flat brand-600 slab with small low-contrast icons — the only block on
   the page with no depth, sitting between two light sections where it had the
   most work to do.

   Rebuilt as an instrument panel, which is what the numbers actually are: a
   deep ground with the same radial glow and hairline grid the hero and page
   headers use, so it reads as part of that family rather than a coloured strip
   dropped between sections. Each reading gets an orange datum flag — the same
   role the accent plays on `.eyebrow` and `.sb-head` — and the separators fade
   out at both ends instead of ruling hard edge to edge.
   -------------------------------------------------------------------------- */
.statband {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--brand-950);
}

/* Depth. Two offset glows so the band is lit from one corner rather than flat. */
.statband::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(80% 150% at 12% 0%, rgba(67, 103, 198, .10) 0%, transparent 60%),
    radial-gradient(70% 130% at 92% 100%, rgba(18, 22, 29, .60) 0%, transparent 58%);
}

/* The measurement grid, faded downward so it never competes with the figures. */
.statband::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    repeating-linear-gradient(to right, rgba(255, 255, 255, .05) 0 1px, transparent 1px 76px);
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), transparent 92%);
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), transparent 92%);
}

/*
 * The figure leads on its own line, then the icon sits inline to the left of
 * the label. Ordered here rather than in the markup so the source stays
 * number-then-label — which is the order it should be read aloud in.
 */
.stat {
  position: relative;
  padding: 58px 34px 54px 0;
  border-right: 0;                 /* replaced by the fading rule below */
  display: flex;
  flex-wrap: wrap;
  /* First-line alignment, not centre: one label wraps, and centring would
     drop its icon off the line the other three sit on. */
  align-items: flex-start;
  column-gap: 12px;
}
.stat .n     { order: 1; flex: 0 0 100%; }
.stat .stat-ic { order: 2; }
.stat .l     { order: 3; flex: 1 1 0; min-width: 0; }
.statband .wrap > .stat + .stat { padding-left: 34px; }

/* Separator that fades at both ends rather than ruling the full height. */
.statband .wrap > .stat + .stat::after {
  content: "";
  position: absolute;
  left: 0;
  top: 14%;
  bottom: 14%;
  width: 1px;
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(255, 255, 255, .18) 20%,
    rgba(255, 255, 255, .18) 80%,
    transparent
  );
}

/* The datum flag marking each reading. */
.stat::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 24px;
  height: 3px;
  background: var(--flag);
}
.statband .wrap > .stat + .stat::before { left: 34px; }

/* Sized against the label it now sits beside, not against the figure. */
.stat-ic {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  margin-bottom: 0;
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: var(--r);
  background: rgba(255, 255, 255, .04);
  color: #fff;
}
.stat-ic svg { width: 16px; height: 16px; stroke-width: 1.5; }

.stat .n {
  font-size: clamp(2.7rem, 1.9rem + 3vw, 4.1rem);
  letter-spacing: -.038em;
  margin-bottom: 16px;
}

.stat .l {
  color: var(--brand-200);
  font-size: 11.5px;
  letter-spacing: .1em;
  line-height: 1.55;
  padding-top: 6px;   /* centres the first line against the 30px icon */
}

/* Two-up: every odd cell starts a row, so it takes no separator and its flag
   returns to the container edge. */
@media (max-width: 860px) {
  .stat { padding: 42px 24px 40px 0; }
  .statband .wrap > .stat + .stat { padding-left: 24px; }
  .statband .wrap > .stat:nth-child(odd) { padding-left: 0; }
  .statband .wrap > .stat:nth-child(odd)::after { display: none; }
  .statband .wrap > .stat:nth-child(odd)::before { left: 0; }
  .statband .wrap > .stat:nth-child(even)::before { left: 24px; }
}

/* --------------------------------------------------------------------------
   23. SERVICE CARDS
   --------------------------------------------------------------------------
   Whole-card links carrying a title and two lines, over art. The number tag and
   the "Explore" affordance are gone: the entire card is already an <a>, so both
   were labelling a link that did not need labelling.

   The card is dark with or without art. Building it to *depend* on an image
   would leave a broken-looking grid whenever one is missing, and the fallback
   ground is what the art sits on anyway.
   -------------------------------------------------------------------------- */
.svc {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;   /* copy sits at the foot, art reads above it */
  min-height: 268px;
  padding: 26px 24px 24px;
  background: var(--brand-950);
  border-color: rgba(255, 255, 255, .10);
}

/* The art, when a card has one. */
/*
 * site.css already uses `.svc::before` for the orange accent bar that wipes in
 * on hover: `width: 3px` and `transform: scaleY(0)`. Both are inherited here,
 * so the art has to unset them explicitly — without this it renders as a 3px
 * sliver scaled to zero height, which is to say not at all, while still
 * computing a perfectly valid background-image.
 *
 * The accent bar is given up on cards that carry art; they get the image and
 * its scale-on-hover instead.
 */
.svc.has-art::before {
  content: "";
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  transform: none;
  z-index: 0;
  background-image: var(--svc-art, none);
  background-size: cover;
  background-position: center;
  transition: transform .55s var(--ease);
}

/*
 * Legibility scrim. Weighted almost entirely to the foot, because the art is
 * already dark by brief — measured at mean 27–51 — and the card behind it is
 * darker still. A scrim heavy at the top composited the photographs down to
 * roughly 29 luma against a card at 15, which rendered them invisible: present
 * in the DOM, doing nothing on screen.
 *
 * The stops hold near-opaque through the copy band (the foot 44% of the card)
 * then fall away fast above it. Measured with the text hidden: behind the copy
 * the ground peaks at 53-63 luma on all seven cards, so white type sits at
 * better than 9.9:1, while the top strip stays at mean 42-87 and the picture
 * still reads.
 */
.svc.has-art::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to top,
    rgba(8, 10, 14, .92) 0%,
    rgba(8, 10, 14, .88) 38%,
    rgba(8, 10, 14, .76) 56%,
    rgba(8, 10, 14, .52) 70%,
    rgba(8, 10, 14, .22) 84%,
    rgba(8, 10, 14, .05) 100%
  );
}

.svc:hover.has-art::before { transform: scale(1.05); }

.svc h3 {
  color: #fff;
  margin: 0 0 8px;
  font-size: 1.16rem;
  line-height: 1.25;
}

.svc p {
  color: rgba(255, 255, 255, .78);
  font-size: 13.5px;
  line-height: 1.5;
  margin: 0;
}

.svc:hover {
  border-color: var(--brand-400);
  transform: translateY(-3px);
}

/* The CTA card keeps its brand ground and its explicit affordance — it is the
   one card whose destination is not obvious from a service name. */
.svc--cta { background: var(--brand-600); min-height: 268px; }
.svc--cta .go {
  margin-top: 14px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/*
 * Explicit stacking, not negative z-index. A negative index on the art put it
 * behind the card's own background here and it never painted — confirmed by
 * flooding the pseudo-element red and getting nothing. Positive levels with the
 * copy lifted above them is unambiguous and does not depend on how the
 * background of a stacking-context element interacts with negative children.
 */
.svc h3,
.svc p,
.svc .go {
  position: relative;
  z-index: 2;
}

/* --------------------------------------------------------------------------
   24. DARK-SECTION LINKS, AND THE DIFFERENCE SECTION ART
   --------------------------------------------------------------------------
   site.css sets `.entry a` to --brand-600 at (0,3,1). On a --brand-950 ground
   that measured 1.62:1 — the Fox Business and MEP links in the authority
   section were effectively invisible. site.css already carries a note about
   this ("or links go dark-on-dark") and patches the one case it knew about,
   `.f2-inner`; this generalises the fix to every dark section so the next one
   does not reintroduce it.

   Matching (0,3,1) exactly and winning on source order, since wp.css loads
   after site.css. No !important needed.
   -------------------------------------------------------------------------- */
.section--deep .entry a:not(.btn),
.section--brand .entry a:not(.btn) {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, .4);
}

.section--deep .entry a:not(.btn):hover,
.section--brand .entry a:not(.btn):hover { border-bottom-color: var(--flag); }

/*
 * The authority section's art. The split leaves its right half empty, so the
 * photograph fills it and dissolves leftward into the section's own ground.
 *
 * The fade is baked into the file as alpha, not painted as a matching colour:
 * the image carries no #031030 of its own, so if the section colour ever
 * changes the art follows it instead of leaving a visible rectangle. Same
 * approach as `.section--intro`.
 *
 * `cover` bleeds it to all four section edges — a photograph that stops short
 * of an edge reads as a placed square, which is the thing being avoided.
 */
.section--difference {
  background-image: url("../img/difference-cnc.webp");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: cover;
}

/* Below the split's 900px breakpoint the columns stack and the copy takes the
   full width, so the art would sit underneath it. It stands down rather than
   being fought with a heavier veil. */
@media (max-width: 900px) {
  .section--difference { background-image: none; }
}

/* --------------------------------------------------------------------------
   25. TOPBAR ICONS
   --------------------------------------------------------------------------
   Leading orange marks on the note, phone and email, matching the diamond the
   tagline already carries. Drawn inline on the same 24 viewBox as the stat
   band and credential strip, so there is still no icon font to load.

   Deliberately inline rather than flex: site.css hides the note below 900px
   with `.topbar-left .hide-sm`, and a media query adds no specificity, so a
   `display` declaration here would out-rank that hide and bring the note back
   on small screens. Leaving the parents alone avoids the whole collision.
   -------------------------------------------------------------------------- */
.topbar .tb-ic {
  display: inline-flex;
  width: 13px;
  height: 13px;
  margin-right: 7px;
  vertical-align: -2px;

  /* Set directly rather than inherited, which is also what keeps the marks
     orange while the links lighten to #fff on hover. */
  color: var(--flag);
}

.topbar .tb-ic svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --------------------------------------------------------------------------
   26. CLOSING SECTION
   --------------------------------------------------------------------------
   The buttons sit at the foot of their column rather than centred on it. The
   inline `align-items:center` they used to carry has been removed from the
   template so this is overridable at all — an inline style cannot be beaten
   from a stylesheet without `!important`.

   Bottom-aligning settles the call to action onto the same baseline as the
   copy it answers, and frees the top of the right column for the section art.
   -------------------------------------------------------------------------- */
.section--closing .split { align-items: end; }

/*
 * The closing art. An isometric index plate drawn as a shop print, anchored
 * into the space the buttons vacated and dissolving down and to the left.
 *
 * Replaces the shop-floor photograph, which was built against the old navy.
 * Line art rather than a photograph is what let the ground change from
 * #21366D to #12161D without a reshoot: it carries no colour of its own to
 * clash, and the orange dimension callouts pick up the flag.
 *
 * No veil gradient here. The alpha is capped at .55 in the file itself, so the
 * drawing composites at partial strength directly over the section colour
 * rather than needing a scrim laid on top. Derived from the per-pixel max
 * channel, not luma, so the orange callouts survive at the same weight as the
 * white linework — luma would have carried them at about half.
 *
 * Height-driven so the frame keeps its proportions as the section widens. At
 * the 756px design height that puts the image at 1012px wide spanning
 * x=396–1408; the artwork's empty left 28% is what clears the copy column,
 * which ends at x=676.
 */
.section--closing {
  background-image: url("../img/closing-flange.webp");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: auto 100%;
}

/* The phone button ships transparent, so the drawing runs straight under its
   label: measured 2.97:1 against white at worst, with 5.5% of the button area
   below 4.5:1 and 2.1% below 3.0:1. The white primary button beside it is
   opaque and unaffected.
 *
 * Same solid ground the hero's outline button already uses in section 29, so
 * the treatment is consistent rather than a local patch. `background-color`
 * deliberately — the shorthand would reset the border-image and is what
 * silently deleted this section's artwork once already. */
.section--closing .btn-outline-dark { background-color: #0F1114; }

/* Stacked, each column is its own grid row and end-alignment does nothing but
   pull the buttons tight against the tickrule. Restore the natural spacing,
   and stand the art down as the copy takes the full width. */
@media (max-width: 900px) {
  .section--closing .split { align-items: start; }
  .section--closing { background-image: none; }
}

/* --------------------------------------------------------------------------
   27. HEADER AND FOOTER EDGES
   --------------------------------------------------------------------------
   Two separate faults, both only visible once the viewport is narrow enough
   that `.wrap` stops being centred inside its max-width.
   -------------------------------------------------------------------------- */

/*
 * The footer's top half lost its horizontal gutter.
 *
 * The element is `<div class="wrap footer-top">`, and site.css line 455 sets
 * `.footer-top { padding: 64px 0 48px; }` — a shorthand, so it overwrites the
 * `padding: 0 var(--gutter)` that `.wrap` had already applied. Same specificity,
 * later in the file, so the shorthand wins and left/right become 0.
 *
 * Measured: `.wrap.footer-top` computes padding-left 0px while the footer's
 * other `.wrap` computes 24px. It is wrong at every width — invisible on
 * desktop only because the 1280px max-width leaves room either side.
 *
 * Restoring the two sides rather than rewriting the shorthand keeps the
 * vertical rhythm site.css set.
 */
.site-footer .footer-top {
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/*
 * Logo left, actions right once the menu collapses.
 *
 * `.nav` is a flex row with no `justify-content`. Below 1120px site.css makes
 * `.nav-main` `position: fixed` for the drawer, which takes it out of flow and
 * with it the `margin-left: auto` that had been pushing everything apart. The
 * brand and `.nav-cta` were left sitting together against the left edge with
 * the whole right side empty.
 *
 * `margin-left: auto` on the actions rather than `justify-content: space-between`
 * on the parent: it pins them right whatever else is in the row, where
 * space-between would redistribute if another in-flow child were ever added.
 */
@media (max-width: 1120px) {
  .site-header .nav-cta { margin-left: auto; }
}

/* --------------------------------------------------------------------------
   28. HEADER — CHARCOAL, NOT NAVY
   --------------------------------------------------------------------------
   The client's reference (mfg.jpg) replaces the navy header with near-black and
   makes the call to action orange. Values sampled from that image rather than
   guessed: topbar rgb(4,5,7), header rgb(10,13,18)-(13,16,21), button
   rgb(239,99,22) — which is `--flag` within JPEG noise, so the existing token is
   reused rather than a second orange introduced.

   The greys keep a slight blue cast so the header still belongs to the palette;
   a pure neutral read as a different site sitting on top of one.

   Scoped to the header on purpose. `.btn-primary` is used across the whole site,
   and turning it orange globally would repaint every section — which is a
   separate decision from this one.
   -------------------------------------------------------------------------- */
.topbar { background: #040507; }

.site-header { background: #0B0E13; }

/* The navy drop shadow read as a blue halo under a charcoal bar. */
.site-header.is-stuck { box-shadow: 0 8px 28px -12px rgba(0, 0, 0, .7); }

/*
 * The drawer is the header's own navigation, so it follows the bar rather than
 * staying navy — otherwise opening the menu on mobile reveals the old colour.
 */
@media (max-width: 1120px) {
  .site-header .nav-main { background: #0B0E13; }
}

/* The header call to action. `--flag-ink` is the palette's existing darker
   orange, so the hover state needs no new value either. */
.site-header .btn-primary {
  background: var(--flag);
  color: #fff;
}

.site-header .btn-primary:hover {
  background: var(--flag-ink);
  color: #fff;
}

/* --------------------------------------------------------------------------
   29. HERO — CHARCOAL, NOT NAVY
   --------------------------------------------------------------------------
   Same move as section 28, applied to the hero from the same reference image.
   Sampled from it rather than matched by eye: overlay rgb(4,5,7)-rgb(7,8,10),
   primary button rgb(227,98,27) — `--flag` within JPEG noise — and the "see our
   work" button a solid rgb(15,17,20) fill rather than the transparent outline it
   has now.

   The form panel needed nothing beyond its submit button: zooming the reference
   shows white inputs with light grey borders, not the grey fills a flat sample
   suggested. Only the blue leaves.
   -------------------------------------------------------------------------- */

/* Shows through if the poster image ever fails to load. */
.hero { background: #05070A; }

/*
 * The scrim over the video. The alpha ramp is left untouched — .86/.74/.62 was
 * measured against the graded clip, where the brightest pixel under the headline
 * is 216 — and only the colour changes. Near-black is darker than the navy it
 * replaces, so every contrast figure in section 18 improves rather than degrades.
 *
 * The blue radial glow goes entirely. It was the single most saturated blue on
 * the page and re-tinting it would have kept exactly what the client asked to
 * remove; a faint warm lift stands in for it so the top right is not flat.
 */
.hero.has-video::before {
  background:
    radial-gradient(120% 90% at 82% 8%, rgba(67, 103, 198, .10) 0%, transparent 58%),
    linear-gradient(90deg,
      rgba(6, 8, 11, .82) 0%,
      rgba(6, 8, 11, .78) 30%,
      rgba(6, 8, 11, .40) 56%,
      rgba(6, 8, 11, .24) 100%);
}

/* Non-video fallback: same treatment, no ramp needed. */
.hero:not(.has-video)::before {
  background:
    radial-gradient(120% 90% at 82% 8%, rgba(67, 103, 198, .08) 0%, transparent 58%),
    #05070A;
}

.hero .btn-primary { background: var(--flag); color: #fff; }
.hero .btn-primary:hover { background: var(--flag-ink); color: #fff; }

/*
 * A filled dark button, not a translucent one. Over video the transparent
 * version let the footage read straight through and the label sat on whatever
 * happened to be moving behind it.
 */
.hero .btn-outline-dark {
  background: #0F1114;
  border-color: rgba(255, 255, 255, .22);
  color: #fff;
}

.hero .btn-outline-dark:hover {
  background: #171A1F;
  border-color: rgba(255, 255, 255, .38);
  color: #fff;
}

/* The panel stays white; only its action turns over. */
.hero .form-panel .wpcf7-submit { background: var(--flag); color: #fff; }
.hero .form-panel .wpcf7-submit:hover { background: var(--flag-ink); color: #fff; }

/* --------------------------------------------------------------------------
   30. INNER PAGE HEADERS — CHARCOAL, NOT NAVY
   --------------------------------------------------------------------------
   Sections 28 and 29 applied to `.phead`, which every page except the home page
   renders. Same substitution: the navy becomes the near-black sampled from the
   client's reference, and the blue radial glow goes rather than being re-tinted.

   The alpha ramp is unchanged at .96/.88/.70. It was set because the spec strip
   spans the full width and the banners are composed with their subject on the
   right; near-black at those same values is darker than the navy it replaces, so
   legibility only improves.
   -------------------------------------------------------------------------- */
.phead { background: #05070A; }

.phead::before {
  background-image:
    radial-gradient(110% 130% at 88% -10%, rgba(67, 103, 198, .09) 0%, transparent 56%),
    linear-gradient(90deg,
      rgba(6, 8, 11, .96) 0%,
      rgba(6, 8, 11, .88) 55%,
      rgba(6, 8, 11, .70) 100%),
    var(--phead-img, none);
  background-color: #05070A;
}

/* Narrow screens crop hard to the centre, so the floor stays lifted — same
   reasoning as the rule this replaces, only the colour changes. */
@media (max-width: 720px) {
  .phead::before {
    background-image:
      linear-gradient(90deg, rgba(6, 8, 11, .96) 0%, rgba(6, 8, 11, .90) 100%),
      var(--phead-img, none);
  }
}

/* --------------------------------------------------------------------------
   31. THE DEEP BLUE, EVERYWHERE ELSE
   --------------------------------------------------------------------------
   Sections 28-30 converted the header, hero and page banners one at a time.
   The client then asked for the deep blue to go generally, which is too many
   surfaces to keep listing individually: `--brand-950` alone is referenced 24
   times and carries the intro band, the stat band, the service cards, the
   footer and more.

   So the dark end of the ramp is redefined at the token instead, and only the
   places that hardcode a navy literal need naming. Those exist because a
   gradient stop cannot take a bare custom property and stay readable — there
   are 11 in site.css, which is frozen, and they are the reason a token change
   on its own would leave blue glows sitting over charcoal.

   Lightness steps are kept close to the originals so every existing contrast
   figure holds; only the hue leaves. A faint blue cast remains deliberately —
   a pure neutral read as a different site rather than a quieter version of
   this one.
   -------------------------------------------------------------------------- */
:root {
  --brand-950: #080A0E;   /* was #031030 */
  --brand-900: #0E1218;   /* was #0A1839 */
  --brand-800: #171C24;   /* was #12224C */
}

/* --- the blue glows -------------------------------------------------------
   rgba(66,96,174) is `--brand-400` written out. It is the most saturated blue
   on the site and re-tinting it would keep exactly what the client asked to
   remove, so each becomes a faint warm lift at a similar weight. */

.hero:not(.has-video)::before,
.info-card::before,
.inner-page-cta-banner.ctabg1::before {
  background-image: radial-gradient(90% 120% at 100% 0%, rgba(67, 103, 198, .10) 0%, transparent 60%);
}

.inner-page-cta-banner.ctabg1 { background: var(--brand-950); }

.inner-page-cta-banner.ctabg2 { background: #12161D; }
.inner-page-cta-banner.ctabg2::before {
  background-image: radial-gradient(80% 140% at 0% 100%, rgba(8, 10, 14, .6) 0%, transparent 60%);
}

/* --- the closing band -----------------------------------------------------
   `.section--brand` is the one mid-blue surface rather than a deep one. It
   stays a step lighter than the deep sections so the page still has a band
   there, rather than collapsing into one flat dark. */
/* `background-color`, not the `background` shorthand: the shorthand resets
   `background-image` and silently removed the closing section's artwork, which
   is set 250 lines above. Same trap as `.footer-top` in section 27 — worth
   stating twice, since it fails without any error. */
.section--brand,
.section--closing { background-color: #12161D; }

/* --- shadows and scrims ---------------------------------------------------
   Navy shadows under charcoal read as a blue halo. */
.site-header .nav-main { box-shadow: -20px 0 60px rgba(0, 0, 0, .55); }
.scrim { background: rgba(4, 5, 7, .62); }
.framed .cap { background: linear-gradient(to top, rgba(8, 10, 14, .9), transparent); }

/* --- primary buttons ------------------------------------------------------
   The reference shows these orange throughout, not just in the header and
   hero where sections 28 and 29 already changed them. `--brand-600` itself is
   left alone: it is also the body link colour, which the client did not ask
   about and which is not the deep blue in question. */
.btn-primary { background: var(--flag); color: #fff; }
.btn-primary:hover { background: var(--flag-ink); color: #fff; }

.wpcf7-submit,
.sb-quote .wpcf7-submit { background: var(--flag); color: #fff; }
.wpcf7-submit:hover,
.sb-quote .wpcf7-submit:hover { background: var(--flag-ink); color: #fff; }

/* --- mid-blue surfaces ----------------------------------------------------
   `--brand-600` is deliberately not redefined: it is also `a { color }` on
   white, and a charcoal link is not a link. Only the places that paint a
   surface with it are converted, so body copy keeps a visible link colour.

   The CTA card takes the lighter charcoal rather than orange — at 268px tall a
   solid orange block shouts louder than the seven cards it sits beside, and its
   own affordance is already orange. */
.svc--cta { background: #12161D; }
.svc--cta:hover { background: #171C25; }

/* Small UI accents, for consistency on the inner pages. */
.pager .page-numbers.current,
.filters button.is-active {
  background: #12161D;
  border-color: #12161D;
  color: #fff;
}

.inner-page-button { background: var(--flag); }
.inner-page-button:hover { background: var(--flag-ink); }

/* --------------------------------------------------------------------------
   32. FOOTER TEXTURE, AND THE LAST OF THE BLUE
   --------------------------------------------------------------------------
   Section 31 converted surfaces. It did not touch the text tokens, and
   `--brand-300` (#7A90C9) was left painting the footer tagline, the contact
   labels, the hero proof labels and the page-header spec labels — measured at
   B−R +79, the most saturated blue left anywhere on the site.

   Desaturated rather than replaced: the hue stays in the same family as the
   surfaces (217 against their 216-220) and only the saturation drops, 42% to
   10%. Contrast on the footer improves as a side effect, 6.28:1 to 7.52:1.
   -------------------------------------------------------------------------- */
:root {
  --brand-300: #99A0AB;   /* was #7A90C9 */
  --brand-200: #CFD3DA;   /* was #C3CEE6 */
}

/* --------------------------------------------------------------------------
   The footer was a flat slab. It now carries the same measurement grid the
   hero and page headers use — 88px, white at 4-5% — so the texture is one the
   design already speaks rather than a new idea introduced at the bottom of the
   page.

   Painted as background layers on the element itself rather than a pseudo:
   `.site-footer` has no stacking context of its own, and a z-index:-1 pseudo
   would sit behind the page background instead of behind the footer's content.

   Two additions on top of the grid: a warm lift in the top right, at the same
   weight as the one replacing the old blue glow elsewhere, and a cool one
   bottom left so the slab has some depth across its width. Both are far below
   the threshold where they would affect the text sitting on them.
   -------------------------------------------------------------------------- */
.site-footer {
  background-image:
    /* the measurement grid, fading out toward the bottom edge */
    repeating-linear-gradient(to right, rgba(255, 255, 255, .045) 0 1px, transparent 1px 88px),
    repeating-linear-gradient(to bottom, rgba(255, 255, 255, .030) 0 1px, transparent 1px 88px),
    radial-gradient(80% 120% at 88% 0%, rgba(67, 103, 198, .07) 0%, transparent 62%),
    radial-gradient(70% 110% at 4% 100%, rgba(153, 160, 171, .05) 0%, transparent 58%);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* A hairline in the flag colour marks the join, the same way the eyebrow rules
   do elsewhere. Keeps the footer from reading as one more dark band. */
.site-footer { border-top: 2px solid var(--flag); }

/* The grid should not run under the bottom bar — that row is already separated
   by its own rule and the lines fight it. */
.site-footer .footer-bottom { position: relative; }

/* `--brand-400` drives the small index numerals on cards and sidebar lists, and
   also the focus ring on form fields. Desaturated in the same direction as the
   rest of the ramp, but only part-way: a focus ring that reads as grey stops
   announcing itself, and that is an accessibility affordance rather than
   decoration. Hue is unchanged, saturation drops 45% to 19%. */
:root { --brand-400: #5A6785; }

/* --------------------------------------------------------------------------
   33. SIDEBAR QUOTE PANEL
   --------------------------------------------------------------------------
   The last large blue block. `.sb-quote` kept `--brand-600` through the token
   sweep because section 31 deliberately left that token alone — it is also the
   body link colour — and this panel was one of the surfaces still painted with
   it. It appears on every inner page with a sidebar, directly below a charcoal
   header, which is where it looked most out of place.

   Charcoal a step lighter than the deep sections, the same value the closing
   band and the services CTA card use, so the three read as one family.
   -------------------------------------------------------------------------- */
.sb-quote { background-color: #12161D; }

/* The panel's own corner lift, warmed to match the rest of the page. Left at a
   similar weight — it is what stops the panel reading as a flat rectangle. */
.sb-quote::before {
  background: radial-gradient(90% 120% at 100% 0%, rgba(67, 103, 198, .10) 0%, transparent 58%);
}

/* Fields keep their translucent-white treatment, which works on charcoal as
   well as it did on navy, but the border needs a little more weight to stay
   visible against the darker ground. */
.sb-quote .wpcf7 input[type="text"],
.sb-quote .wpcf7 input[type="email"],
.sb-quote .wpcf7 input[type="tel"],
.sb-quote .wpcf7 input[type="url"],
.sb-quote .wpcf7 select,
.sb-quote .wpcf7 textarea {
  background: rgba(255, 255, 255, .05);
  border-color: rgba(255, 255, 255, .26);
}

.sb-quote .wpcf7 input:focus,
.sb-quote .wpcf7 select:focus,
.sb-quote .wpcf7 textarea:focus {
  border-color: var(--flag);
  box-shadow: 0 0 0 3px rgba(67, 103, 198, .18);
}

/* The submit follows every other call to action on the site rather than staying
   the odd white one out. */
.sb-quote .wpcf7 input[type="submit"] { background: var(--flag); color: #fff; }
.sb-quote .wpcf7 input[type="submit"]:hover { background: var(--flag-ink); color: #fff; }

/* --------------------------------------------------------------------------
   34. THE ACCENT — BLUE, NOT ORANGE

   The client asked for the deep blue we removed to take the orange's place.
   Taken literally that fails: #21366D measures 1.57:1 against the #12161D
   ground it would now sit on, so every button, rule and icon would sink into
   the page. That blue was a *surface* — it was the panel and section ground —
   and its whole job was to sit still. An accent has the opposite job.

   So the hue is kept exactly (223 degrees) and lifted to 52% lightness. What
   that buys, against the orange it replaces:

                       vs ground    white text
     orange #F26522     5.75:1       3.15:1   <- failed AA in five places
     blue   #4367C6     3.45:1       5.26:1   <- passes

   The accent separates a little less from the ground than the orange did, but
   clears the 3:1 threshold for interface components, and white labels on it
   now pass AA. The long-standing 3.15:1 button failure is fixed by this change
   rather than in spite of it.

   The other three tokens are derived from the same hue rather than picked:
   --flag-ink is the client's actual #21366D, which is excellent for small text
   on light grounds (11.58:1) and is the one place the literal colour belongs.
   --flag-lite is lifted far enough to carry small text on dark (5.59:1), the
   job --flag itself is too dark for. --flag-soft is #FDE9DF's own lightness
   and saturation rebuilt at the blue hue, so it stays the same kind of tint.

   Redefining here rather than editing site.css flips all 44 var(--flag) uses
   across both stylesheets at once, and keeps site.css diffable.
   -------------------------------------------------------------------------- */
:root {
  --flag: #4367C6;
  --flag-soft: #DFE7FD;
  --flag-ink: #21366D;   /* blue for small text on light bg */
  --flag-lite: #728DD4;  /* blue for small text on brand bg */
}

/* site.css writes the orange literally in .qcard, where a token would not
   reach it. Same values, same alphas, at the new accent. */
.qcard.is-you {
  border-color: rgba(67, 103, 198, .6);
  background: linear-gradient(90deg, rgba(67, 103, 198, .16), rgba(67, 103, 198, .03));
}

.qcard .badge { border-color: rgba(67, 103, 198, .45); }

/* --------------------------------------------------------------------------
   35. THE DEEP NAVY, WHERE IT WORKS

   The client picked #243C77. Against the #12161D ground it measures 1.72:1 —
   the same problem as #21366D before it — so it cannot be the whole accent:
   25 of the 44 var(--flag) uses are thin lines, dots, carets and small text,
   and every one of them would disappear.

   But 18 uses are fills, and white on #243C77 is 10.56:1. So the navy takes
   the seven real button fills, where its darkness is an asset, and the lighter
   #4367C6 keeps the thin work legible. Same hue (223 degrees) throughout, so
   they read as one family rather than two colours.

   The button SHAPE is what a 1.72:1 fill loses, not the label. `.btn` already
   carries `border: 1px solid transparent`, so colouring that border costs no
   layout shift and restores the edge at 5.59:1 against the ground.

   Hover had to move too: it previously went to --flag-ink, which against this
   fill is almost the same colour and would read as no feedback at all. It now
   lifts to the lighter blue instead of deepening.
   -------------------------------------------------------------------------- */
:root {
  --flag-deep: #243C77;
  --flag-ink: #243C77;   /* the client's own navy; 10.56:1 on light grounds */
}

.btn-primary,
.site-header .btn-primary,
.hero .btn-primary,
.hero .form-panel .wpcf7-submit,
.sb-quote .wpcf7-submit,
.sb-quote .wpcf7 input[type="submit"],
.inner-page-button {
  background: var(--flag-deep);
  border-color: var(--flag-lite);
  color: #fff;
}

.btn-primary:hover,
.site-header .btn-primary:hover,
.hero .btn-primary:hover,
.hero .form-panel .wpcf7-submit:hover,
.sb-quote .wpcf7-submit:hover,
.sb-quote .wpcf7 input[type="submit"]:hover,
.inner-page-button:hover {
  background: var(--flag);
  border-color: var(--flag-lite);
  color: #fff;
}
