@charset "UTF-8";
/* ==========================================================================
   NOVA ADUs — Homepage design system
   "Site lines" — architectural photography carries the page; type, scale and
   negative space do the rest. Warm paper ground, near-black ink, burgundy used
   once per screen at most.

   Self-contained on purpose: the homepage links this file and nothing else, so
   nothing here can reach the other templates (which still run styles.css).

   01  Fonts
   02  Tokens
   03  Reset & base
   04  Utilities
   05  Type components
   06  Buttons, links, controls
   07  Motion primitives
   08  Header & navigation
   09  Hero
   10  Intro / Why
   11  Selected work
   12  Image break
   13  Services
   14  Craft
   15  Testimonials
   16  Areas we serve
   17  Final CTA
   18  Footer
   19  Injected controls (back-to-top, studio credit)
   20  Print
   ========================================================================== */

/* ==========================================================================
   01  FONTS — self-hosted variable woff2 (shared with the rest of the site)
   ========================================================================== */

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/manrope-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/manrope-latin-ext-var.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/instrument-sans-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/instrument-sans-latin-ext-var.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   02  TOKENS
   ========================================================================== */

:root {
  /* --- Ground: warm off-white through to near-black --------------------- */
  --paper: #f7faf7; /* page ground — the faintest green cast, not grey */
  --paper-raised: #fdfefd; /* cards and panels that sit on the ground */
  --sand: #e7f0e8; /* PALE GREEN — alternate band; 1.11:1 against paper, as the old sand was */
  --ink: #1e1e1e; /* CHARCOAL — near-black surface + primary text */
  --ink-soft: #36423a; /* FOREST — second dark surface, for layering */

  /* --- Text ------------------------------------------------------------- */
  --text: #1e1e1e; /* CHARCOAL, 14.9:1 on paper */
  --text-soft: #444c45; /* RIVIAN GREEN darkened — 7.9:1 paper, 7.0:1 sand */
  --text-mute: #5e695f; /* RIVIAN GREEN darkened — 5.1:1 paper, 4.5:1 sand */
  --text-invert: #ffffff;
  --text-invert-soft: #c4c6bf; /* CLAY — 9.7:1 on charcoal, 6.1:1 on forest */
  --text-invert-mute: #8d948d; /* STONE — 5.4:1 on charcoal */

  /* --- Lines ------------------------------------------------------------ */
  --line: rgb(54 66 58 / 0.16); /* FOREST at low alpha */
  --line-strong: rgb(54 66 58 / 0.3);
  --line-invert: rgb(196 198 191 / 0.24); /* CLAY */
  --line-invert-strong: rgb(196 198 191 / 0.45);

  /* --- Brand accent — sage green --------------------------------------- */
  /* --accent is the mark's own gold: it carries 6.4:1 against --ink, so it is
     the value used on dark surfaces and as a button fill (with ink type on it).
     On paper it only reaches 2.6:1, so text and rules on light grounds use
     --accent-ink, a darkened gold that clears AA at 4.8:1. */
  --accent: #8baf8f; /* SAGE — fills; pair with --ink text at 6.9:1 */
  --accent-hover: #688e6c; /* SAGE darkened — button hover (ink on it 4.5:1) and small marks on light grounds (3.4:1 paper, 3.2:1 pale green) */
  --accent-ink: #4a6b4f; /* SAGE darkened — 5.7:1 paper, 5.1:1 pale green */
  --focus: #36423a; /* FOREST — 9.4:1 on paper */
  --focus-invert: #ffffff;

  /* --- Type ------------------------------------------------------------- */
  --font-display: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --font-body: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;

  /* Fluid scale, 360px → 1600px. Display steps are deliberately far apart:
     the hierarchy is meant to read from across the room. */
  --t-label: clamp(0.6875rem, 0.66rem + 0.12vw, 0.75rem);
  --t-small: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
  --t-body: clamp(1rem, 0.97rem + 0.15vw, 1.125rem);
  --t-body-l: clamp(1.0625rem, 1rem + 0.28vw, 1.3125rem);
  --t-lede: clamp(1.1875rem, 1.06rem + 0.55vw, 1.625rem);
  --t-d1: clamp(1.5rem, 1.31rem + 0.83vw, 2.25rem); /* card titles */
  --t-d2: clamp(1.875rem, 1.5rem + 1.65vw, 3.375rem); /* sub-headings */
  --t-d3: clamp(2.25rem, 1.6rem + 2.85vw, 4.75rem); /* section statements */
  --t-d4: clamp(2.1875rem, 1.35rem + 3.6vw, 5.5rem); /* hero + closing CTA */
  /* Sized against the real testimonials, which run to ~95 words: big enough to
     read as a statement, small enough not to become a wall. */
  --t-quote: clamp(1.25rem, 1rem + 1.15vw, 1.9375rem);
  --t-index: clamp(3.5rem, 2.2rem + 5.6vw, 8rem); /* oversized numerals */

  --w-display: 700;
  --w-heading: 600;

  /* --- Space ------------------------------------------------------------ */
  --s-1: clamp(0.25rem, 0.24rem + 0.06vw, 0.3125rem);
  --s-2: clamp(0.5rem, 0.47rem + 0.12vw, 0.625rem);
  --s-3: clamp(0.75rem, 0.7rem + 0.25vw, 1rem);
  --s-4: clamp(1rem, 0.94rem + 0.31vw, 1.375rem);
  --s-5: clamp(1.5rem, 1.38rem + 0.55vw, 2.125rem);
  --s-6: clamp(2rem, 1.72rem + 1.24vw, 3.5rem);
  --s-7: clamp(3rem, 2.5rem + 2.2vw, 5.5rem);
  --s-8: clamp(4rem, 3.1rem + 4vw, 8.5rem);
  --s-9: clamp(5.5rem, 3.9rem + 7.1vw, 13rem); /* section rhythm */
  --s-10: clamp(7rem, 4.6rem + 10.6vw, 18rem); /* the big pauses */

  /* --- Containers ------------------------------------------------------- */
  --w-wide: 110rem; /* 1760px — visual container */
  --w-main: 87.5rem; /* 1400px — editorial container */
  --w-text: 46rem; /* 736px — reading width */
  --w-text-tight: 38rem;
  --gutter: clamp(1.25rem, 0.55rem + 3vw, 4.5rem);

  /* --- Radius — small system, applied to media and controls only -------- */
  --r-s: 4px;
  --r-m: 10px;
  --r-l: clamp(10px, 0.5vw + 6px, 18px);
  --r-pill: 999px;

  /* --- Motion ----------------------------------------------------------- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-soft: cubic-bezier(0.33, 0, 0.15, 1);
  --d-fast: 240ms;
  --d-base: 460ms;
  --d-slow: 900ms;

  /* --- Header ----------------------------------------------------------- */
  --header-h: clamp(4.25rem, 3.9rem + 1.5vw, 5.25rem);
}

/* Any dark surface remaps the semantic tokens rather than restating colours. */
.on-dark {
  --text: var(--text-invert);
  --text-soft: var(--text-invert-soft);
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --accent-ink: var(--accent);
  --focus: var(--focus-invert);
  color: var(--text);
  background-color: var(--ink);
}

/* ==========================================================================
   03  RESET & BASE
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background-color: var(--paper);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

body.is-locked {
  overflow: hidden;
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
ul,
ol,
dl {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
}

/* A <picture> is only a wrapper - it has no height of its own, so `height: 100%`
   on the <img> inside resolves against the picture rather than the frame, and a
   photograph shorter than its frame leaves a bare strip of ground below it.
   Giving every picture the height too fixes that once instead of per component
   (it had already been patched onto .phero__media, .break__media, .close__media
   and .hero__stack one at a time). A percentage height against an auto-height
   parent resolves to auto, so this is inert in flow content and only takes
   effect where a frame has a definite height - exactly where the strip showed. */
picture {
  height: 100%;
}

img {
  height: auto;
  border: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

address {
  font-style: normal;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--r-s);
}
:focus:not(:focus-visible) {
  outline: none;
}

::selection {
  background: var(--accent);
  color: var(--ink);
}

/* ==========================================================================
   04  UTILITIES
   ========================================================================== */

.shell {
  width: 100%;
  max-width: var(--w-main);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.shell--wide {
  max-width: var(--w-wide);
}

.shell--text {
  max-width: calc(var(--w-text) + var(--gutter) * 2);
}

.section {
  padding-block: var(--s-9);
}

.section--tight {
  padding-block: var(--s-8);
}

.section--flush-top {
  padding-block-start: 0;
}

/* `.u-visually-hidden` is the name used by the markup the sandbox plugins
   inject into the finished HTML (the back-to-top control), so it has to be
   honoured here as well as the page's own shorter `.u-vh`. */
.u-vh,
.u-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 200;
  transform: translateY(-120%);
  background: var(--ink);
  color: #fff;
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 var(--r-m) 0;
  font-size: var(--t-small);
  font-weight: 600;
}
.skip-link:focus-visible {
  transform: translateY(0);
}

.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Full-bleed escape from any container. */
.bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}

/* ==========================================================================
   05  TYPE COMPONENTS
   ========================================================================== */

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0;
}

.display {
  font-family: var(--font-display);
  font-weight: var(--w-heading);
  line-height: 1.02;
  letter-spacing: -0.028em;
  text-wrap: balance;
  margin: 0;
}

.display--1 {
  font-size: var(--t-d1);
  line-height: 1.14;
  letter-spacing: -0.02em;
}
.display--2 {
  font-size: var(--t-d2);
  line-height: 1.08;
}
.display--3 {
  font-size: var(--t-d3);
  line-height: 1.04;
}
.display--4 {
  font-size: var(--t-d4);
  font-weight: var(--w-display);
  line-height: 0.98;
  letter-spacing: -0.035em;
}

.lede {
  font-size: var(--t-lede);
  line-height: 1.45;
  color: var(--text-soft);
  letter-spacing: -0.012em;
  text-wrap: pretty;
}

.body-l {
  font-size: var(--t-body-l);
  line-height: 1.6;
  color: var(--text-soft);
  text-wrap: pretty;
}

.measure {
  max-width: var(--w-text);
}
.measure--tight {
  max-width: var(--w-text-tight);
}

/* ==========================================================================
   06  BUTTONS, LINKS, CONTROLS
   ========================================================================== */

.btn {
  /* FOREST, not charcoal: the solid button is the brand green. Hover deepens
     to CHARCOAL, so both states come out of the client's palette. */
  --btn-bg: var(--ink-soft);
  --btn-fg: #fff;
  --btn-bd: transparent;
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.9375rem 1.75rem;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  font-family: var(--font-body);
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.2;
  white-space: nowrap;
  transition: background-color var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease), border-color var(--d-fast) var(--ease),
    transform var(--d-fast) var(--ease);
}

.btn__arrow {
  width: 1rem;
  height: 0.75rem;
  flex: none;
}

.btn--solid:hover {
  --btn-bg: var(--ink);
}

.btn--accent {
  --btn-bg: var(--accent);
  --btn-fg: var(--ink);
}
.btn--accent:hover {
  --btn-bg: var(--accent-hover);
}

.btn--outline {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-bd: var(--line-strong);
}
.btn--outline:hover {
  --btn-bd: currentColor;
}

.btn--light {
  --btn-bg: #fff;
  --btn-fg: var(--ink);
}
.btn--light:hover {
  --btn-bg: rgb(255 255 255 / 0.86);
}

.btn--glass {
  --btn-bg: rgb(255 255 255 / 0.08);
  --btn-fg: #fff;
  --btn-bd: rgb(255 255 255 / 0.42);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.btn--glass:hover {
  --btn-bg: rgb(255 255 255 / 0.16);
  --btn-bd: rgb(255 255 255 / 0.7);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

/* Text link with an arrow — the page's quietest call to action. */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text);
  padding-block: 0.25rem;
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--d-fast) var(--ease);
}
.arrow-link svg {
  width: 1rem;
  height: 0.75rem;
  flex: none;
}
.arrow-link:hover {
  border-color: currentColor;
}

/* Circular previous / next control — used by every carousel on the page. */
.ctrl {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  color: var(--text);
  transition: background-color var(--d-fast) var(--ease),
    border-color var(--d-fast) var(--ease), color var(--d-fast) var(--ease),
    opacity var(--d-fast) var(--ease);
}
.ctrl svg {
  width: 1rem;
  height: 0.75rem;
}
.ctrl:hover:not(:disabled) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}
.on-dark .ctrl:hover:not(:disabled) {
  color: var(--ink);
}
.ctrl:disabled {
  opacity: 0.35;
  cursor: default;
}

.counter {
  font-family: var(--font-display);
  font-size: var(--t-small);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--text-mute);
}
.counter b {
  color: var(--text);
  font-weight: 600;
}

/* A hairline that reads as a drawing rule, not a border. */
.rule {
  height: 1px;
  background: var(--line);
  border: 0;
  margin: 0;
}

/* ==========================================================================
   07  MOTION PRIMITIVES
   Reveals are CSS animations driven by a class the observer adds. If the
   script never runs, `.has-anim` is dropped by the inline failsafe and every
   element is simply visible — the page is never blank because of motion.
   ========================================================================== */

@keyframes rise {
  from {
    opacity: 0;
    transform: translate3d(0, 1.75rem, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes unmask {
  from {
    clip-path: inset(0 0 100% 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

@keyframes settle {
  from {
    transform: scale(1.08);
  }
  to {
    transform: scale(1);
  }
}

@keyframes line-in {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

.has-anim [data-anim] {
  opacity: 0;
}

.has-anim [data-anim].is-in {
  animation: rise var(--d-slow) var(--ease) both;
  animation-delay: var(--anim-delay, 0ms);
}

.has-anim [data-anim="fade"].is-in {
  animation-name: fade;
}

.has-anim [data-anim="mask"] {
  opacity: 1;
  clip-path: inset(0 0 100% 0);
}
.has-anim [data-anim="mask"].is-in {
  animation: unmask 1.1s var(--ease-soft) both;
  animation-delay: var(--anim-delay, 0ms);
  clip-path: inset(0 0 0 0);
}

.has-anim [data-anim="rule"] {
  opacity: 1;
  transform: scaleX(0);
  transform-origin: left center;
}
.has-anim [data-anim="rule"].is-in {
  animation: line-in 1s var(--ease-soft) both;
  animation-delay: var(--anim-delay, 0ms);
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .has-anim [data-anim],
  .has-anim [data-anim].is-in {
    opacity: 1;
    clip-path: none;
    transform: none;
    animation: none !important;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   08  HEADER & NAVIGATION
   Transparent over the hero, then a translucent inset bar once scrolled.
   ========================================================================== */

.header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  padding-block: var(--s-3);
  transition: background-color var(--d-base) var(--ease),
    box-shadow var(--d-base) var(--ease), padding var(--d-base) var(--ease);
}

.header::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgb(18 18 18 / 0.55),
    rgb(18 18 18 / 0)
  );
  opacity: 1;
  transition: opacity var(--d-base) var(--ease);
  pointer-events: none;
}

.header.is-solid::before {
  opacity: 0;
}

.header.is-solid {
  background: rgb(244 242 238 / 0.82);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  box-shadow: 0 1px 0 var(--line);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .header.is-solid {
    background: rgb(244 242 238 / 0.98);
  }
}

/* Brand and actions hold the edges; the nav is optically centred between
   them by giving both flanks equal grid tracks. */
.header__inner {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--s-4);
  min-height: calc(var(--header-h) - var(--s-3) * 2);
}

/* Header colour is inverted until the bar turns solid. */
.header {
  color: #fff;
}
.header.is-solid {
  color: var(--text);
}

.brand {
  display: inline-flex;
  align-items: center;
  flex: none;
}

.brand__mark {
  width: auto;
  /* Sized for a wide wordmark (3.9:1), not the old near-square mark. */
  height: clamp(2.065rem, 1.77rem + 1.3vw, 2.8025rem);
}

/* The NOVA wordmark is two-tone (near-black letters, gold tagline), so it is
   never filtered — the white artwork is used on dark grounds instead. */

.nav {
  display: none;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(0.25rem, -0.5rem + 1.6vw, 1.25rem);
}

.nav__link {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding: 0.625rem 0.75rem;
  border-radius: var(--r-pill);
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0.005em;
  color: currentColor;
  white-space: nowrap;
  transition: opacity var(--d-fast) var(--ease),
    background-color var(--d-fast) var(--ease);
  opacity: 0.85;
}
.nav__link:hover,
.nav__item[data-open="true"] .nav__link {
  opacity: 1;
}

.nav__caret {
  width: 0.625rem;
  height: 0.4375rem;
  transition: transform var(--d-fast) var(--ease);
}
.nav__item[data-open="true"] .nav__caret {
  transform: rotate(180deg);
}

.nav__item {
  position: relative;
}

.nav__menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 50%;
  translate: -50% 0;
  min-width: 15rem;
  padding: 0.5rem;
  border-radius: var(--r-l);
  background: var(--paper-raised);
  color: var(--text);
  border: 1px solid var(--line);
  box-shadow: 0 30px 60px -30px rgb(30 30 30 / 0.32);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--d-fast) var(--ease),
    transform var(--d-fast) var(--ease), visibility var(--d-fast) var(--ease);
}

.nav__item[data-open="true"] .nav__menu {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.nav__menu a {
  display: block;
  padding: 0.625rem 0.875rem;
  border-radius: var(--r-m);
  font-size: var(--t-small);
  color: var(--text-soft);
  transition: background-color var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease);
}
.nav__menu a:hover {
  background: rgb(30 30 30 / 0.05);
  color: var(--text);
}

.header__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s-3);
  flex: none;
}

.header__phone {
  display: none;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0.01em;
  opacity: 0.85;
  transition: opacity var(--d-fast) var(--ease);
}
.header__phone:hover {
  opacity: 1;
}
.header__phone svg {
  width: 0.875rem;
  height: 0.875rem;
}

.header__cta {
  display: none;
}

.header:not(.is-solid) .header__cta {
  --btn-bg: rgb(255 255 255 / 0.12);
  --btn-fg: #fff;
  --btn-bd: rgb(255 255 255 / 0.4);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.header:not(.is-solid) .header__cta:hover {
  --btn-bg: rgb(255 255 255 / 0.22);
}

.burger {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 2.75rem;
  height: 2.75rem;
  align-items: center;
  border-radius: 50%;
  border: 1px solid currentColor;
  color: inherit;
  opacity: 0.9;
}
.burger__bar {
  display: block;
  width: 1.125rem;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}

@media (min-width: 68rem) {
  .header__inner {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  }
  .nav {
    display: block;
  }
  .nav__list {
    justify-content: center;
  }
  .header__cta {
    display: inline-flex;
  }
  .burger {
    display: none;
  }
}

@media (min-width: 84rem) {
  .header__phone {
    display: inline-flex;
  }
}

/* --- Mobile panel ------------------------------------------------------- */

.panel {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--text);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-1.5rem);
  transition: opacity var(--d-base) var(--ease),
    transform var(--d-base) var(--ease), visibility var(--d-base) var(--ease);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.panel[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.panel__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: var(--header-h);
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--line);
}

.panel__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
}
.panel__close svg {
  width: 0.875rem;
  height: 0.875rem;
}

.panel__body {
  flex: 1 1 auto;
  padding-block: var(--s-5) var(--s-6);
}

.panel__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  width: 100%;
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem);
  font-weight: var(--w-heading);
  letter-spacing: -0.022em;
  text-align: left;
  color: var(--text);
}
.panel__link .nav__caret {
  width: 0.875rem;
  height: 0.625rem;
  color: var(--text-mute);
}
.panel__item[data-open="true"] .nav__caret {
  transform: rotate(180deg);
}

.panel__sub {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--d-base) var(--ease);
}
.panel__item[data-open="true"] .panel__sub {
  grid-template-rows: 1fr;
}
.panel__sub > ul {
  overflow: hidden;
}
.panel__sub a {
  display: block;
  padding: var(--s-3) 0 var(--s-3) var(--s-4);
  font-size: var(--t-body);
  color: var(--text-soft);
  border-bottom: 1px solid var(--line);
}
.panel__sub li:last-child a {
  border-bottom: 0;
}
.panel__item[data-open="true"] .panel__sub > ul {
  padding-block: var(--s-3);
}

.panel__foot {
  padding-block: var(--s-5) var(--s-7);
  border-top: 1px solid var(--line);
  display: grid;
  gap: var(--s-4);
}
.panel__foot .btn {
  justify-content: center;
}
.panel__contact {
  display: grid;
  gap: var(--s-2);
  font-size: var(--t-small);
  color: var(--text-soft);
}

@media (min-width: 68rem) {
  .panel {
    display: none;
  }
}

/* ==========================================================================
   09  HERO
   ========================================================================== */

.hero {
  /* The hero's bottom inset, as a token because two things sit on it: the body's
     bottom padding, which is what puts the buttons where they are, and the
     rating badge, which is absolutely positioned and has to land on the same
     line. Kept in one place so the two cannot drift apart. */
  --hero-pad-b: clamp(4rem, 2rem + 8vw, 8rem);
  position: relative;
  min-height: 100dvh;
  display: flex;
  align-items: flex-end;
  isolation: isolate;
  background: var(--ink);
  color: #fff;
  overflow: clip;
}

@supports not (min-height: 100dvh) {
  .hero {
    min-height: 100vh;
  }
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero__stack {
  position: absolute;
  inset: 0;
  will-change: transform;
}

.hero__stack picture,
.hero__stack img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.hero__stack img {
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s var(--ease-soft);
}

.hero__stack img.is-current {
  opacity: 1;
}

/* The film is the whole hero background. Its poster attribute carries the
   opening frame, so there is no image layer underneath and nothing to fade
   between: a visitor who never gets the video - reduced motion, Save-Data, a
   refused autoplay - simply keeps the poster as a still. */
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--ink);
}

/* Two overlays: a foot-weighted scrim for the copy, and a light top wash so
   the transparent header stays legible over pale ceilings. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
      to top,
      rgb(18 18 18 / 0.82) 0%,
      rgb(18 18 18 / 0.52) 28%,
      rgb(18 18 18 / 0.1) 62%,
      rgb(18 18 18 / 0.02) 84%,
      rgb(18 18 18 / 0.26) 100%
    ),
    linear-gradient(to right, rgb(18 18 18 / 0.34), rgb(18 18 18 / 0) 52%);
  pointer-events: none;
}

.hero__body {
  width: 100%;
  padding-block: var(--s-7) var(--hero-pad-b);
}

.hero__content {
  max-width: min(62rem, 100%);
}

.hero__eyebrow {
  color: rgb(255 255 255 / 0.92);
  margin-bottom: var(--s-4);
}

.hero__title {
  color: #fff;
  margin-bottom: var(--s-5);
  max-width: 18ch;
  /* the homepage line is short now, so it can carry a larger size than the shared display--4 step */
  font-size: clamp(2.5rem, 1.2rem + 5.4vw, 7.5rem);
}

/* Each headline line rides up out of a clipping frame. The frame has to reach
   far enough below the line box to clear a descender - this font's descent ends
   0.2em past it, so a "g" was being cut off flat - and the reveal has to start
   below the deepened frame, or the tops of the letters show before it plays. */
.hero__row {
  --hero-clip: 0.2em;
  display: block;
  overflow: hidden;
  padding-bottom: var(--hero-clip);
  margin-bottom: calc(var(--hero-clip) * -1);
}
.hero__line {
  display: block;
}

.hero__lede {
  color: rgb(255 255 255 / 0.86);
  font-size: var(--t-body-l);
  max-width: 42ch;
  margin-bottom: var(--s-6);
}

.hero__actions {
  align-items: center;
}

/* Entrance is CSS-only so the hero never depends on a script to be readable. */
/* The headline is generated one .hero__row per source line, so the stagger is
   positional rather than something the template has to count. */
.has-anim .hero__line {
  transform: translateY(calc(105% + var(--hero-clip, 0.2em)));
  animation: hero-line 1.15s var(--ease-soft) both;
  animation-delay: 160ms;
}
.has-anim .hero__row:nth-child(2) .hero__line {
  animation-delay: 250ms;
}
.has-anim .hero__row:nth-child(3) .hero__line {
  animation-delay: 340ms;
}
.has-anim .hero__row:nth-child(4) .hero__line {
  animation-delay: 430ms;
}
.has-anim .hero__row:nth-child(n + 5) .hero__line {
  animation-delay: 520ms;
}
@keyframes hero-line {
  from {
    transform: translateY(calc(105% + var(--hero-clip, 0.2em)));
  }
  to {
    transform: translateY(0);
  }
}

.has-anim .hero__eyebrow,
.has-anim .hero__lede,
.has-anim .hero__actions,
.has-anim .hero .rating {
  animation: rise 0.95s var(--ease) both;
}
.has-anim .hero__eyebrow {
  animation-delay: 120ms;
}
.has-anim .hero__lede {
  animation-delay: 520ms;
}
.has-anim .hero__actions {
  animation-delay: 640ms;
}
.has-anim .hero .rating {
  animation-delay: 900ms;
}

.has-anim .hero__stack {
  animation: settle 2s var(--ease-soft) both;
}

.has-anim .header__inner {
  animation: rise 0.9s var(--ease) both;
  animation-delay: 80ms;
}

/* Inner pages: the badge is a child of the phero's own .shell, so anchoring it
   to that box lines its right edge up with the hero copy's right edge and its
   base with the bottom of the copy - no separate measurement to keep in sync.
   These rules live in home.css because .phero belongs to pages.css but the
   badge is shared, and home.css is the sheet that loads on every template. */
@media (min-width: 62rem) {
  .phero > .shell {
    position: relative;
  }
  .phero .rating {
    position: absolute;
    right: var(--gutter);
    bottom: 0;
    margin-top: 0;
  }
}

/* Review badge. One partial, dropped into the hero of every template, so the
   positioning is the only thing that differs by context: absolute in the
   bottom-right corner of the hero's own container from 62rem, and in flow
   underneath the hero copy below that, where there is no room beside it. */
.rating {
  /* One token for both the Google mark and the faces, so the two can never
     drift out of step - they are meant to read as a single row of discs. */
  --rating-disc: 2.375rem;
  display: flex;
  align-items: center;
  gap: 0.875rem;
  width: fit-content;
  margin-top: var(--s-5);
  padding: 0.6875rem 1.0625rem;
  border: 1px solid rgb(255 255 255 / 0.18);
  border-radius: var(--r-l);
  background: rgb(255 255 255 / 0.08);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

/* Three faces, overlapping. The ring is the frosted panel's own tone rather
   than white, so the discs read as cut out of the badge instead of pasted on. */
.rating__faces {
  display: flex;
  flex: none;
  align-items: center;
}
.rating__faces img {
  width: var(--rating-disc);
  height: var(--rating-disc);
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 0 2px rgb(56 56 52 / 0.55);
}
.rating__faces img + img {
  /* Overlap as a fraction of the disc, so it stays proportional if the size
     token changes. */
  margin-inline-start: calc(var(--rating-disc) * -0.3);
}

.rating__mark {
  width: var(--rating-disc);
  height: var(--rating-disc);
  flex: none;
}

.rating__body {
  display: grid;
  gap: 0.3125rem;
}

.rating__stars {
  display: flex;
  gap: 0.1875rem;
  /* Google's own star yellow, not the site accent — these five marks are read as
     a Google review score, so they carry Google's colour rather than ours. It
     stays exact here because the badge sits on white and is Google's own
     widget; the testimonial stars use a darkened version of it, since the band
     they sit on is pale green. */
  color: #fbbc04;
}
.rating__stars svg {
  display: block;
  width: 0.9375rem;
  height: 0.9375rem;
}

.rating__text {
  font-size: var(--t-small);
  font-weight: 600;
  line-height: 1.2;
  color: rgb(255 255 255 / 0.92);
  white-space: nowrap;
}

@media (min-width: 62rem) {
  .hero .rating {
    position: absolute;
    right: var(--gutter);
    bottom: var(--hero-pad-b);
    margin-top: 0;
  }
}

/* ==========================================================================
   10  INTRO / WHY
   ========================================================================== */

.intro {
  background: var(--paper);
  padding-block: var(--s-9) var(--s-8);
}

.intro__head {
  display: grid;
  gap: var(--s-5);
  align-items: start;
}

.intro__statement {
  max-width: 20ch;
}

.intro__aside {
  display: grid;
  gap: var(--s-5);
  align-content: start;
}

@media (min-width: 62rem) {
  /* The eyebrow gets a row of its own so the statement and the aside both start
     on row two - their first lines then align by layout at every width, rather
     than by a hand-tuned offset that goes stale the moment the heading changes
     size. The optical nudge below is the only hand-set part, and it is small. */
  .intro__head {
    grid-template-columns: 1.15fr 1fr;
    grid-template-areas:
      "eyebrow ."
      "title   aside";
    gap: var(--s-5) var(--s-7);
  }
  /* About links to itself, so the block's "About Nova ADUs" link is suppressed
   there rather than being made conditional in the shared partial. */
.why--nolink .intro__link {
  display: none;
}

.intro__eyebrow {
    grid-area: eyebrow;
  }
  .intro__statement {
    grid-area: title;
  }
  .intro__aside {
    grid-area: aside;
    /* Aligning the boxes is not enough: the body copy carries far more leading
       than the display type, so its first line of ink lands about half its own
       font-size below the heading's. Lift it by that difference - measured at
       9-11px from 1024 to 1920 - so the two first lines start on one line. */
    margin-top: calc(var(--t-body-l) * -0.5);
  }
}

/* Oversized editorial principles — numerals, rules, air. Not feature cards. */
.principles {
  display: grid;
  gap: 0;
  margin-top: var(--s-8);
  border-top: 1px solid var(--line);
}

.principle {
  display: grid;
  gap: var(--s-3);
  padding-block: var(--s-6);
  border-bottom: 1px solid var(--line);
  align-items: start;
}

.principle__no {
  font-family: var(--font-display);
  font-size: var(--t-index);
  font-weight: var(--w-display);
  line-height: 0.8;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 1px var(--line-strong);
  user-select: none;
}

.principle__title {
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin: 0;
}

.principle__text {
  color: var(--text-soft);
  max-width: 42ch;
  margin: 0;
}

/* --- Bento variant ------------------------------------------------------- */
/* The same numbered content as a mosaic instead of ruled rows: tiles that
   alternate wide and narrow so the grid has a rhythm rather than a march.
   Opt-in — add `principles--bento` to any .principles list to switch it over.
   Declared after the base rules because the two modifiers share specificity. */

.principles--bento {
  gap: var(--s-4);
  margin-top: var(--s-7);
  border-top: 0;
}

.principles--bento .principle {
  gap: var(--s-4);
  padding: var(--s-6);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  background: var(--paper-raised);
  align-content: start;
  grid-template-columns: minmax(0, 1fr);
  /* A 1fr spacer row between the index and the copy. Tiles in a row are all as
     tall as the tallest, so without it the short ones leave their slack at the
     bottom; this puts the slack in the middle and pins the copy to the floor. */
  grid-template-rows: auto 1fr auto auto;
  grid-template-areas:
    "no"
    "."
    "title"
    "text";
}

/* The index slot holds an outline mark rather than a numeral. Sized against
   the display scale so it tracks the rest of the type. Drawn in the brand
   accent — near-identical to --accent (#8baf8f) but specified separately as the
   value asked for; unify the two if the palette should carry one accent. */
.principles--bento .principle__icon {
  grid-area: no;
  display: block;
  width: clamp(2.25rem, 1.9rem + 1.5vw, 3rem);
  /* The accent itself, as asked. Only 2.3:1 on the card, but these are aria-hidden
     decoration and the heading beside each one carries the meaning. */
  color: var(--accent);
}

.principles--bento .principle__icon svg {
  display: block;
  width: 100%;
  height: auto;
}

.principles--bento .principle__title {
  grid-area: title;
}

.principles--bento .principle__text {
  grid-area: text;
  max-width: 46ch;
}

@media (min-width: 44rem) {
  .principles--bento {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 62rem) {
  /* Six tracks so a tile can be 4 or 2 wide: wide + narrow, then narrow + wide. */
  .principles--bento {
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: var(--s-5);
  }

  .principles--bento .principle:nth-child(4n + 1),
  .principles--bento .principle:nth-child(4n + 4) {
    grid-column: span 4;
    /* A wide tile sets its title against its text rather than stacking them,
       which would leave one short line stranded across a long box. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
      "no    no"
      ".     ."
      "title text";
    column-gap: var(--s-6);
  }

  .principles--bento .principle:nth-child(4n + 2),
  .principles--bento .principle:nth-child(4n + 3) {
    grid-column: span 2;
  }

  /* An odd number of tiles would leave a hole in the last row, so the final
     one takes the full width instead. Keeps 3- and 5-item lists usable. */
  .principles--bento .principle:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}

@media (min-width: 48rem) {
  .principle {
    grid-template-columns: minmax(6rem, 0.55fr) minmax(0, 1fr) minmax(0, 1.15fr);
    gap: var(--s-5);
    /* Cap tops, not baselines. The numeral runs 3–7× the body size, so sharing
       a baseline with it pushes its ink most of a row above the title. Aligning
       the boxes instead lands all three ink tops within ~3px of each other,
       because their leading offsets happen to be near-identical: 0.04em of a
       0.8 line-height numeral, 0.23em of the title, 0.45em of the body. */
    align-items: start;
    padding-block: var(--s-6);
  }
}

@media (min-width: 62rem) {
  .principle {
    grid-template-columns: minmax(8rem, 0.5fr) minmax(0, 1fr) minmax(0, 1.2fr);
    gap: var(--s-6);
  }
}

/* --- Founder portrait, editorial scale ---------------------------------- */

.founder {
  padding-block: var(--s-8) var(--s-9);
  background: var(--paper);
}

.founder__layout {
  display: grid;
  gap: var(--s-5);
  align-items: end;
}

.founder__figure {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 5;
}

.founder__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
}

/* The caption block is anchored to the foot of the portrait and given a rule,
   so the open space beside it reads as composition rather than a gap. */
.founder__aside {
  display: grid;
  gap: var(--s-3);
  align-content: end;
  padding-bottom: var(--s-3);
}

.founder__caption {
  font-size: var(--t-small);
  color: var(--text-mute);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
}

.founder__credit {
  font-size: var(--t-small);
  color: var(--text-mute);
  padding-top: var(--s-3);
}
.founder__credit strong {
  display: block;
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.15;
  color: var(--text);
  letter-spacing: -0.02em;
  margin-bottom: 0.15em;
}

@media (min-width: 62rem) {
  .founder__layout {
    grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
    gap: var(--s-6);
  }
  .founder__figure {
    aspect-ratio: 4 / 5;
  }
  .founder__aside {
    padding-bottom: var(--s-6);
  }
}

/* ==========================================================================
   11  SELECTED WORK
   ========================================================================== */

.work {
  /* FOREST rather than charcoal: the one large surface that carries the brand
     green. It has no photograph behind it, so the colour reads as chosen. */
  background: var(--ink-soft);
  padding-block: var(--s-9);
  overflow: clip;
  /* Remap the semantic tokens rather than restating colours, so every
     component inside inherits the dark treatment. */
  --text: var(--text-invert);
  --text-soft: var(--text-invert-soft);
  /* STONE is 5.4:1 on charcoal but only 3.38:1 on forest, so the muted tone is
     lifted here. Overriding the invert token itself — not just --text-mute —
     because .stage__facts dt reaches for --text-invert-mute directly. */
  --text-invert-mute: #a6aba6;
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --focus: var(--focus-invert);
  color: var(--text);
}

/* The photo frame's own backdrop is --ink-soft, which is now the band itself;
   drop it back to charcoal so the frame still reads as a frame. */
.work .stage__viewport {
  background: var(--ink);
}

.work__head {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--s-5);
  margin-bottom: var(--s-6);
}

.work__title {
  max-width: 16ch;
}

.stage {
  position: relative;
}

.stage__layout {
  display: grid;
  gap: var(--s-5);
}

/* Stacked, the photograph leads and the caption follows it — the section has
   to stay image-led on a phone, not open with a block of text. */
@media (max-width: 61.999rem) {
  .stage__viewport {
    order: -1;
  }
}

/* The image is the product: it takes the lion's share of the composition. */
.stage__viewport {
  position: relative;
  border-radius: var(--r-l);
  overflow: hidden;
  background: var(--ink-soft);
  aspect-ratio: 4 / 3;
  touch-action: pan-y;
  cursor: grab;
}
.stage__viewport:active {
  cursor: grabbing;
}

/* The crossfade runs on the *incoming* frame while the outgoing one stays
   opaque underneath it. If the animation never gets to run — a backgrounded
   tab throttles it — the worst case is the previous photograph staying on
   screen, never an empty frame. */
.stage__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}

.stage__slide.was-current {
  opacity: 1;
  z-index: 1;
}

.stage__slide.is-current {
  opacity: 1;
  z-index: 2;
  pointer-events: auto;
}

.stage__slide.is-swapping {
  animation: fade 800ms var(--ease-soft);
}

.stage__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.stage__slide.is-swapping img {
  animation: settle 1.5s var(--ease-soft);
}

.stage__slide::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgb(18 18 18 / 0.72),
    rgb(18 18 18 / 0.1) 46%,
    rgb(18 18 18 / 0) 70%
  );
  pointer-events: none;
}

.stage__meta {
  display: grid;
  gap: var(--s-5);
  align-content: start;
}

.stage__meta-item {
  display: none;
  gap: var(--s-4);
}
/* Transform-only entrance: a stalled animation leaves the caption legible,
   just un-nudged. */
.stage__meta-item.is-current {
  display: grid;
  animation: nudge var(--d-base) var(--ease);
}

@keyframes nudge {
  from {
    transform: translate3d(0, 0.625rem, 0);
  }
  to {
    transform: none;
  }
}

.stage__name {
  font-family: var(--font-display);
  font-size: var(--t-d2);
  font-weight: var(--w-heading);
  line-height: 1.06;
  letter-spacing: -0.028em;
  color: #fff;
  margin: 0;
}

.stage__facts {
  display: grid;
  gap: 0.375rem;
  font-size: var(--t-small);
}
.stage__facts div {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  border-top: 1px solid var(--line-invert);
  padding-top: 0.625rem;
}
.stage__facts dt {
  flex: none;
  width: 5.5rem;
  color: var(--text-invert-mute);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: var(--t-label);
  font-weight: 600;
}
.stage__facts dd {
  margin: 0;
  color: var(--text-invert-soft);
}

.stage__controls {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

.stage__controls .counter {
  margin-left: auto;
}

/* On a phone the counter and the progress rail are noise beside two arrows,
   and the row has room for the link that would otherwise sit above it. The
   per-slide link in the meta block cannot be moved into this row by CSS —
   different container — so the row carries its own copy and the script
   re-points it on each step. Above 62rem the meta column has space for the
   link in its own right, and the rail earns its place again. */
.stage__cta {
  margin: 0 0 0 auto;
}

@media (max-width: 61.999rem) {
  .stage__controls .stage__progress,
  .stage__controls .counter,
  .stage__link {
    display: none;
  }
}

@media (min-width: 62rem) {
  .stage__cta {
    display: none;
  }
}

.stage .ctrl {
  border-color: var(--line-invert-strong);
  color: #fff;
}

.stage__progress {
  position: relative;
  flex: 1 1 auto;
  height: 1px;
  background: var(--line-invert);
  max-width: 12rem;
}
.stage__progress span {
  position: absolute;
  inset: 0 auto 0 0;
  background: #fff;
  transition: width var(--d-base) var(--ease);
}

.work__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-6);
}

.work .arrow-link {
  color: #fff;
  border-color: var(--line-invert-strong);
}

@media (min-width: 62rem) {
  /* The image runs at roughly three quarters of the composition: the work is
     the product, the caption is the label. */
  .stage__layout {
    grid-template-columns: minmax(15rem, 1fr) minmax(0, 3.1fr);
    gap: var(--s-6);
    align-items: stretch;
  }
  .stage__meta {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding-block: var(--s-2);
  }
  .stage__viewport {
    aspect-ratio: 16 / 10;
  }
}

@media (min-width: 90rem) {
  .stage__viewport {
    aspect-ratio: 1.75 / 1;
  }
}

/* ==========================================================================
   12  IMAGE BREAK — a visual pause, full bleed
   ========================================================================== */

.break {
  position: relative;
  display: grid;
  place-items: center;
  min-height: clamp(26rem, 19rem + 36vw, 54rem);
  background: var(--ink);
  overflow: clip;
  isolation: isolate;
}

.break__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.break__media picture,
.break__media img {
  width: 100%;
  height: 100%;
}
.break__media picture {
  display: block;
}
.break__media img {
  object-fit: cover;
  will-change: transform;
}

.break::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgb(18 18 18 / 0.34),
    rgb(18 18 18 / 0.12) 45%,
    rgb(18 18 18 / 0.46)
  );
}

.break__statement {
  color: #fff;
  text-align: center;
  max-width: 18ch;
  padding-inline: var(--gutter);
  text-shadow: 0 2px 40px rgb(0 0 0 / 0.35);
}

@media (min-width: 62rem) {
  /* Three lines, at every desktop width.

     The box is border-box, so a plain `max-width` in ch has the gutter inside
     it and the measure is only whatever is left over. That is stable while
     both tokens grow together, but --t-d3 tops out at 1768px and --gutter
     keeps growing to 2107px, so past ~1900px the padding eats into the text
     and the statement drops to a fourth line. Adding the gutter back makes the
     measure a true 20ch regardless of viewport. 20ch sits mid-band: it takes
     roughly 16ch to spill to four lines and 22ch to pull up to two. */
  .break__statement {
    max-width: calc(20ch + 2 * var(--gutter));
  }
}

/* ==========================================================================
   13  SERVICES — large editorial image cards
   ========================================================================== */

.services {
  background: var(--paper);
  padding-block: var(--s-9);
}

.services__head {
  display: grid;
  gap: var(--s-5);
  margin-bottom: var(--s-7);
}

.services__title {
  max-width: 18ch;
}

.services__grid {
  display: grid;
  gap: var(--s-6) var(--s-5);
}

.card {
  display: grid;
  gap: var(--s-4);
  grid-template-rows: auto 1fr;
  align-content: start;
}

.card__figure {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

.card__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s var(--ease-soft);
}

.card:hover .card__figure img,
.card:focus-visible .card__figure img {
  transform: scale(1.045);
}

.card__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "head cue"
    "text text";
  align-items: start;
  gap: var(--s-3) var(--s-4);
  align-content: start;
}

.card__head {
  grid-area: head;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s-3);
}

.card__text {
  grid-area: text;
}

.card__no {
  font-family: var(--font-display);
  font-size: var(--t-small);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-mute);
  letter-spacing: 0.06em;
  flex: none;
  padding-top: 0.2em;
}

.card__title {
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.12;
  letter-spacing: -0.022em;
  margin: 0;
}

.card__text {
  color: var(--text-soft);
  font-size: var(--t-small);
  line-height: 1.62;
  max-width: 46ch;
  margin: 0;
}

/* Arrow only — the card title is the label, so a second one would be noise. */
.card__cue {
  grid-area: cue;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--text-mute);
  transition: background-color var(--d-fast) var(--ease),
    border-color var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.card__cue svg {
  width: 0.9375rem;
  height: 0.6875rem;
}
.card:hover .card__cue,
.card:focus-visible .card__cue {
  /* GOLD fill, charcoal arrow — 8.8:1, and --accent/--ink are not remapped on
     dark grounds so the same rule holds wherever the cue appears. */
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}
@media (min-width: 48rem) {
  .services__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 62rem) {
  .services__head {
    grid-template-columns: 1.2fr 1fr;
    align-items: end;
    gap: var(--s-7);
  }
  .services__grid {
    gap: var(--s-7) var(--s-6);
  }
}

/* ==========================================================================
   14  CRAFT — asymmetric, sticky statement against detail photography

   These components moved to the About page's "How we work" band, which
   supplies its own ground; `.craft` itself is the homepage wrapper the section
   used to sit in and is kept only so the section can be dropped back in.
   ========================================================================== */

.craft {
  background: var(--sand);
  padding-block: var(--s-9);
}

.craft__layout {
  display: grid;
  gap: var(--s-6);
}

.craft__aside {
  display: grid;
  gap: var(--s-5);
  align-content: start;
}

.craft__items {
  display: grid;
  gap: var(--s-6);
}

.craft__item {
  display: grid;
  gap: var(--s-4);
}

.craft__figure {
  overflow: hidden;
  border-radius: var(--r-l);
  background: rgb(30 30 30 / 0.06);
  aspect-ratio: 3 / 2;
}
.craft__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.craft__label {
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0;
}

.craft__text {
  color: var(--text-soft);
  font-size: var(--t-small);
  line-height: 1.62;
  width: 100%;
  max-width: none;
  margin: 0;
}

@media (min-width: 62rem) {
  .craft__layout {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
    gap: var(--s-7);
    align-items: start;
  }
  .craft__aside {
    position: sticky;
    top: calc(var(--header-h) + var(--s-6));
  }
  .craft__items {
    gap: var(--s-8);
  }
  /* Keep every craft image frame the same width and 3 / 2 ratio. */
}

/* ==========================================================================
   15  TESTIMONIALS — one voice at a time, at editorial scale
   ========================================================================== */

.voices {
  /* Sand, inheriting the band the craft section used to hold, so services and
     areas still sit either side of a change of ground. */
  background: var(--sand);
  padding-block: var(--s-9) var(--s-9);
}

.voices__inner {
  display: grid;
  gap: var(--s-7);
}

.voices__quote {
  display: grid;
  gap: var(--s-6);
}

/* Vertical scrolling stays the browser's; the horizontal axis is the
   carousel's. Without this the pointer stream is cancelled the moment the
   page decides a touch is a scroll, and the swipe never completes. */
[data-voice-viewport] {
  touch-action: pan-y;
}

.quote {
  margin: 0;
  display: none;
}
.quote.is-current {
  display: grid;
  gap: var(--s-5);
  animation: nudge var(--d-base) var(--ease);
}

.quote__text {
  font-family: var(--font-display);
  font-size: var(--t-quote);
  font-weight: 500;
  line-height: 1.22;
  letter-spacing: -0.026em;
  color: var(--text);
  margin: 0;
  max-width: 34rem;
  text-wrap: pretty;
}

.quote__text::before {
  content: "\201C";
}
.quote__text::after {
  content: "\201D";
}

/* Five review stars over each quote — read as a rating, so they carry the
   yellow a rating is expected to be rather than the site accent. NOT Google's
   own #fbbc04: that measures 1.47:1 on the pale-green band these sit on, which
   is barely there. This is that yellow darkened until it clears 3:1 (3.07:1 on
   --sand, 3.40:1 on --paper) — the same bar the sage stars it replaces met.
   Decorative to a screen reader: the visually hidden line beside them carries
   the meaning instead of five unlabelled graphics. */
.quote__stars {
  display: flex;
  gap: 0.25rem;
  margin: 0;
  color: #a98200;
}
.quote__stars > span[aria-hidden] {
  display: flex;
  gap: 0.25rem;
}
.quote__stars svg {
  display: block;
  width: 1rem;
  height: 1rem;
}

.quote__cite {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-mute);
}

/* A monogram sits under an optional photograph: rows carrying an `avatar`
   cover it, rows without one keep the initials. Nobody's face is invented for
   a placeholder testimonial that way. */
.quote__avatar {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  overflow: hidden;
  background: var(--sand);
  border: 1px solid var(--line);
}

.quote__avatar-mono {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-soft);
}

.quote__avatar-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

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

.voices__controls {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
}
.voices__controls .counter {
  margin-left: auto;
}

@media (min-width: 62rem) {
  .voices__inner {
    grid-template-columns: minmax(0, 0.5fr) minmax(0, 1.5fr);
    gap: var(--s-7);
    align-items: start;
  }
  .quote__text {
    max-width: 52rem;
  }
}

/* ==========================================================================
   16  AREAS WE SERVE
   ========================================================================== */

.areas {
  background: var(--paper);
  padding-block: var(--s-8) var(--s-9);
}

.areas__layout {
  display: grid;
  gap: var(--s-6);
}

.areas__aside {
  display: grid;
  gap: var(--s-5);
  align-content: start;
}

.areas__title {
  max-width: 16ch;
}

.areas__list {
  border-top: 1px solid var(--line);
}

.areas__list a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: clamp(0.875rem, 0.6rem + 1vw, 1.375rem);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-display);
  font-size: clamp(1.125rem, 0.95rem + 0.75vw, 1.75rem);
  font-weight: var(--w-heading);
  letter-spacing: -0.024em;
  line-height: 1.2;
  color: var(--text);
  transition: color var(--d-fast) var(--ease),
    padding-inline-start var(--d-base) var(--ease);
}

.areas__list a svg {
  width: 1.125rem;
  height: 0.875rem;
  flex: none;
  /* GOLD itself, the lightest step, because these should register as a hint
     rather than as marks in their own right. It is faint - about 1.7:1 on paper
     - but the arrow is aria-hidden decoration repeated down a list of
     full-contrast city names, so it carries no meaning of its own and 1.4.11
     does not bind. Hover takes it to --accent-ink along with the label, which
     is where the row actually announces itself. */
  color: var(--accent);
  transition: color var(--d-fast) var(--ease);
}

.areas__list a:hover,
.areas__list a:focus-visible {
  color: var(--accent-ink);
  padding-inline-start: var(--s-3);
}
.areas__list a:hover svg,
.areas__list a:focus-visible svg {
  color: var(--accent-ink);
}

@media (min-width: 62rem) {
  .areas__layout {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: var(--s-7);
    align-items: start;
  }
  .areas__aside {
    position: sticky;
    top: calc(var(--header-h) + var(--s-6));
  }
}

/* ==========================================================================
   17  FINAL CTA
   ========================================================================== */

.close {
  position: relative;
  background: var(--ink);
  padding-block: var(--s-10);
  overflow: clip;
  isolation: isolate;
  --text: var(--text-invert);
  --text-soft: var(--text-invert-soft);
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --focus: var(--focus-invert);
  color: var(--text);
}

.close__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
/* <picture> is inline by default: without a height of its own, height:100% on
   the <img> resolves against the picture element and a short photograph leaves
   a bare strip of the section ground showing. */
.close__media picture,
.close__media img {
  width: 100%;
  height: 100%;
}
.close__media picture {
  display: block;
}
.close__media img {
  object-fit: cover;
  opacity: 0.46;
}

.close::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgb(18 18 18 / 0.72),
    rgb(18 18 18 / 0.5) 45%,
    rgb(18 18 18 / 0.85)
  );
}

.close__inner {
  display: grid;
  gap: var(--s-5);
  max-width: 42rem;
}

.close__title {
  color: #fff;
}

.close__lede {
  color: rgb(255 255 255 / 0.78);
  max-width: 40ch;
}

.close__actions {
  margin-top: var(--s-3);
}

/* ==========================================================================
   17a  CREDENTIALS MARQUEE — the same facts, scrolling
   ========================================================================== */

/* The track is scrolled by exactly one group width, which home.js measures and
   writes to --marquee-shift, so the seam never shows regardless of how many
   items there are or how wide the screen is. Duration is derived from the same
   measurement, so the speed stays constant instead of racing on a phone.

   Full bleed: the band spans the window and carries the sand ground, which
   separates it from the paper sections it sits between. */
.marquee {
  background: var(--sand);
  border-bottom: 1px solid var(--line);
}

.marquee__viewport {
  overflow: hidden;
  padding-block: var(--s-4);
  /* Feather the ends so items enter and leave rather than being guillotined.
     On the viewport, not the band, so the ground and the rule stay solid. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}

.marquee__track {
  display: flex;
  width: max-content;
}

.marquee.is-running .marquee__track {
  animation: marquee-scroll var(--marquee-duration, 40s) linear infinite;
}

@keyframes marquee-scroll {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(calc(var(--marquee-shift, 0px) * -1), 0, 0);
  }
}

.marquee__group {
  display: flex;
  align-items: center;
  flex: none;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The gap lives on the item, not the group, so a group's measured width
   already contains its trailing space and the shift is exact. */
.marquee__item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: none;
  margin-inline-end: clamp(2.5rem, 1.5rem + 3vw, 5rem);
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-soft);
  white-space: nowrap;
}

.marquee__icon {
  display: block;
  width: 1.5rem;
  flex: none;
  color: var(--accent-hover);
}
.marquee__icon svg {
  display: block;
  width: 100%;
  height: auto;
}

/* No JS, or a visitor who asked for less motion: the row simply sits there, so
   it has to be padded in from the window edges and allowed to wrap. */
.marquee:not(.is-running) .marquee__viewport {
  padding-inline: var(--gutter);
  -webkit-mask-image: none;
  mask-image: none;
}
.marquee:not(.is-running) .marquee__track {
  width: 100%;
}
/* The group is a flex item sized to max-content while it scrolls; standing
   still it has to be allowed to shrink, or it overflows the track and the
   viewport's overflow:hidden quietly eats two of the three facts. */
.marquee:not(.is-running) .marquee__group {
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: wrap;
  gap: var(--s-3) 0;
}
.marquee:not(.is-running) .marquee__item {
  margin-inline-end: var(--s-5);
  white-space: normal;
}

@media (prefers-reduced-motion: reduce) {
  .marquee .marquee__track {
    animation: none;
  }
}

/* ==========================================================================
   17b  CREDENTIALS — the numbers, as a shared strip
   ========================================================================== */

/* Three facts, rendered from one global field so About and the homepage cannot
   drift. Auto-fit rather than a fixed three-up: the strip is dropped into
   different column widths, and a hard 3 would overflow inside a narrow one. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: var(--s-5) var(--s-6);
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}

.stat {
  padding-top: var(--s-5);
}

.stat__value {
  font-family: var(--font-display);
  /* One step under the section headings: these are supporting facts, not the
     statement. The value can be a word ("Generational") as well as a number,
     so the measure has to survive both. */
  font-size: var(--t-d2);
  font-weight: var(--w-heading);
  line-height: 1.02;
  letter-spacing: -0.028em;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: balance;
}

.stat__label {
  margin: var(--s-2) 0 0;
  max-width: 26ch;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--text-soft);
}

/* ==========================================================================
   18  FOOTER
   ========================================================================== */

.footer {
  background: var(--ink);
  color: #fff;
  padding-block: var(--s-8) var(--s-6);
  --text: #fff;
  --text-soft: var(--text-invert-soft);
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --focus: #fff;
}

.footer__top {
  display: grid;
  gap: var(--s-6);
  padding-bottom: var(--s-7);
}

.footer__brand {
  display: grid;
  gap: var(--s-4);
  align-content: start;
  max-width: 22rem;
}

.footer__brand .brand__mark {
  height: clamp(2rem, 1.75rem + 1.1vw, 2.75rem);
}

.footer__tagline {
  color: var(--text-invert-soft);
  font-size: var(--t-small);
  max-width: 28ch;
}

.footer__social {
  display: flex;
  gap: var(--s-3);
}
.footer__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid var(--line-invert);
  transition: background-color var(--d-fast) var(--ease),
    border-color var(--d-fast) var(--ease), color var(--d-fast) var(--ease);
}
.footer__social a:hover {
  background: #fff;
  border-color: #fff;
  color: var(--ink);
}
.footer__social svg {
  width: 1.125rem;
  height: 1.125rem;
}

.footer__heading {
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-invert-mute);
  margin: 0 0 var(--s-4);
}

.footer__links {
  display: grid;
  gap: 0.75rem;
}
.footer__links a {
  font-size: var(--t-small);
  color: var(--text-invert-soft);
  transition: color var(--d-fast) var(--ease);
}
.footer__links a:hover {
  color: #fff;
}

.footer__contact {
  display: grid;
  gap: 0.875rem;
  font-size: var(--t-small);
  color: var(--text-invert-soft);
}
.footer__contact-item {
  display: flex;
  gap: 0.75rem;
  align-items: start;
  transition: color var(--d-fast) var(--ease);
}
a.footer__contact-item:hover {
  color: #fff;
}
.footer__contact-icon {
  width: 1.0625rem;
  height: 1.0625rem;
  flex: none;
  margin-top: 0.15em;
  color: var(--text-invert-mute);
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3) var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line-invert);
  font-size: var(--t-small);
  color: var(--text-invert-mute);
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
}
.footer__legal a {
  color: var(--text-invert-soft);
  transition: color var(--d-fast) var(--ease);
}
.footer__legal a:hover {
  color: #fff;
}

/* The back-to-top control is injected as the last child of .footer__legal, so
   it finishes the bottom row. Once that row wraps onto a line of its own —
   every phone, and tablets down to the point the three blocks stop fitting —
   the group takes the whole line and the button is pushed to the far right, so
   it sits in the corner of the footer instead of a few pixels after the phone
   number. On desktop the group is only as wide as its links, so the auto
   margin has no free space to take and nothing moves. */
.footer__legal .to-top {
  margin-left: auto;
}

@media (max-width: 61.999rem) {
  .footer__legal {
    flex: 1 1 100%;
  }
}

@media (min-width: 48rem) {
  .footer__top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-7) var(--s-6);
  }
}

@media (min-width: 62rem) {
  .footer__top {
    grid-template-columns: 1.4fr 1fr 1fr 1.3fr;
  }
}

/* On a phone the footer links were 16px tall and the contact rows 21px. Rows
   become 44px targets by absorbing the list gap rather than adding height, so
   the footer keeps roughly the same overall depth. */
@media (pointer: coarse) {
  /* The slide-out menu's phone and email were 20px tall - the one place the
     original touch-target pass missed, and the place it matters most, since
     the panel only exists on touch. The list absorbs its own gap so the panel
     does not grow taller for it. */
  .panel__contact {
    gap: 0;
  }

  .panel__contact a {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
  }

  .footer__links {
    gap: 0;
  }

  .footer__links a {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
  }

  .footer__contact {
    gap: 0.25rem;
  }

  a.footer__contact-item {
    min-height: 2.75rem;
    align-items: center;
  }

  .footer__legal a,
  .arrow-link {
    min-height: 2.75rem;
  }

  .footer__social a {
    min-width: 2.75rem;
    min-height: 2.75rem;
  }
}

/* ==========================================================================
   19  INJECTED CONTROLS
   The back-to-top button and the studio credit are appended to the finished
   HTML by two sandbox plugins, so the homepage has to carry their styles.
   ========================================================================== */

.footer__credit {
  font-size: var(--t-small);
  color: var(--text-invert-mute);
  margin: 0;
}
.footer__credit a {
  color: var(--text-invert-soft);
  border-bottom: 1px solid var(--line-invert);
  transition: color var(--d-fast) var(--ease),
    border-color var(--d-fast) var(--ease);
}
.footer__credit a:hover {
  color: #fff;
  border-color: #fff;
}

.to-top {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid var(--line-invert);
  color: var(--text-invert-soft);
  transition: background-color var(--d-fast) var(--ease),
    border-color var(--d-fast) var(--ease), color var(--d-fast) var(--ease),
    opacity var(--d-base) var(--ease);
}
.to-top:hover {
  background: #fff;
  border-color: #fff;
  color: var(--ink);
}
.to-top__icon svg {
  width: 0.75rem;
  height: 1rem;
}
.footer.has-to-top-reveal .to-top {
  opacity: 0;
}
.footer.has-to-top-reveal .to-top.is-visible {
  opacity: 1;
}

/* ==========================================================================
   20  PRINT
   ========================================================================== */

@media print {
  .header,
  .panel,
  .rating,
  .stage__controls,
  .voices__controls,
  .to-top {
    display: none !important;
  }
  body {
    background: #fff;
    color: #000;
  }
  .hero,
  .work,
  .close,
  .footer {
    background: #fff !important;
    color: #000 !important;
  }
}

/* ==========================================================================
   NWB FLOATING HEADER
   ========================================================================== */

.header,
.header.is-solid {
  inset: clamp(0.75rem, 1.5vw, 1.5rem) clamp(0.75rem, 1.5vw, 1.5rem) auto;
  padding-block: 0;
  background: #fff;
  color: var(--text);
  border: 1px solid rgb(30 30 30 / 0.08);
  border-radius: clamp(0.875rem, 1.25vw, 1.25rem);
  box-shadow: 0 16px 48px -24px rgb(37 34 31 / 0.32);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.header::before,
.header.is-solid::before {
  display: none;
}

.header__inner {
  max-width: none;
  min-height: clamp(4rem, 5.8vw, 5.75rem);
  padding-inline: clamp(1rem, 2.4vw, 2.5rem);
}

/* 18% larger than the original 1.75 / 1.5+1.1vw / 2.375 ramp. This trio of
   selectors is (0,3,0) and outranks `.nwb-global-header .brand__mark`, so the
   size has to be set here as well or the header keeps the old value. */
.header .brand__mark,
.header:not(.is-solid) .brand__mark,
.header.is-solid .brand__mark {
  height: clamp(2.065rem, 1.77rem + 1.3vw, 2.8025rem);
  filter: none;
}

.header .nav__list {
  gap: clamp(0.5rem, 1.15vw, 1.25rem);
}

.header .nav__link {
  padding: 0.625rem 0.75rem;
  font-size: clamp(0.875rem, 0.8rem + 0.15vw, 1rem);
  font-weight: 500;
  letter-spacing: 0;
  opacity: 1;
}

.header .nav__link:hover,
.header .nav__item[data-open="true"] .nav__link {
  background: rgb(30 30 30 / 0.06);
}

.header .nav__caret {
  display: none;
}

.header__actions {
  gap: clamp(1rem, 1.5vw, 1.75rem);
}

.header__phone {
  font-size: clamp(0.875rem, 0.82rem + 0.12vw, 1rem);
  letter-spacing: 0;
  opacity: 1;
}

.header .header__cta,
.header:not(.is-solid) .header__cta,
.header.is-solid .header__cta {
  --btn-bg: var(--ink-soft);
  --btn-fg: #fff;
  --btn-bd: var(--ink-soft);
  min-height: 3rem;
  padding: 0.75rem 1.75rem;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.header .header__cta:hover,
.header:not(.is-solid) .header__cta:hover {
  --btn-bg: var(--ink);
  --btn-bd: var(--ink);
}

.header__cta .btn__arrow {
  display: none;
}

@media (max-width: 67.99rem) {
  .header,
  .header.is-solid {
    inset: 0.75rem 0.75rem auto;
    border-radius: 0.875rem;
  }

  .header__inner {
    min-height: 4rem;
    padding-inline: 1rem;
  }

  .header .brand__mark,
  .header:not(.is-solid) .brand__mark,
  .header.is-solid .brand__mark {
    height: 2.065rem;
    filter: none;
  }

  .burger {
    width: 2.75rem;
    height: 2.75rem;
    border: 0;
    border-radius: 0;
    opacity: 1;
  }

  .burger__bar {
    width: 1.25rem;
  }
}

/* Floating-header fidelity refinement. */
@media (min-width: 68rem) {
  .header__inner {
    padding-inline: clamp(1rem, 3vw, 3rem) 1.5rem;
  }

  .header .brand__mark,
  .header:not(.is-solid) .brand__mark,
  .header.is-solid .brand__mark {
    filter: none;
  }

  .header .nav__list {
    gap: 3rem;
  }

  .header .header__cta,
  .header:not(.is-solid) .header__cta,
  .header.is-solid .header__cta {
    padding-inline: 2.125rem;
  }
}

@media (min-width: 84rem) and (max-width: 89.99rem) {
  .header__phone {
    display: none;
  }
}

@media (min-width: 90rem) {
  .header .nav {
    transform: translateX(-3.25rem);
  }

  .header__actions {
    gap: 2rem;
  }

  .header__phone {
    gap: 0.75rem;
    letter-spacing: 0.04em;
  }
}
