/* ==========================================================================
   Massive Financial — David Bryan
   ========================================================================== */

:root {
  /* Brand */
  --navy-900: #060f22;
  --navy-800: #0a1b3d;
  --navy-700: #0e2650;
  --navy-600: #143165;
  --blue-600: #1d4ed8;
  --blue-500: #2f6bff;
  --blue-400: #4d90ff;
  --blue-300: #7fb2ff;
  --plum-900: #262040;
  --plum-800: #2e2749;

  --ink: #101828;
  --ink-soft: #475467;
  --ink-mute: #667085;
  --line: #e6e9f0;
  --surface: #ffffff;
  --surface-alt: #f5f7fb;
  --gold: #e0b23c;

  /* Type */
  --font-display: "Satoshi", "Outfit", ui-sans-serif, system-ui, sans-serif;
  --font-body: "General Sans", "Satoshi", ui-sans-serif, system-ui, sans-serif;

  --step--1: clamp(0.895rem, 0.87rem + 0.12vw, 0.975rem);
  --step-0: clamp(1.045rem, 1.01rem + 0.17vw, 1.15rem);
  --step-1: clamp(1.2rem, 1.13rem + 0.32vw, 1.42rem);
  --step-2: clamp(1.55rem, 1.38rem + 0.78vw, 2.18rem);
  --step-3: clamp(1.98rem, 1.6rem + 1.7vw, 3.1rem);
  --step-4: clamp(2.1rem, 1.55rem + 2.35vw, 3.35rem);

  /* Space */
  --gutter: clamp(1.15rem, 4vw, 2.5rem);
  --section-y: clamp(3.5rem, 7vw, 7rem);
  --maxw: 1475px;
  /* header pill, mobile nav panel and footer run 10% wider than the page shell,
     so the chrome frames the content rather than lining up flush with it. This
     only takes effect once the viewport is wide enough for the 1475px cap to be
     the binding constraint — below roughly 1600px the viewport is narrower than
     either cap and both widths are gutter-bound instead. */
  --maxw-wide: 1623px;

  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 32px;

  --shadow-card: 0 18px 45px -22px rgba(10, 27, 61, 0.28);
  --shadow-float: 0 26px 60px -26px rgba(10, 27, 61, 0.42);
}

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

/* The canvas, not just the body, carries the dark colour. A root background
   propagates to the canvas, which is what paints iOS safe-area insets and the
   region Safari exposes when its bottom toolbar collapses on scroll. Left
   white, both showed as a white strip against the navy hero — one behind the
   status bar, one under the CTAs. `body` keeps its white surface for the rest
   of the document; only the areas body does not paint fall through to this. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--navy-900);
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.68;
  color: var(--ink-soft);
  background: var(--surface);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  line-height: 1.12;
  letter-spacing: -0.022em;
  margin: 0 0 0.6em;
  font-weight: 700;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-1); }
p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

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

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-size: 0.8464rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blue-500);
  margin-bottom: 0.9rem;
}
.eyebrow::before {
  content: "";
  width: 30px;
  height: 2px;
  background: var(--blue-500);
  border-radius: 2px;
}
.eyebrow--center { justify-content: center; }
.eyebrow--light { color: var(--blue-300); }
.eyebrow--light::before { background: var(--blue-300); }

.section { padding-block: var(--section-y); }
.section-head { max-width: 640px; margin-inline: auto; text-align: center; margin-bottom: clamp(2.2rem, 4vw, 3.4rem); }
.section-head p { color: var(--ink-mute); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.073rem;
  letter-spacing: -0.01em;
  padding: 0.9rem 1.6rem;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, color 0.25s ease;
  will-change: transform;
}
.btn:hover { transform: translateY(-2px); }
.btn--primary {
  background: linear-gradient(180deg, var(--blue-500), var(--blue-600));
  color: #fff;
  box-shadow: 0 14px 30px -12px rgba(47, 107, 255, 0.7);
}
.btn--primary:hover { box-shadow: 0 20px 38px -12px rgba(47, 107, 255, 0.85); }
.btn--ghost { background: rgba(255, 255, 255, 0.08); color: #fff; border-color: rgba(255, 255, 255, 0.28); }
.btn--ghost:hover { background: rgba(255, 255, 255, 0.16); }
.btn--light { background: #fff; color: var(--navy-800); box-shadow: var(--shadow-card); }
.btn--outline { background: transparent; color: var(--navy-800); border-color: var(--line); }
.btn--outline:hover { border-color: var(--blue-500); color: var(--blue-600); }
.btn--sm { padding: 0.62rem 1.25rem; font-size: 0.9984rem; }
.btn--block { width: 100%; }

/* ---------- Header ---------- */
.masthead {
  /* Fixed, not sticky: the pill floats OVER the hero photograph instead of
     reserving a solid band above it. Pages add top padding via --mast-h. */
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 90;
  background: transparent;
  border-bottom: 0;
  padding: clamp(0.65rem, 1.4vw, 0.95rem) clamp(0.9rem, 3vw, 1.5rem);
}
.masthead__inner {
  max-width: var(--maxw-wide);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 68px;
  /* overrides .shell's gutter: the gutter now lives on .masthead itself, so the
     pill can sit inset from the viewport edge on every width. */
  padding-inline: clamp(0.9rem, 1.8vw, 1.5rem);
  border-radius: 999px;
  background: rgba(6, 15, 34, 0.52);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 18px 44px -26px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(22px) saturate(1.5);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
.masthead.is-stuck .masthead__inner {
  background: rgba(6, 15, 34, 0.88);
  box-shadow: 0 22px 52px -24px rgba(0, 0, 0, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* No backdrop-filter (older Firefox): fall back to a near-opaque pill so the
   nav never sits as low-contrast text directly on the photograph. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .masthead__inner { background: rgba(6, 15, 34, 0.93); }
}
/* the mobile panel becomes a rounded glass card under the pill */
.mobile-nav {
  margin: 0.55rem clamp(0.9rem, 3vw, 1.5rem) 0;
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(6, 15, 34, 0.94);
  backdrop-filter: blur(22px) saturate(1.4);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  box-shadow: 0 26px 60px -28px rgba(0, 0, 0, 0.85);
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
  overflow: hidden auto;
}

.brand { display: inline-flex; align-items: center; gap: 0.7rem; }
.brand__mark {
  width: 44px;
  height: 44px;
  object-fit: contain;
  flex: none;
}
.brand__type { display: flex; flex-direction: column; line-height: 1.05; }
.brand__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.06rem;
  letter-spacing: -0.02em;
  color: #fff;
}
.brand__tag {
  font-size: 0.7776rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--blue-300);
}

.nav { display: flex; align-items: center; gap: 0.35rem; }
.nav a {
  font-family: var(--font-display);
  font-size: 1.034rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.76);
  padding: 0.5rem 0.85rem;
  border-radius: 999px;
  transition: color 0.2s ease, background 0.2s ease;
}
.nav a:hover, .nav a.is-active { color: #fff; background: rgba(255, 255, 255, 0.08); }

.masthead__cta { display: flex; align-items: center; gap: 0.6rem; }

.burger {
  display: none;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
  padding: 0;
  place-items: center;
}
.burger span {
  display: block;
  width: 18px;
  height: 2px;
  margin: 3px auto;
  background: #fff;
  border-radius: 2px;
  transition: transform 0.3s ease, opacity 0.2s ease;
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

.mobile-nav {
  display: none;
  background: var(--navy-900);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  /* never taller than the space under the sticky masthead, and always scrollable */
  max-height: calc(100dvh - var(--mast-h, 76px) - 8px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.mobile-nav.is-open { display: block; }
/* Panel owns the bottom breathing room so the CTA never sits flush on the edge. */
.mobile-nav > .shell { max-width: var(--maxw-wide); padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px)); }
.mobile-nav ul { list-style: none; margin: 0; padding: 0.6rem 0 0; }
/* Scoped to list links only — an unscoped `.mobile-nav a` outranks `.btn`
   and would strip the CTA's centering, padding and pill shape. */
.mobile-nav ul a {
  display: block;
  padding: 0.85rem 0.25rem;
  font-family: var(--font-display);
  font-weight: 500;
  color: rgba(255, 255, 255, 0.82);
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.mobile-nav ul li:last-child a { border-bottom: 0; }
.mobile-nav .btn { margin-top: 1.25rem; }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  background: var(--navy-900);
  padding-block: 0;
  overflow: hidden;
  isolation: isolate;
}
/* full-bleed collage; an <img> rather than a CSS background so it can be a
   responsive, art-directed, high-priority LCP candidate. */
.hero__bg { position: absolute; inset: 0; z-index: -3; margin: 0; display: block; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; object-position: 100% 24%; }
/* Two stacked scrims. The photograph is composed with a shadowed wall on the
   left, so the horizontal scrim only has to deepen an already dark area rather
   than wipe a bright one — it stays light enough that the wall keeps its
   concrete texture. The vertical one darkens the top so the glass pill holds
   contrast, and the bottom so the trust cards separate from the table. */
.hero__scrim {
  position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(6, 15, 34, 0.72) 0%, rgba(6, 15, 34, 0.3) 20%,
      rgba(6, 15, 34, 0.1) 44%, rgba(6, 15, 34, 0.58) 78%, rgba(6, 15, 34, 0.93) 100%),
    linear-gradient(101deg, rgba(6, 15, 34, 0.8) 0%, rgba(6, 15, 34, 0.62) 28%,
      rgba(6, 15, 34, 0.22) 54%, rgba(6, 15, 34, 0) 74%);
}
.hero__inner {
  position: relative;
  min-height: min(94svh, 900px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-top: calc(var(--mast-h, 98px) + clamp(1.5rem, 4vw, 3rem));
  padding-bottom: clamp(1.8rem, 3.5vw, 3rem);
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(70% 60% at 50% 40%, #000 0%, transparent 78%);
  z-index: -1;
}
.hero__grid { display: block; }
.hero__figure {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 3 / 4.05;
  box-shadow: var(--shadow-float);
}
.hero__figure img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 14%; }
.hero__figure::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 27, 61, 0) 40%, rgba(10, 27, 61, 0.6) 100%);
}
.hero__figure--b { aspect-ratio: 3 / 4.05; }

.hero__copy { text-align: left; color: #fff; max-width: 41rem; }
.hero__pills {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.35rem 1rem;
  margin-bottom: 1.4rem;
  font-family: var(--font-display);
  font-size: 0.978rem;
  color: rgba(255, 255, 255, 0.72);
}
.hero__pills span {
  display: inline-flex; align-items: center; gap: 0;
  padding: 0.44rem 0.92rem;
  border-radius: 999px;
  font-size: 0.9495rem;
  letter-spacing: 0.01em;
  color: rgba(233, 240, 255, 0.96);
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.19);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}
.hero__pills span:not(:last-child)::after {
  content: "";
  width: 1px;
  height: 14px;
  background: rgba(255, 255, 255, 0.28);
}
.hero h1 {
  color: #fff;
  /* sentence case, not uppercase: the serif italic accent below only works in
     mixed case, and it matches the h1 treatment on every other page. */
  font-size: clamp(2.1rem, 1.05rem + 3.5vw, 3.95rem);
  line-height: 1.03;
  max-width: none;
  margin-inline: 0;
  text-transform: none;
  letter-spacing: -0.03em;
  margin-bottom: 0.58em;
  text-wrap: balance;
  text-shadow: 0 2px 34px rgba(3, 9, 24, 0.5);
}
.hero h1 em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  color: var(--blue-300);
  letter-spacing: -0.008em;
}
.hero__lede {
  max-width: 42ch;
  margin: 0 0 1.55rem;
  color: rgba(226, 235, 252, 0.87);
  font-size: clamp(1rem, 0.95rem + 0.22vw, 1.14rem);
}
.hero__checks {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 0.5rem 1.6rem;
  margin-bottom: 1.75rem;
  font-size: 1.011rem;
  color: var(--blue-300);
  font-family: var(--font-display);
  font-weight: 500;
}
.hero__checks li { list-style: none; display: inline-flex; align-items: center; gap: 0.45rem; }
.hero__checks svg { flex: none; }
.hero__checks ul, .hero__checks { padding: 0; margin-left: 0; }
.hero__actions { display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: center; gap: 0.8rem; }
.btn__arrow { flex: none; transition: transform 0.28s ease; }
.btn:hover .btn__arrow { transform: translateX(3px); }

/* Glass cards on the photograph. A bare divider row (the old treatment) has
   nothing to sit on once the background is a busy image. */
.hero__trust {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.7rem, 1.4vw, 1rem);
  margin-top: clamp(2rem, 4vw, 3.1rem);
  padding-top: 0;
  border-top: 0;
}
.trust {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1rem 1.15rem;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.075);
  border: 1px solid rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 18px 40px -28px rgba(0, 0, 0, 0.8);
  color: rgba(233, 240, 255, 0.86);
  font-size: 1.019rem;
  font-family: var(--font-display);
  line-height: 1.32;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .trust { background: rgba(10, 27, 61, 0.86); }
  .hero__pills span { background: rgba(10, 27, 61, 0.86); }
}
.trust__icon {
  width: 46px;
  height: 46px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
  color: var(--blue-300);
}
.trust strong { display: block; color: #fff; font-weight: 700; font-size: 1.09rem; margin-bottom: 2px; }
/* scale the inline 20x20 SVGs up with the badge without editing the markup */
.trust__icon svg { width: 24px; height: 24px; }

/* notch under hero */
.notch {
  position: relative;
  height: 0;
}
.notch::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -1px;
  transform: translateX(-50%);
  width: min(420px, 56%);
  height: 40px;
  background: var(--surface);
  clip-path: polygon(0 0, 100% 0, calc(100% - 42px) 100%, 42px 100%);
}

/* ---------- About / collage ---------- */
.split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
/* container-type makes the collage a size container so the overlay chips can be
   sized in cqw (percent of collage width) instead of fixed px. Before this the
   chips measured a constant 156x96 / 208x97 at every viewport from 320 to 1920
   while the collage itself scaled 283 -> 662px wide, so combined chip coverage
   of the photograph went from 8.9% at 1920 to 40.2% at 320. inline-size
   containment is safe here: the collage is a 1fr grid item, so its width never
   depended on its contents. */
.collage { position: relative; padding-bottom: 4.2rem; container-type: inline-size; }
.collage__main {
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  aspect-ratio: 4 / 3.35;
}
.collage__main img { width: 100%; height: 100%; object-fit: cover; }
.collage__inset {
  position: absolute;
  right: -6%;
  bottom: 0;
  width: 52%;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 6px solid #fff;
  box-shadow: var(--shadow-float);
  aspect-ratio: 4 / 3;
}
.collage__inset img { width: 100%; height: 100%; object-fit: cover; }

/* Every dimension below is `clamp(floor, Ncqw, ceiling)` where the ceiling is
   the previous fixed px value and N is that value expressed against the 662px
   collage it was designed at (156px = 23.56cqw, 208px = 31.42cqw, and so on).
   So desktop is pixel-identical to v3.5.18 and only narrow collages shrink.
   The floors are legibility limits, not proportional values -- type is not
   allowed below 0.72rem (11.52px) anywhere. */
.chip {
  position: absolute;
  display: flex;
  align-items: center;
  gap: clamp(0.42rem, 1.571cqw, 0.65rem);
  background: #fff;
  border-radius: var(--r-md);
  padding: clamp(0.5rem, 1.691cqw, 0.7rem) clamp(0.72rem, 2.417cqw, 1rem);
  box-shadow: var(--shadow-float);
  font-family: var(--font-display);
}
.chip strong { display: block; font-size: clamp(0.92rem, 2.538cqw, 1.05rem); color: var(--ink); line-height: 1.1; }
.chip small { font-size: clamp(0.72rem, 1.879cqw, 0.7776rem); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mute); }
.chip--years {
  top: 9%;
  right: -4%;
  width: clamp(108px, 23.56cqw, 156px);
  flex-direction: column;
  gap: 0.1rem;
  text-align: center;
  background: linear-gradient(180deg, var(--blue-500), var(--blue-600));
  padding: clamp(0.72rem, 2.417cqw, 1rem) clamp(0.72rem, 2.779cqw, 1.15rem);
  border-radius: var(--r-md);
}
.chip--years strong { color: #fff; font-size: clamp(1.15rem, 3.987cqw, 1.65rem); }
.chip--years small { color: rgba(255, 255, 255, 0.9); line-height: 1.32; letter-spacing: 0.07em; font-size: clamp(0.72rem, 1.932cqw, 0.7992rem); white-space: normal; }
/* The org-wide label ("Clients served by his team") is long enough to stretch this
   chip across half the collage on one line, so it is capped and allowed to wrap.

   Deliberately NOT scaled in cqw like .chip--years. The label needs a roughly
   fixed ~180px to reach three lines -- that requirement comes from the longest
   word plus tracking, not from the container -- so scaling the box
   proportionally starved it: at 31.42cqw the chip sat between 140 and 191px from
   320 all the way to 1280, and every one of those widths wrapped the five-word
   label to five lines, one word per line. Holding the design width keeps it at
   three lines everywhere it fits, and it only ever collided with
   .collage__inset below ~430px anyway (measured 55px at 320, 33 at 360, 17 at
   390, 4 at 414, and 0 from 480 up). */
.chip--clients { bottom: 1.1rem; left: -5%; max-width: 208px; }
.chip--clients small { white-space: normal; line-height: 1.32; }
/* The home-page collage photograph carries people across the whole top edge, so
   the years chip cannot sit in the top-right corner without covering a face —
   that corner was empty wall in the previous image. Here the two chips stack on
   the left instead, over the lower body area where there are no faces. */
.collage--why .chip--years {
  top: auto;
  right: auto;
  /* Clears the clients chip below it, which is now two label lines tall
     (measured 97-103px) rather than one. */
  bottom: 8.7rem;
  left: -5%;
}

/* Fixed, not cqw: the avatar belongs to .chip--clients, which holds its design
   width, so it holds its design size too. `flex: none` keeps the circle from
   being squeezed oval once the label wraps. */
.chip--clients img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex: none; }

/* Below a 430px collage the chip cannot keep both its design width and its
   clearance, so this is the one band where the chip's own styling gives way.
   A parameter sweep over width x avatar x padding x tracking found exactly one
   combination that holds the label to three lines while keeping the chip clear
   of the inset: 148px wide, 22px avatar, 0.5rem padding, 0.03em tracking --
   103px of text box and 5px of clearance at a 320px viewport. The tracking is
   the real sacrifice (0.14em is the label's design value), so the query is
   scoped as tightly as it can be and any collage 430px or wider is untouched.

   This block must stay BELOW `.chip--clients img` above. A container query adds
   no specificity, so with both selectors at one class plus one type the later
   rule wins -- when this lived above that rule the avatar stayed 34px, which
   cost 12px of text box and left the label at five lines, defeating the whole
   query. The band is not only phones: the two-column .split resumes around a
   960px viewport with a 418px collage, so that width lands here too. */
@container (max-width: 429px) {
  .chip--clients { max-width: 148px; padding: 0.5rem; }
  .chip--clients img { width: 22px; height: 22px; }
  .chip--clients small { letter-spacing: 0.03em; }
}

.stat-card {
  margin-top: 1.6rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: 0;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.stat-card__body { padding: 1.4rem 1.5rem; }
.stat-card__top { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 1rem; }
.stat-card__badge {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: linear-gradient(180deg, var(--blue-500), var(--blue-600));
  color: #fff;
  flex: none;
}
.stat-card__top strong { font-family: var(--font-display); font-size: 1.4rem; color: var(--ink); line-height: 1; display: block; }
.stat-card__top small { font-size: 0.7776rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mute); }
.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.ticks li { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 1.017rem; }
.ticks svg { flex: none; margin-top: 3px; color: var(--blue-500); }
.stat-card__media { min-height: 250px; }
.stat-card__media img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- Services ---------- */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}
/* Same 3+1 orphan as .pillars, in the band where a 290px auto-fit track
   resolves to three columns. Both places this grid is used -- the four pillars
   on About and the four links on 404 -- hold exactly four items. */
@media (min-width: 1000px) and (max-width: 1365px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.card {
  position: relative;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
  overflow: hidden;
}
.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(90deg, var(--blue-500), var(--blue-300));
  opacity: 0;
  transition: opacity 0.3s ease;
}
.card:hover { transform: translateY(-6px); box-shadow: var(--shadow-float); border-color: transparent; }
.card:hover::before { opacity: 1; }
.card__icon {
  width: 50px;
  height: 50px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  background: rgba(47, 107, 255, 0.1);
  color: var(--blue-600);
  margin-bottom: 1.1rem;
}
.card h3 { margin-bottom: 0.5rem; }
.card p { font-size: 1.051rem; color: var(--ink-mute); }
.card__list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.card__list li {
  position: relative;
  padding-left: 1.1rem;
  font-size: 1.034rem;
  color: var(--ink-soft);
}
.card__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--blue-400);
}

/* ---------- Pillars strip ---------- */
.pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
/* Four items in an auto-fit track resolve to three columns between 640 and
   820px, which leaves a single pillar alone on a second row. Forcing two
   columns across that band gives 2+2 instead. The window starts at 430px
   because below that the strip is intentionally a single column, and ends
   before 860px where all four fit on one row. */
@media (min-width: 430px) and (max-width: 859px) {
  .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.pillar { background: #fff; padding: 1.5rem 1.4rem; }
.pillar strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--ink);
  margin-bottom: 0.3rem;
}
.pillar p { font-size: 1.022rem; color: var(--ink-mute); }

/* ---------- CTA band (claims analog) ---------- */
.band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 0.92fr);
  align-items: center;
  gap: 0;
}
.band__photo {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 1 / 1.06;
  z-index: 2;
  box-shadow: var(--shadow-card);
}
.band__photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 8%; }
.band__glow {
  position: absolute;
  inset: -12% auto auto -12%;
  width: 70%;
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(47, 107, 255, 0.22), transparent 70%);
  border-radius: 50%;
  z-index: 0;
}
.band__body {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: clamp(1.6rem, 3vw, 2.4rem) clamp(1.6rem, 3vw, 2.6rem);
  padding-left: clamp(1.6rem, 3vw + 2.5rem, 4.6rem);
  margin-left: -2.5rem;
  box-shadow: var(--shadow-card);
  z-index: 1;
}
.band__body h2 { font-size: var(--step-2); margin-bottom: 0.5rem; }
.band__body p { font-size: 1.051rem; color: var(--ink-mute); }
.band__cta {
  background: linear-gradient(170deg, var(--blue-500), var(--blue-600));
  border-radius: var(--r-md);
  padding: clamp(1.8rem, 3vw, 2.4rem) 1.4rem;
  text-align: center;
  color: #fff;
  margin-left: -1.5rem;
  z-index: 2;
  box-shadow: 0 22px 45px -20px rgba(29, 78, 216, 0.75);
}
.band__cta .or { display: block; margin: 0.9rem 0 0.4rem; font-size: 0.871rem; letter-spacing: 0.16em; text-transform: uppercase; opacity: 0.75; }
.band__cta small { display: block; font-size: 0.8464rem; letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.8; margin-bottom: 0.2rem; }
.band__cta a.mail { color: #fff; font-family: var(--font-display); font-weight: 500; font-size: 1.04rem; border-bottom: 1px solid rgba(255,255,255,0.5); }

/* ---------- Process (dark) ---------- */
.process {
  position: relative;
  background: linear-gradient(150deg, var(--plum-900) 0%, #1f1b36 55%, var(--plum-800) 100%);
  color: rgba(255, 255, 255, 0.72);
  overflow: hidden;
}
.process::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 60% at 20% 10%, rgba(47, 107, 255, 0.2), transparent 60%);
}
.process .shell { position: relative; }
.process h2, .process h3 { color: #fff; }
.process .section-head p { color: rgba(255, 255, 255, 0.6); }
.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.2rem, 3vw, 2.4rem);
  position: relative;
}
.steps::before {
  content: "";
  position: absolute;
  top: 152px;
  left: 16%;
  right: 16%;
  border-top: 1px dashed rgba(255, 255, 255, 0.28);
}
.step { text-align: center; position: relative; }
.step__ring {
  width: clamp(148px, 17vw, 226px);
  height: clamp(148px, 17vw, 226px);
  margin: 0 auto;
  border-radius: 50%;
  padding: 9px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.05);
}
.step__ring img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.step__num {
  position: relative;
  z-index: 2;
  display: inline-grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin: 1rem 0 1rem;
  border-radius: 50%;
  background: linear-gradient(180deg, var(--blue-500), var(--blue-600));
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9984rem;
  box-shadow: 0 0 0 6px rgba(47, 107, 255, 0.16);
}
.step h3 { font-size: 1.02rem; margin-bottom: 0.4rem; }
.step p { font-size: 1.011rem; max-width: 30ch; margin-inline: auto; }

/* ---------- Testimonials ---------- */
.quotes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  gap: clamp(1rem, 2vw, 1.6rem);
}
@media (min-width: 760px) {
  .quotes { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1180px) {
  .quotes { grid-template-columns: repeat(4, 1fr); }
}
.quote {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 1.5rem 1.5rem 1.4rem;
  position: relative;
  box-shadow: var(--shadow-card);
}
.quote__top { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; margin-bottom: 0.9rem; }
.quote__mark { font-family: var(--font-display); font-size: 2.4rem; line-height: 0.7; color: rgba(47, 107, 255, 0.28); }
.stars { display: inline-flex; gap: 2px; color: var(--gold); }
.quote p { font-size: 1.017rem; color: var(--ink-mute); }
.quote__who { display: flex; align-items: center; gap: 0.7rem; margin-top: 1.2rem; padding-top: 1.1rem; border-top: 1px solid var(--line); }
.quote__who img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; }
.quote__who strong { display: block; font-family: var(--font-display); font-size: 1.04rem; color: var(--ink); }
.quote__who small { font-size: 0.7776rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mute); }

/* ---------- FAQ ---------- */
.faq { max-width: 820px; margin-inline: auto; display: grid; gap: 0.7rem; }
.faq details {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 0 1.3rem;
  transition: box-shadow 0.25s ease, border-color 0.25s ease;
}
.faq details[open] { box-shadow: var(--shadow-card); border-color: rgba(47, 107, 255, 0.32); }
.faq summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.15rem 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.096rem;
  color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex: none;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--ink-mute);
  border-bottom: 2px solid var(--ink-mute);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.28s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.faq .faq__a { padding: 0 0 1.25rem; font-size: 1.04rem; color: var(--ink-mute); max-width: 68ch; }
/* Inline prose link. The base reset is `a { color: inherit; text-decoration:
   none }`, which is right for nav items, cards and buttons — every one of them
   carries its own affordance — but inside a paragraph it leaves the link
   indistinguishable from the surrounding text, so it needs both colour and an
   underline restored explicitly. Underline rather than colour alone: colour is
   the one cue that does not survive greyscale or the common forms of colour
   blindness (WCAG 1.4.1). */
.faq .faq__a a {
  color: var(--blue-600);
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px; border-radius: 3px;
  transition: color 0.2s ease, text-decoration-thickness 0.2s ease;
}
.faq .faq__a a:hover { color: var(--blue-500); text-decoration-thickness: 2px; }
.faq .faq__a a:focus-visible { outline: 2px solid var(--blue-500); outline-offset: 3px; }

/* ---------- Two tracks + scheduler (contact) --------------------------------
   The page offers booking and messaging as a deliberate choice rather than a
   scheduler bolted above a form. Track 01 carries the navy weight because
   booking is the outcome the page wants; track 02 is an equal-size card in
   plain surface so it reads as a real alternative and not a consolation.
--------------------------------------------------------------------------- */
.tracks__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.1rem, 2.4vw, 1.9rem);
  margin-top: clamp(2rem, 4vw, 3.2rem);
  align-items: stretch;
}
.track {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(1.5rem, 2.6vw, 2.35rem);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}
.track__num {
  font-family: var(--font-display);
  font-size: 0.8688rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--blue-600);
  opacity: 0.72;
}
.track h3 {
  font-size: var(--step-2);
  line-height: 1.14;
  letter-spacing: -0.018em;
  margin: 0.5rem 0 0.6rem;
}
.track > p { color: var(--ink-soft); margin: 0; font-size: 1.04rem; }
.track__facts {
  list-style: none;
  /* The bottom margin is the floor for the gap above the CTA; the auto margin
     on .btn below supplies whatever extra is needed to bottom-align the two
     cards' buttons, and collapses to nothing when the card has no slack. */
  margin: 1.35rem 0 1.6rem;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0.75rem;
}
.track__facts li { display: grid; gap: 0.1rem; }
.track__facts strong {
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--ink);
}
.track__facts span { font-size: 0.9558rem; color: var(--ink-soft); }
/* The button is pinned to the bottom so the two cards' CTAs align on one line
   regardless of how the paragraphs above them wrap. Nothing may set margin-top
   on .track .btn after this — an equal-specificity rule silently wins and the
   two CTAs drift apart by whatever the paragraph lengths differ by. */
.track .btn { margin-top: auto; }
.track .btn svg { flex: none; }
.track__note {
  margin: 0.85rem 0 0;
  font-size: 0.9231rem;
  color: var(--ink-mute);
  text-align: center;
  /* Reserve one line so a wrap at the narrow end of the two-up range cannot
     push one card's button out of line with the other's. */
  min-height: 1.4em;
}

/* Card 01 wears the same frosted-glass shell as the consultation panel and the
   address card, so the two treatments on this page are one idea rather than
   two. It does not borrow the `.glass` class itself: `.glass` expects a
   `.glass__inner` wrapper carrying its own padding, and `.track` already owns
   the padding and — critically — the flex column whose `margin-top: auto` on
   the button is what keeps both cards' CTAs on the same baseline. Wrapping the
   children in another box would move that flex context and break the
   alignment. So the layers are rebuilt here on the article itself and the
   layout is left completely alone.

   `overflow: clip` rather than `hidden`, per the scheduler panel: clip rounds
   the photo to the border radius identically without making the card a scroll
   container. */
.track--book {
  isolation: isolate;
  overflow: hidden;
  overflow: clip;
  background: linear-gradient(160deg, var(--navy-800), var(--navy-700));
  border-color: rgba(255, 255, 255, 0.16);
  box-shadow: 0 40px 90px -40px rgba(3, 9, 24, 0.8);
}
.track__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* This card is much narrower and taller than the wide panels that share the
     photograph, so the crop is pulled left and down off `.glass__bg`'s
     62% 46% — that framing put the brightest part of the frame directly behind
     the middle of every line of text. */
  object-position: 34% 62%;
}
/* the frost. The first gradient is a scrim that holds the text column on deep
   navy; it runs further across than `.glass::before`'s (opaque to 62%, clear at
   96% rather than 54%/82%) because text here spans nearly the full card width
   instead of sitting in a left column. */
.track--book::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  -webkit-backdrop-filter: blur(7px) saturate(180%);
  backdrop-filter: blur(7px) saturate(180%);
  background:
    linear-gradient(100deg, rgba(5, 16, 40, 0.78) 0%, rgba(5, 16, 40, 0.55) 62%,
      rgba(5, 16, 40, 0.12) 96%),
    linear-gradient(158deg,
      rgba(7, 21, 52, 0.62) 0%,
      rgba(12, 35, 82, 0.44) 42%,
      rgba(23, 74, 190, 0.26) 100%);
}
/* specular top edge + hairline, so it reads as glass rather than a flat tint */
.track--book::after {
  content: ''; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 -60px 80px -60px rgba(3, 9, 24, 0.55);
}
/* content above the frost, below the specular edge */
.track--book > *:not(.track__bg) { position: relative; z-index: 2; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .track--book::before {
    background: linear-gradient(158deg, rgba(7, 21, 52, 0.92), rgba(13, 38, 88, 0.86) 50%,
      rgba(23, 74, 190, 0.78));
  }
}
.track--book .track__num { color: var(--blue-300); opacity: 1; }
.track--book h3 { color: #fff; }
.track--book > p { color: rgba(219, 228, 252, 0.9); }
.track--book .track__facts { border-top-color: rgba(255, 255, 255, 0.14); }
.track--book .track__facts strong { color: #fff; }
.track--book .track__facts span { color: rgba(219, 228, 252, 0.72); }
.track--book .track__note { color: rgba(219, 228, 252, 0.62); }

/* ---- the scheduler frame ---- */
.cal {
  margin-top: clamp(1.6rem, 3vw, 2.6rem);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}
.cal[hidden] { display: none; }
.cal__head {
  padding: clamp(1.25rem, 2.4vw, 1.8rem) clamp(1.25rem, 2.4vw, 2rem) 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}
.cal__head-copy { min-width: 0; }
.cal__head h3 { font-size: var(--step-2); letter-spacing: -0.018em; margin: 0 0 0.35rem; }
.cal__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-display);
  font-size: 0.9558rem;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: 999px;
  /* 44px minimum touch target: this is the control a phone user reaches for. */
  min-height: 44px;
  padding: 0 1.05rem;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}
.cal__close:hover { color: var(--ink); border-color: var(--ink-mute); background: #fff; }
.cal__close:focus-visible { outline: 2px solid var(--blue-500); outline-offset: 2px; }
.cal__head p { margin: 0; color: var(--ink-soft); font-size: 1.007rem; }
.cal__head strong { color: var(--ink); }
.cal__frame { position: relative; padding: clamp(0.6rem, 1.6vw, 1rem); }
/* This floor is load-bearing and must never be released. Calendly's frame is
   width:100% height:100% of the element the widget sizes, and the height it
   reports by postMessage is measured inside that frame — so if the floor is
   removed the two feed back on each other and the calendar collapses. Measured
   directly: dropping the floor once the frame went live drove the reported
   height to 2px. With the floor in place the report is stable, 687px at
   desktop and 602px on a phone, and the widget's inline height still governs
   whenever it exceeds the floor. */
.cal__frame { min-height: 700px; }
@media (max-width: 560px) { .cal__frame { min-height: 600px; } }
/* Calendly's own iframe is width:100% height:100%; data-resize makes it report
   its height by postMessage, which the widget writes onto this element as an
   inline style — so no height is hard-coded per breakpoint here. min-height
   only holds the panel open while the frame is still loading. */
/* Calendly centres a fixed-width column inside whatever width it is given, so
   at full shell width the calendar sits alone in ~900px of empty white. Capping
   the frame lets its column fill the panel instead. */
.cal .calendly-inline-widget { width: 100%; max-width: 760px; margin-inline: auto; }
.cal__load {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 0.9rem;
  font-size: 0.9885rem;
  color: var(--ink-soft);
  background: var(--surface);
  pointer-events: none;
}
.cal__load[hidden] { display: none; }
.cal__spin {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2.5px solid var(--line);
  border-top-color: var(--blue-600);
  animation: calspin 0.75s linear infinite;
}
@keyframes calspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cal__spin { animation-duration: 2.4s; } }

.cal__fallback, .cal__done { padding: clamp(1.5rem, 3vw, 2.4rem); text-align: center; }
.cal__fallback[hidden], .cal__done[hidden] { display: none; }
.cal__fallback p { margin: 0 0 1.1rem; color: var(--ink-soft); }
.cal__fallback strong { color: var(--ink); }
/* A completed booking retires the live calendar, so a second accidental
   booking is not one stray click away. */
.cal.is-done .cal__head, .cal.is-done .cal__frame { display: none; }

/* On a phone the calendar is taller than the viewport, so a Close button that
   only lives at the top of the panel scrolls out of reach exactly when it is
   needed. The head sticks below the fixed masthead instead.
   `.cal` must use `overflow: clip` rather than `hidden` for this to work at
   all — `hidden` makes .cal a scroll container, which silently kills sticky
   inside it. `clip` clips identically without creating one. The `hidden`
   declaration above stays as the fallback for engines without `clip`: they
   lose the stickiness, not the layout. */
.cal { overflow: clip; }
/* Phones: while the booking panel is open the masthead stops being fixed.
   A fixed element is anchored to the LAYOUT viewport, so when the iOS software
   keyboard opens over Calendly's form the pill detaches from the top of the
   screen and drifts across the iframe. Absolute parks it at the top of the
   document, out of the way, and the base `position: fixed` returns as soon as
   JS drops .cal-open. Specificity here (2 classes + 1 type) already beats the
   base rule, so this does not depend on source order.
   NOTE: the masthead keeps a real box either way, which is what keeps the
   --mast-h measurement in main.js honest -- do not switch this to display:none,
   or a keyboard-driven resize would measure 0 and collapse the page padding. */
@media (max-width: 860px) {
  html.cal-open .masthead { position: absolute; }
  /* The panel header sticks below the masthead; with the masthead no longer
     fixed there is nothing to clear, so reclaim that reserved band instead of
     leaving an ~80px gap above the sticky header. */
  html.cal-open .cal__head { top: 8px; }
}
.cal__head {
  position: sticky;
  top: calc(var(--mast-h, 98px) + 8px);
  z-index: 3;
  background: var(--surface);
  padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--line);
}
.cal__check {
  display: inline-grid;
  place-items: center;
  width: 58px;
  height: 58px;
  margin-bottom: 1rem;
  border-radius: 50%;
  color: #fff;
  background: linear-gradient(150deg, var(--blue-500), var(--blue-600));
  box-shadow: 0 16px 34px -16px rgba(29, 78, 216, 0.7);
}
.cal__done h3 { font-size: var(--step-3); letter-spacing: -0.02em; margin: 0 0 0.6rem; }
.cal__done p { margin: 0 auto 1.4rem; color: var(--ink-soft); max-width: 52ch; }

@media (max-width: 860px) {
  .tracks__grid { grid-template-columns: minmax(0, 1fr); }
  /* Booking stays first on phones: it is the shorter path, and the message
     track sits directly above the form it scrolls to. */
  .cal__frame { padding: 0.4rem; }
}
@media (max-width: 560px) {
  .track { padding: 1.35rem 1.2rem; border-radius: var(--r-md); }
  .cal { border-radius: var(--r-md); }
  /* The head is sticky, so every line in it is charged against the calendar
     for the whole scroll. Measured at 390x844 the full head held the title
     over two lines and the note over four, 170px of a 844px viewport. The
     note goes: the booking card above already states "1 hour / Full
     consultation", and Calendly renders its own timezone control inside the
     frame, so nothing here is only said once. */
  .cal__head { padding: 0.95rem 1.15rem; align-items: center; }
  .cal__head p { display: none; }
  .cal__head h3 { font-size: var(--step-1); margin: 0; }
  /* 44px, not 40: the compact head trades vertical space for reach, but 40
     sits under the 44px minimum touch target. The 4px costs ~4px of head
     height and buys a control a thumb can actually hit. */
  .cal__close { min-height: 44px; padding: 0 0.85rem; }
}

/* ---------- Contact ---------- */
/* `reveal--right` holds its element at translateX(34px) until the observer
   fires. Between 780px and roughly 860px the shell is wide enough that those
   34px reach past the viewport edge, so `documentElement.scrollWidth` reports
   1-3px more than the viewport until the reveal runs — 783 against 780 at
   780px, 821 against 820 at 820px, clean at every other width, measured
   without forcing reveals (forcing them is exactly what hides this).
   `body` already carries `overflow-x: hidden`, so this was never a visible
   sideways scroll; the clip is here to keep the measurement honest and to stop
   a future change to that body rule from turning it into one. Applied to the
   two sections that hold a `reveal--right`: `#why` on the home page and
   `#write` here. `clip` rather than `hidden`, so no scroll container is
   created and sticky children still work. */
.clip-x { overflow-x: clip; }
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.6rem, 4vw, 3.4rem);
  align-items: start;
}
/* ---------- Reusable frosted glass ------------------------------------------
   A photo sits behind a bluish translucent pane. `isolation: isolate` confines
   the backdrop-filter to the element so it blurs only the photo below it and
   nothing else on the page. Shared by .contact__panel and .map__card.
   Markup: <div class="glass"><img class="glass__bg"><div class="glass__inner">
--------------------------------------------------------------------------- */
.glass {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid rgba(255, 255, 255, 0.16);
  /* opaque floor so the panel still reads if the image 404s */
  background: linear-gradient(160deg, var(--navy-800), var(--navy-700));
  color: rgba(255, 255, 255, 0.86);
  box-shadow: 0 40px 90px -40px rgba(3, 9, 24, 0.8);
}
.glass__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 62% 46%;
}
/* the frost itself */
.glass::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  -webkit-backdrop-filter: blur(7px) saturate(180%);
  backdrop-filter: blur(7px) saturate(180%);
  background:
    /* scrim keeps the text column on deep navy for contrast */
    linear-gradient(100deg, rgba(5, 16, 40, 0.62) 0%, rgba(5, 16, 40, 0.28) 54%, rgba(5, 16, 40, 0) 82%),
    linear-gradient(158deg,
      rgba(7, 21, 52, 0.62) 0%,
      rgba(12, 35, 82, 0.44) 42%,
      rgba(23, 74, 190, 0.26) 100%);
}
/* specular top edge + hairline, so it reads as glass rather than a flat tint */
.glass::after {
  content: ''; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 -60px 80px -60px rgba(3, 9, 24, 0.55);
}
.glass__inner { position: relative; z-index: 2; padding: clamp(1.7rem, 3vw, 2.7rem); }
/* icon chips pick up the same translucent glass */
.glass .trust__icon {
  background: rgba(255, 255, 255, 0.15);
  border-color: rgba(255, 255, 255, 0.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: #fff;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* no backdrop-filter: lean on a heavier tint so text stays legible */
  .glass::before {
    background: linear-gradient(158deg, rgba(7, 21, 52, 0.90), rgba(13, 38, 88, 0.84) 50%, rgba(23, 74, 190, 0.76));
  }
}

/* consultation panel: type on the shared glass shell */
.contact__panel h2 { color: #fff; font-size: var(--step-2); text-shadow: 0 1px 18px rgba(3, 9, 24, 0.45); }
.contact__panel > .glass__inner > p { color: rgba(255, 255, 255, 0.84); }
.contact__panel .contact__list li { color: rgba(255, 255, 255, 0.95); }

.contact__list { list-style: none; margin: 1.6rem 0 0; padding: 0; display: grid; gap: 1rem; }
.contact__list li { display: flex; gap: 0.85rem; align-items: center; font-size: 1.04rem; }
.contact__list .trust__icon { width: 40px; height: 40px; }
.contact__list a { color: #fff; }

.form { display: grid; gap: 0.9rem; }
.field { display: grid; gap: 0.4rem; }
.field label {
  font-family: var(--font-display);
  font-size: 0.9062rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.field input, .field select, .field textarea {
  font: inherit;
  font-size: 1.073rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.8rem 0.95rem;
  width: 100%;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.field textarea { min-height: 118px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--blue-500);
  box-shadow: 0 0 0 4px rgba(47, 107, 255, 0.14);
}
.form__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.9rem; }
.form__note { font-size: 0.9062rem; color: var(--ink-mute); }

/* ---------- Footer ---------- */
.footer > .shell { max-width: var(--maxw-wide); }
/* navy-900, not plum-800. Every other cool token in this palette sits in a
   218-224 degree hue band at 70-100% saturation; plum-800 sat at 252 degrees
   and 30% saturation, so it read as faded navy rather than as a second brand
   colour. It also measured worst on every footer contrast pair and was the
   direct cause of the footer CTA failing the 3:1 UI-component minimum.
   navy-900 additionally matches the `html` canvas, so iOS overscroll and the
   safe-area insets paint the same colour as the footer. */
.footer {
  background: var(--navy-900);
  color: rgba(255, 255, 255, 0.62);
  padding-block: clamp(2.6rem, 5vw, 4rem) 1.6rem;
}
.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(2, minmax(0, 0.8fr)) minmax(0, 1fr);
  gap: clamp(1.6rem, 3vw, 3rem);
}
.footer .brand__name { font-size: 1.02rem; }
.footer p { font-size: 0.9984rem; margin-top: 1rem; }
.footer h4 {
  color: #fff;
  font-size: 0.9537rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; font-size: 1.022rem; }
.footer a:hover { color: #fff; }
.socials { display: flex; gap: 0.5rem; margin-top: 1.3rem; }
.socials a {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.09);
  color: #fff;
  transition: background 0.2s ease, transform 0.2s ease;
}
.socials a:hover { background: var(--blue-500); transform: translateY(-2px); }
.footer__bar {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: 1.3rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.4rem;
  justify-content: space-between;
  font-size: 0.9062rem;
}
.footer__legal { font-size: 0.871rem; line-height: 1.6; opacity: 0.72; max-width: 100%; margin-top: 1.2rem; }
.footer__ver {
  font-family: var(--font-display);
  font-size: 0.8346rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 2px 10px;
}

/* ---------- Reveal ---------- */
.reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.78s ease, transform 0.78s cubic-bezier(0.2, 0.78, 0.22, 1);
  transition-delay: var(--rv-d, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }

/* directional + scale variants */
.reveal--left  { transform: translateX(-34px); }
.reveal--right { transform: translateX(34px); }
.reveal--zoom  { transform: translateY(18px) scale(0.965); }
.reveal--fade  { transform: none; }
.reveal--left.is-in, .reveal--right.is-in, .reveal--zoom.is-in { transform: none; }
/* Horizontal slide-ins would widen the scroll box on narrow screens */
@media (max-width: 760px) {
  .reveal--left  { transform: translateY(26px); }
  .reveal--right { transform: translateY(26px); }
}

/* auto-stagger for grids and lists of revealed children */
.reveal-stagger > * { opacity: 0; transform: translateY(24px); transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.78, 0.22, 1); }
.reveal-stagger.is-in > * { opacity: 1; transform: none; }
.reveal-stagger.is-in > *:nth-child(1) { transition-delay: 40ms; }
.reveal-stagger.is-in > *:nth-child(2) { transition-delay: 110ms; }
.reveal-stagger.is-in > *:nth-child(3) { transition-delay: 180ms; }
.reveal-stagger.is-in > *:nth-child(4) { transition-delay: 250ms; }
.reveal-stagger.is-in > *:nth-child(5) { transition-delay: 320ms; }
.reveal-stagger.is-in > *:nth-child(6) { transition-delay: 390ms; }

/* per-line heading reveal */
.rv-line { display: block; overflow: hidden; }
.rv-line > span {
  display: block;
  transform: translateY(105%);
  transition: transform 0.9s cubic-bezier(0.2, 0.8, 0.2, 1);
  transition-delay: var(--rv-d, 0ms);
}
.is-in > .rv-line > span, .rv-line.is-in > span { transform: none; }



/* ---------- South Bay Coders credit line -------------------------------------
   Ported from the footer of jadabutler.com so the attribution is identical
   across South Bay Coders builds: same wording, same mono/uppercase treatment,
   same #D9A441 gold link, same #E06A55 heart, same beat keyframes.
   .nb keeps "love (heart) by" from splitting across a line break, and the link
   is nowrap so the company name never breaks mid-name when the line wraps.
--------------------------------------------------------------------------- */
.footer .credit {
  margin: 30px auto 0;
  text-align: center;
  text-wrap: balance;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.88px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* 0.48, the one deviation from the jadabutler.com port above. At the ported
     0.4 this line composited to 3.79:1 on navy-900, under the 4.5:1 required at
     its 11.88px size (and 10.26px on phones). 0.48 gives 4.97:1 while staying
     clearly quieter than footer body copy at 0.62 / 7.63:1. Wording, mono
     treatment, gold link and heart are unchanged. */
  color: rgba(255, 255, 255, 0.48);
}
.credit .nb { white-space: nowrap; }
.credit .heart {
  color: #e06a55;
  font-size: 12.84px;
  letter-spacing: 0;
  display: inline-block;
  animation: beat 2.6s ease-in-out infinite;
}
.credit a {
  white-space: nowrap;
  color: #d9a441;
  text-decoration: none;
  box-shadow: inset 0 -1px 0 rgba(217, 164, 65, 0);
  transition: box-shadow 0.3s ease, color 0.25s ease;
}
.credit a:hover, .credit a:focus-visible { box-shadow: inset 0 -1px 0 #d9a441; }
@keyframes beat {
  0%, 100% { transform: scale(1); }
  14% { transform: scale(1.28); }
  28% { transform: scale(1); }
  42% { transform: scale(1.18); }
  56% { transform: scale(1); }
}

@media (max-width: 560px) {
  /* Was 10.26px, the smallest type anywhere on the site. Raised to 11.5px so no
     rendered text falls below that floor. The tracking absorbs the extra width
     so the line still wraps to two balanced rows rather than three -- 0.16em at
     11.5px is wider than the old 0.1em at 10.26px was. Contrast is unaffected:
     the 0.48 alpha composites to 4.97:1, which clears 4.5:1 at any size. */
  .footer .credit { margin-top: 24px; font-size: 11.5px; letter-spacing: 0.07em; line-height: 1.95; }
}

/* Pre-reveal transforms must not widen the document. `.collage` carries
   .reveal--right, so before it reveals it sits 34px right of its resting
   position, pushing the absolutely-positioned .collage__inset past the viewport
   edge and producing a transient horizontal scrollbar on About. `clip` rather
   than `hidden` because clip does not create a scroll container, so the sticky
   masthead keeps working. Browsers without clip support fall back to today's
   behaviour, so this cannot regress.

   No `overflow-clip-margin` here, deliberately. Both chips overhang this box
   (left: -5%, right: -4%) by a measured 32px at 1440, so an axis clip looks
   like it should shear them off -- but paired with `overflow-y: visible`
   Chromium never establishes the clip, and the chips paint in full. Verified by
   pixel-diffing this render against the same page with
   `overflow-clip-margin: 0`: zero differing pixels at both 1440 and 390. A clip
   margin here would be dead code. (An earlier note in this file claimed the
   chips were being sliced 32px; that came from `element.screenshot()`, which
   crops to the element's own border box and therefore hides any overhang by
   construction. It was a measurement artifact, not a rendering bug.) */
.collage { overflow-x: clip; }

/* ---------- Hero entrance (load-time, one shot) -----------------------------
   The scroll .reveal system cannot do this job: the hero is already inside the
   viewport at load, so its IntersectionObserver fires for every child at once
   and you get one simultaneous pop instead of a sequence. This runs off load +
   document.fonts.ready instead.

   The hidden starting state is scoped to .js-enter, which an inline <head>
   script sets. If JS never runs, that class is never added and the hero renders
   normally rather than staying invisible forever.
--------------------------------------------------------------------------- */
.js-enter .enter {
  opacity: 0;
  transform: translateY(var(--enter-y, 18px));
  will-change: opacity, transform;
  transition:
    opacity   var(--enter-t, 0.8s) cubic-bezier(0.2, 0.78, 0.22, 1) var(--enter-d, 0ms),
    transform var(--enter-t, 0.8s) cubic-bezier(0.2, 0.78, 0.22, 1) var(--enter-d, 0ms);
}
.js-enter .enter-on .enter { opacity: 1; transform: none; }

/* Photos settle out of a slight scale instead of sliding, so they do not
   compete with the copy. The scale is on the <img>, not the <figure>: both
   figures are overflow:hidden, so the scaled image is clipped and cannot widen
   the document. Scaling the figure itself would risk a horizontal scrollbar. */
.js-enter .enter-img img {
  transform: scale(1.045);
  transition: transform 1.15s cubic-bezier(0.2, 0.78, 0.22, 1) var(--enter-d, 0ms);
}
.js-enter .enter-on .enter-img img { transform: none; }

/* homepage hero, in reading order */
.hero__pills     { --enter-d:   0ms; --enter-y: 14px; }
.hero h1         { --enter-d: 120ms; --enter-y: 20px; }
.hero__lede      { --enter-d: 300ms; --enter-y: 18px; }
.hero__checks    { --enter-d: 420ms; --enter-y: 16px; }
.hero__actions   { --enter-d: 520ms; --enter-y: 16px; }
.hero__figure    { --enter-d: 180ms; --enter-y: 22px; --enter-t: 1.15s; }
.hero__figure--b { --enter-d: 300ms; }
.hero__trust     { --enter-d: 700ms; --enter-y: 14px; }

/* about + contact page headers, same cadence */
.crumbs             { --enter-d:   0ms; --enter-y: 12px; }
.page-hero h1       { --enter-d: 120ms; --enter-y: 20px; }
.page-hero__lede    { --enter-d: 300ms; --enter-y: 18px; }
.page-hero__chips   { --enter-d: 440ms; --enter-y: 16px; }
.page-hero__actions { --enter-d: 440ms; --enter-y: 16px; }
.page-hero__figure  { --enter-d: 200ms; --enter-y: 22px; --enter-t: 1.15s; }

/* section-level entrance for full bands */
.section, .band, .quotes, .steps { will-change: auto; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
  .reveal, .reveal-stagger > * { opacity: 1; transform: none; }
  .rv-line > span { transform: none; }
  .js-enter .enter { opacity: 1; transform: none; }
  .js-enter .enter-img img { transform: none; }
  .credit .heart { animation: none; }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/* Tablet landscape and below */
@media (max-width: 1024px) {
  /* Three content-sized items no longer fit on one row. Stack them, but as a
     grid with a single max-content track and justify-content:center rather than
     a max-content flex box: the track centres while the container stays the full
     shell width, so the border-top still spans the shell as a divider and the
     icons stay column-aligned instead of wrapping 2 + 1. */
  .hero__trust {
    display: grid;
    grid-template-columns: minmax(0, max-content);
    justify-content: center;
    gap: 1.2rem;
  }
  .nav { display: none; }
  .burger { display: grid; }
  .masthead__cta .btn { display: none; }

  .hero__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero__copy { grid-column: 1 / -1; order: -1; }
  .hero__figure { aspect-ratio: 4 / 3.15; }

  .band { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem; }
  .band__photo { max-width: 320px; }
  .band__body { margin-left: 0; padding-left: clamp(1.6rem, 3vw, 2.6rem); }
  .band__cta { margin-left: 0; grid-column: 1 / -1; }

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

/* Tablet portrait */
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; gap: clamp(2.5rem, 8vw, 4rem); }
  /* `width: 100%` is load-bearing, not redundant. A grid item stretches to its
     track by default, but `margin-inline: auto` cancels that stretch and hands
     the item a content-based width -- and .collage is a size container, so
     `contain: inline-size` means its contents contribute nothing and the width
     resolved to 0, collapsing both chips to their clamp floors at every width
     under 900px. An explicit 100% restores a definite width so max-width and
     the auto margins behave exactly as they did before containment. */
  .collage { width: 100%; max-width: 560px; margin-inline: auto; }
  .contact { grid-template-columns: 1fr; }
  .steps { grid-template-columns: 1fr; gap: 2.4rem; }
  .steps::before { display: none; }
  .step__num { margin-block: 0.9rem 0.7rem; }
  .stat-card { grid-template-columns: 1fr; }
  .stat-card__media { min-height: 210px; order: -1; }
}

/* Mobile */
@media (max-width: 640px) {
  .hero__grid { grid-template-columns: 1fr; }
  .hero__figure--b { display: none; }
  .hero__figure { aspect-ratio: 4 / 3.4; max-height: 380px; }
  .hero__trust { gap: 1.1rem; }
  .hero__pills { font-size: 0.8945rem; gap: 0.3rem 0.7rem; }
  .hero__pills span:not(:last-child)::after { display: none; }

  .band { grid-template-columns: 1fr; }
  .band__photo { max-width: 100%; aspect-ratio: 16 / 11; }

  .collage { padding-bottom: 4.5rem; }
  .collage__inset { right: 0; width: 46%; }
  .chip--years { right: 0; top: 8%; }
  .chip--clients { left: 0; }

  .form__row { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  .notch::before { width: 70%; }
}

@media (max-width: 380px) {
  .brand__mark { width: 38px; height: 38px; }
  .brand__name { font-size: 1.073rem; }
}

/* ==========================================================================
   Services coverflow carousel  (3D cover-flow, autoplay, drag/swipe/keys)
   ========================================================================== */
.svc-top {
  display: flex; flex-wrap: wrap; gap: 16px 40px;
  align-items: flex-end; justify-content: space-between;
  margin-bottom: clamp(26px, 3vw, 40px);
}
.svc-top .section-head { margin-bottom: 0; max-width: 62ch; text-align: left; }
.svc-top .section-head p { margin-inline: 0; }
.svc-top .eyebrow { justify-content: flex-start; }
.svc-note {
  font-size: 1.04rem; color: var(--ink-mute); max-width: 30ch;
  padding-left: 18px; border-left: 2px solid var(--line); line-height: 1.6;
}

.cf {
  position: relative; user-select: none;
  --cf-w: 356px; --cf-h: 534px;
  /* Vertical panning stays with the browser so a downward swipe over the
     carousel scrolls the page; horizontal panning is handled in JS. */
  touch-action: pan-y;
  cursor: grab;
}
.cf.is-dragging { cursor: grabbing; }
.cf.is-dragging .cf-go { pointer-events: none; }
.cf-stage {
  perspective: 1750px; perspective-origin: 50% 46%;
  overflow: hidden; padding: 14px 0 18px;
  margin-inline: -6px;
}
.cf-track { position: relative; height: var(--cf-h); transform-style: preserve-3d; }
.cf-card {
  position: absolute; top: 0; left: 50%;
  width: var(--cf-w); height: var(--cf-h); margin-left: calc(var(--cf-w) / -2);
  border-radius: 24px; overflow: hidden; background: var(--navy-900); cursor: pointer;
  box-shadow: 0 22px 54px rgba(6, 15, 34, 0.26);
  /* No transform/opacity transition: JS writes both every frame from the rAF
     spring. A CSS transition here would lag one frame behind and fight it. */
  transition: box-shadow 0.5s ease;
  will-change: transform; backface-visibility: hidden;
}
.cf-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.cf-card .scrim {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(5, 11, 28, 0.06) 0%, rgba(5, 11, 28, 0.44) 44%, rgba(5, 11, 28, 0.94) 100%);
}
.cf-card .dim {
  position: absolute; inset: 0; pointer-events: none;
  background: #050b1c; opacity: var(--dim, 0);   /* driven per frame by JS */
}
.cf-card.is-active { box-shadow: 0 34px 84px rgba(6, 15, 34, 0.44); }
.cf-body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 24px 22px; color: #fff; }
.cf-body h3 { color: #fff; font-size: 1.24rem; letter-spacing: -0.022em; margin-bottom: 8px; }
.cf-body p { color: rgba(219, 228, 252, 0.88); font-size: 1.02rem; line-height: 1.55; }
.cf-list {
  list-style: none; margin: 12px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.cf-list li {
  font-size: 0.82rem; font-weight: 500; letter-spacing: 0.01em;
  color: rgba(226, 235, 255, 0.94);
  background: rgba(255, 255, 255, 0.13);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px; padding: 4px 10px;
  backdrop-filter: blur(3px);
}
.cf-go {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
  font-family: var(--font-display); font-size: 0.96rem; font-weight: 700;
  color: var(--blue-600); background: #fff; border-radius: 8px; padding: 10px 17px;
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.4s ease, transform 0.4s ease, box-shadow 0.2s ease;
  pointer-events: none;
}
.cf-card.is-active .cf-go { opacity: 1; transform: none; pointer-events: auto; }
.cf-go:hover { box-shadow: 0 8px 22px rgba(255, 255, 255, 0.3); }
.cf-go svg { width: 14px; height: 14px; transition: transform 0.2s ease; }
.cf-go:hover svg { transform: translateX(3px); }
.cf-tag {
  position: absolute; top: 20px; left: 20px; z-index: 3;
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--navy-900);
  background: linear-gradient(96deg, #9fd0ff, #d8c48f);
  border-radius: 999px; padding: 5px 11px;
}
.cf-ui { display: flex; align-items: center; justify-content: center; gap: 20px; margin-top: clamp(18px, 2.2vw, 28px); }
.cf-arrow {
  width: 50px; height: 50px; border-radius: 50%; border: 1px solid var(--line);
  background: #fff; color: var(--navy-800);
  display: grid; place-items: center; cursor: pointer; transition: all 0.2s ease; flex: none;
}
.cf-arrow:hover { border-color: var(--blue-500); color: var(--blue-600); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.cf-arrow svg { width: 20px; height: 20px; }
/* The dots are an 8x8 visual and were also an 8x8 hit target, which is far
   under any touch minimum. The visual is held pixel-identical to the South Bay
   Coders carousel -- 8px dot, 9px apparent gap -- and only the hit area grows:
   padding inflates each button's box to 17x24 while `background-clip:
   content-box` keeps the paint confined to the original 8x8 content box. The
   9px gap is rebuilt out of the 4.5px inline padding on adjacent buttons, and
   the negative inline margin on the row cancels the leading and trailing
   padding so the row's visual width is unchanged too.

   Note the longhands: `background: <colour>` would reset background-clip back
   to border-box and paint the whole 17x24 box, so base colour and active
   gradient must be set as background-color / background-image. */
.cf-dots { display: flex; align-items: center; gap: 0; margin-inline: -4.5px; }
.cf-dot {
  width: 8px; height: 8px; border-radius: 999px; border: 0; cursor: pointer;
  box-sizing: content-box; padding: 8px 4.5px; background-clip: content-box;
  background-color: #cbd5ec;
  transition: width 0.34s cubic-bezier(0.2, 0.8, 0.2, 1), background-color 0.3s ease;
}
.cf-dot.on { width: 30px; background-image: linear-gradient(96deg, var(--blue-500), var(--blue-600)); }
.cf-hint { text-align: center; margin-top: 14px; font-size: 0.95rem; color: var(--ink-mute); letter-spacing: 0.01em; }

@media (max-width: 1040px) {
  .cf-body h3 { font-size: 1.18rem; }
  .cf-body p { font-size: 0.98rem; }
}
@media (max-width: 760px) {
  .cf-card { border-radius: 20px; }
  .cf-body { padding: 20px 18px; }
  .cf-body h3 { font-size: 1.08rem; }
  .cf-body p {
    font-size: 0.94rem; line-height: 1.48;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  }
  .cf-list { gap: 5px; margin-top: 10px; }
  .cf-list li { font-size: 0.76rem; padding: 3px 9px; }
  .cf-go { margin-top: 12px; padding: 9px 15px; font-size: 0.9rem; }
  .svc-note { padding-left: 0; border-left: 0; }
}

/* The control row is arrows + a dot per card, so its width grows with the
   carousel. Adding a tenth service pushed it past the shell on the narrowest
   viewports: measured at a 320px viewport the row ran 19.2px beyond the shell
   on each side, leaving the arrows 4.2px from the screen edge. It was already
   breaking out at 11.0px per side with nine dots, so this is a pre-existing
   overflow the tenth dot made worse by 8.2px rather than a new one; the row is
   flush at 360 and comfortably inside above it, which is why 359 is the cutoff.

   Recovered 43px against the 38.4px needed, from the two places that cost
   nothing: the flex gap either side of the dot row (20px -> 6px, 28px) and
   0.75px of each dot's inline padding (10 dots, 15px). The arrows are
   deliberately left at 48px, above the 44px touch minimum, and the dots keep a
   15.5x23 hit area against the 16x23 they carry elsewhere — shrinking either
   one to buy back space would trade a real touch target for a cosmetic margin.
   Dot gap goes 9px to 7.5px here. Measured result at 320: the row now sits
   0.8px inside the shell and the arrows 24.2px from the screen edge.
   .cf-dots' own negative margin has to track the padding change: it exists to
   cancel the outer dots' padding so the row's paint edges line up with its box. */
@media (max-width: 359px) {
  .cf-ui { gap: 6px; }
  .cf-dot { padding-inline: 3.75px; }
  .cf-dots { margin-inline: -3.75px; }
}

/* ==========================================================================
   v3.0.0 — inner pages (about.html, contact.html)
   ========================================================================== */

/* ---------- Breadcrumbs ---------- */
.crumbs {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.9495rem; letter-spacing: 0.02em;
  color: rgba(197, 213, 248, 0.72); margin-bottom: 1.15rem;
}
.crumbs a { color: rgba(220, 232, 255, 0.9); text-decoration: none; }
.crumbs a:hover { color: #fff; text-decoration: underline; }
.crumbs span[aria-hidden] { color: rgba(197, 213, 248, 0.4); }
.crumbs [aria-current] { color: var(--blue-300); font-weight: 500; }

/* ---------- Page hero ---------- */
.page-hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(1200px 620px at 78% -14%, rgba(47, 107, 255, 0.34), transparent 62%),
    linear-gradient(168deg, var(--navy-800) 0%, var(--navy-900) 68%);
  color: #fff;
  padding-top: clamp(2.6rem, 5vw, 4.4rem);
}
.page-hero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
  background-size: 74px 74px;
  mask-image: radial-gradient(circle at 50% 30%, #000 0%, transparent 78%);
}
.page-hero > .shell { position: relative; z-index: 2; }
.page-hero__grid {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: 1.24fr 0.9fr;
  gap: clamp(2rem, 4.4vw, 4.2rem); align-items: center;
  padding-bottom: clamp(2.4rem, 4.6vw, 4rem);
}
.page-hero h1 {
  color: #fff; font-size: var(--step-4); line-height: 1.04;
  letter-spacing: -0.032em; margin: 0 0 1.1rem;
}
.page-hero h1 em { font-style: normal; color: var(--blue-300); }
.page-hero__lede {
  font-size: var(--step-1); line-height: 1.58; max-width: 62ch;
  color: rgba(219, 228, 252, 0.92); margin: 0;
}
.page-hero__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 1.9rem; }
.page-hero__figure {
  position: relative; margin: 0; border-radius: 26px; overflow: hidden;
  aspect-ratio: 3 / 3.7;
  box-shadow: 0 40px 90px -32px rgba(3, 9, 24, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
.page-hero__figure img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* The portrait source is very tall (1600x3310) — anchoring the crop near the
     top keeps the subject's head fully in frame at every aspect ratio. */
  object-position: 50% 6%;
}

.page-hero--slim { padding-bottom: clamp(2.6rem, 5vw, 4.4rem); }
.page-hero--slim h1 { max-width: 22ch; }
.page-hero__chips { list-style: none; display: flex; flex-wrap: wrap; gap: 0.65rem; margin: 1.8rem 0 0; padding: 0; }
.page-hero__chips li {
  font-size: 0.9706rem; font-weight: 500; letter-spacing: 0.01em;
  color: rgba(228, 236, 255, 0.94);
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.17);
  border-radius: 999px; padding: 7px 15px;
  backdrop-filter: blur(6px);
}

.page-hero__stats { position: relative; z-index: 2; border-top: 1px solid rgba(255, 255, 255, 0.12); }
.page-hero__stats ul {
  list-style: none; margin: 0; padding: clamp(1.5rem, 2.6vw, 2.2rem) 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.4rem;
}
.page-hero__stats li { display: flex; flex-direction: column; gap: 4px; }
.page-hero__stats strong {
  font-family: var(--font-display); font-size: clamp(1.7rem, 2.4vw, 2.35rem);
  font-weight: 900; letter-spacing: -0.03em; color: #fff; line-height: 1;
}
.page-hero__stats small {
  font-size: 0.9073rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: rgba(197, 213, 248, 0.78);
}
/* Attribution footnote: the agent and client counts are organisation-wide, so the
   stats bar has to say so rather than implying individual production. */
.page-hero__stats ul:has(+ .page-hero__stats-note) { padding-bottom: 0.85rem; }
/* The note's bottom margin collapses straight out through .shell, leaving the text
   flush against the section edge (measured 0px), so the space has to be padding here. */
.page-hero__stats:has(.page-hero__stats-note) { padding-bottom: clamp(1.3rem, 2.2vw, 1.9rem); }
.page-hero__stats-note {
  margin: 0;
  max-width: 68ch; font-size: 0.8rem; line-height: 1.55;
  color: rgba(197, 213, 248, 0.62);
}

/* ---------- Lead-in ticks + actions (landing "why" block, about story) ---------- */
.ticks--lead { margin-top: 1.6rem; }
.lead-actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.9rem; }

/* ---------- Affiliation band (About) ----------
   Text-only by client instruction: no PHP or Integrity logos. Sits between two
   white sections, so it runs dark to break up the page rhythm. */
.affil-band { background: linear-gradient(180deg, var(--navy-800), var(--navy-900)); }
.affil-band .section-head h2 { color: #fff; }
.affil-band .section-head p { color: rgba(214, 226, 250, 0.82); }
.affil {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 2.6vw, 2.6rem);
  margin-top: clamp(2rem, 3.4vw, 3rem);
}
.affil__item {
  display: flex; flex-direction: column; gap: 0.55rem;
  padding-top: 1.15rem; border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.affil__label {
  font-size: 0.7992rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--blue-300);
}
.affil__item strong {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.15rem, 1.5vw, 1.34rem);
  line-height: 1.22; letter-spacing: -0.015em; color: #fff;
}
.affil__item p { margin: 0; font-size: 1.017rem; line-height: 1.62; color: rgba(214, 226, 250, 0.8); }
/* Same reasoning as .faq__a a, but on the navy band, where --blue-600 would be
   far too dark — the light tint is the only usable direction here. */
.affil__item p a {
  color: var(--blue-300);
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px; border-radius: 3px;
  transition: color 0.2s ease, text-decoration-thickness 0.2s ease;
}
.affil__item p a:hover { color: #fff; text-decoration-thickness: 2px; }
.affil__item p a:focus-visible { outline: 2px solid var(--blue-300); outline-offset: 3px; }
@media (max-width: 860px) { .affil { grid-template-columns: 1fr; gap: 1.5rem; } }

/* ---------- Values grid ---------- */
.values { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1rem, 1.8vw, 1.6rem); }
.values--3 { grid-template-columns: repeat(3, 1fr); }
.value {
  background: var(--surface); border: 1px solid var(--line); border-radius: 18px;
  padding: clamp(1.4rem, 2vw, 1.85rem);
  box-shadow: var(--shadow-card);
}
.value strong {
  display: block; font-family: var(--font-display); font-size: 1.12rem;
  font-weight: 700; letter-spacing: -0.018em; color: var(--ink); margin-bottom: 0.5rem;
}
.value p { margin: 0; color: var(--ink-mute); font-size: 1.03rem; line-height: 1.62; }

/* ---------- Google Map + office card ---------- */
.map {
  display: grid; grid-template-columns: 1.6fr 0.95fr;
  gap: clamp(1.2rem, 2.2vw, 2rem); align-items: stretch;
}
.map__frame {
  position: relative; border-radius: 22px; overflow: hidden;
  border: 1px solid var(--line); background: var(--surface-alt);
  min-height: 460px; box-shadow: var(--shadow-card);
}
.map__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
/* .map__card carries .glass; only the layout + narrow-column tuning lives here */
.map__card { border-radius: 22px; }
/* the column is narrow, so favour the darker upper-left of the backdrop */
.map__card .glass__bg { object-position: 24% 38%; }
.map__card > .glass__inner {
  display: flex; flex-direction: column; height: 100%;
  padding: clamp(1.6rem, 2.4vw, 2.2rem);
}
.map__card h3 {
  color: #fff; font-size: 1.32rem; letter-spacing: -0.022em; margin: 0 0 0.55rem;
}
.map__card address {
  font-style: normal; color: rgba(255, 255, 255, 0.88);
  font-size: 1.02rem; line-height: 1.58; margin-bottom: 1.4rem;
}
.map__meta { margin: 0 0 1.6rem; display: grid; gap: 0.95rem; }
.map__meta > div { display: grid; gap: 3px; }
.map__meta dt {
  font-size: 0.8453rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: #a9c8ff; font-weight: 600;
}
.map__meta dd { margin: 0; color: rgba(255, 255, 255, 0.95); font-size: 1.03rem; line-height: 1.52; }
.map__meta a { color: #fff; text-decoration: none; border-bottom: 1px solid rgba(127, 178, 255, 0.5); }
.map__meta a:hover { border-bottom-color: #fff; }
.map__card .btn { margin-top: auto; }
.map__card h3 { text-shadow: 0 1px 16px rgba(3, 9, 24, 0.45); }

/* ---------- Final CTA (replaces the inline form on index + about) ---------- */
.final-cta { padding-block: clamp(2.6rem, 5vw, 4.6rem); }
.final {
  display: grid; grid-template-columns: 1.5fr 0.92fr;
  gap: clamp(1.6rem, 3vw, 3rem); align-items: center;
  background:
    radial-gradient(760px 420px at 88% 8%, rgba(47, 107, 255, 0.4), transparent 64%),
    linear-gradient(160deg, var(--navy-800), var(--navy-900));
  color: #fff; border-radius: 26px;
  padding: clamp(1.9rem, 3.4vw, 3.2rem);
  box-shadow: var(--shadow-float);
}
.final__body h2 { color: #fff; margin: 0.35rem 0 0.85rem; }
.final__body p { color: rgba(219, 228, 252, 0.9); max-width: 56ch; margin: 0; }
.final__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.8rem; }
.final__facts { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.05rem; }
/* `anywhere`, not `break-word`. This list carries David@davidbryanfinances.com,
   which has no break opportunity in it, so its min-content width measured 244px
   -- and 244 plus this card's 2x30.4px padding is 305px, against the 283px of
   shell available at a 320px viewport. That overflowed the document to 343px on
   Home and About. `break-word` would not have fixed it: only `anywhere` lets an
   unbreakable run reduce the box's min-content contribution. */
.final__facts li { display: flex; gap: 0.85rem; align-items: flex-start; font-size: 1rem; line-height: 1.5; color: rgba(219, 228, 252, 0.88); overflow-wrap: anywhere; }
.final__facts a { color: #fff; text-decoration: none; border-bottom: 1px solid rgba(127, 178, 255, 0.5); }
.final__facts a:hover { border-bottom-color: #fff; }

/* ---------- Inner-page responsive ---------- */
@media (max-width: 1024px) {
  .page-hero__grid { grid-template-columns: 1fr; gap: 2rem; }
  .page-hero__figure { aspect-ratio: 4 / 3.1; max-height: 460px; }
  .page-hero__figure img { object-position: 50% 4%; }
  .page-hero__stats ul { grid-template-columns: repeat(2, 1fr); gap: 1.2rem 1.6rem; }
  .map { grid-template-columns: 1fr; }
  .map__frame { min-height: 380px; }
  .values, .values--3 { grid-template-columns: repeat(2, 1fr); }
  .final { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .page-hero__figure { aspect-ratio: 4 / 3.6; max-height: 400px; }
  .page-hero__figure img { object-position: 50% 3%; }
  .values, .values--3 { grid-template-columns: 1fr; }
  .final__actions .btn { width: 100%; }
  .map__frame { min-height: 320px; }
}

/* ============================================================
   Office map — Leaflet, recoloured into the site palette
   ------------------------------------------------------------
   The basemap is CARTO's greyscale dark tiles. Rather than guess at a
   hue-rotate over a coloured basemap, the tiles are desaturated to a clean
   luminance ramp first and then tinted, so what survives the filter is the
   map's structure and the colour comes entirely from the palette. Labels
   ride in a second pane above the tint so type stays legible.
   ============================================================ */
.map__frame {
  background:
    radial-gradient(120% 90% at 50% 0%, var(--navy-700), var(--navy-900) 72%);
}
.map__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: transparent; outline: none;
  opacity: 0; transition: opacity 0.5s ease;
}
.map__frame.is-live .map__canvas { opacity: 1; }
.map__canvas:focus-visible { box-shadow: inset 0 0 0 2px var(--blue-300); }

/* Basemap recolour. The duotone lives in an inline SVG filter on contact.html
   (see the comment there for why a CSS filter chain cannot reach navy). Only the
   tile pane is filtered, so the lion pin, tooltip, popup and controls — which
   sit in Leaflet's marker and control panes — keep their true colours. */
.map__filter { position: absolute; width: 0; height: 0; overflow: hidden; }
/* Tiles are positioned at fractional pixels on high-DPR screens, which can leave
   hairline gaps between them. The gaps expose whatever sits under the tile pane,
   so the container is painted the duotone's own dark endpoint (#08152f, the value
   the filter maps white to) and any seam becomes invisible instead of bright.
   This has to live on .leaflet-container, outside the filtered pane — a colour
   set inside the pane would be run through the inversion too. */
.map__frame .leaflet-container { background: #08152f; }
.leaflet-mfBase-pane {
  filter: url(#mfDuotone);
}
.leaflet-container { background: transparent; font: inherit; }
.leaflet-container a { color: var(--blue-300); }

/* ---------- the pin ---------- */
.map-pin-host { background: none; border: 0; }
.map-pin { position: relative; display: block; width: 56px; height: 72px; }
.map-pin__mark {
  position: relative; display: block; z-index: 2;
  filter: drop-shadow(0 8px 14px rgba(3, 9, 24, 0.55));
}
.map-pin__body {
  fill: url(#mfPinFill);
  stroke: rgba(255, 255, 255, 0.92);
  stroke-width: 2.4;
  stroke-linejoin: round;
}
.map-pin__shadow {
  position: absolute; left: 50%; bottom: 1px; transform: translateX(-50%);
  width: 22px; height: 6px; border-radius: 50%;
  background: rgba(3, 9, 24, 0.5); filter: blur(2.5px);
}
.map-pin__pulse {
  position: absolute; left: 50%; top: 27px;
  width: 52px; height: 52px; margin: -26px 0 0 -26px;
  border-radius: 50%; border: 2px solid var(--blue-300);
  opacity: 0; animation: mfPulse 2.8s cubic-bezier(0.2, 0.6, 0.3, 1) infinite;
}
@keyframes mfPulse {
  0%   { transform: scale(0.6); opacity: 0.55; }
  70%  { transform: scale(2.5); opacity: 0; }
  100% { transform: scale(2.5); opacity: 0; }
}
.map-pin.is-still .map-pin__pulse { animation: none; }
.leaflet-marker-icon:focus-visible .map-pin__body {
  stroke: var(--blue-300);
  stroke-width: 3.4;
}

/* ---------- permanent pin label ---------- */
.leaflet-tooltip.map-tag {
  background: rgba(10, 27, 61, 0.82);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid rgba(127, 178, 255, 0.34);
  color: #fff; font-family: var(--font-display);
  font-size: 0.8774rem; font-weight: 600; letter-spacing: 0.01em;
  padding: 5px 11px; border-radius: 999px;
  box-shadow: 0 10px 24px -14px rgba(3, 9, 24, 0.9);
  white-space: nowrap;
}
.leaflet-tooltip.map-tag::before { display: none; }

/* ---------- popup ---------- */
.map-pop .leaflet-popup-content-wrapper {
  background: rgba(10, 27, 61, 0.94);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border: 1px solid rgba(127, 178, 255, 0.3);
  border-radius: 16px; color: #fff;
  box-shadow: 0 22px 50px -22px rgba(3, 9, 24, 0.95);
}
.map-pop .leaflet-popup-content { margin: 14px 16px; line-height: 1.5; }
.map-pop .leaflet-popup-content strong {
  display: block; font-family: var(--font-display);
  font-size: 1.02rem; margin-bottom: 4px;
}
.map-pop__addr {
  display: block; color: rgba(255, 255, 255, 0.82);
  font-size: 0.9495rem; margin-bottom: 10px;
}
.map-pop__go {
  display: inline-block; font-size: 0.8988rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--blue-300); text-decoration: none;
  border-bottom: 1px solid rgba(127, 178, 255, 0.45);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.map-pop__go:hover { color: #fff; border-bottom-color: #fff; }
.map-pop .leaflet-popup-tip {
  background: rgba(10, 27, 61, 0.94);
  border-right: 1px solid rgba(127, 178, 255, 0.3);
  border-bottom: 1px solid rgba(127, 178, 255, 0.3);
}
.map-pop .leaflet-popup-close-button { color: rgba(255, 255, 255, 0.6); }
.map-pop .leaflet-popup-close-button:hover { color: #fff; background: none; }

/* ---------- zoom + recentre controls ---------- */
.leaflet-bar, .leaflet-bar a, .map-recenter { border: 0; box-shadow: none; }
.leaflet-control-zoom { margin: 0 14px 14px 0 !important; }
.leaflet-touch .leaflet-bar { border: 0; }
.leaflet-control-zoom a, .map-recenter {
  width: 36px; height: 36px; display: grid; place-items: center;
  background: rgba(10, 27, 61, 0.78);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border: 1px solid rgba(127, 178, 255, 0.26) !important;
  color: #fff; cursor: pointer; padding: 0;
  font-size: 1.05rem; line-height: 1;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.leaflet-control-zoom a { border-radius: 0; }
.leaflet-control-zoom a:first-child { border-radius: 11px 11px 0 0; }
.leaflet-control-zoom a:last-child { border-radius: 0 0 11px 11px; border-top: 0 !important; }
.leaflet-control-zoom a:hover, .map-recenter:hover {
  background: var(--blue-600); border-color: var(--blue-300) !important; color: #fff;
}
.leaflet-control-zoom a:focus-visible, .map-recenter:focus-visible {
  outline: 2px solid var(--blue-300); outline-offset: 2px;
}
.map-recenter {
  border-radius: 11px; margin: 0 14px 10px 0 !important;
  opacity: 0; pointer-events: none; transform: translateY(6px) scale(0.94);
}
.map__frame.has-moved .map-recenter { opacity: 1; pointer-events: auto; transform: none; }
.map__frame.has-popup .leaflet-tooltip.map-tag { opacity: 0; }
.leaflet-container a.leaflet-popup-close-button {
  color: rgba(255, 255, 255, 0.55);
  width: 30px; height: 30px; font-size: 20px; line-height: 28px;
  transition: color 0.2s ease;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus-visible {
  color: #fff; background: none;
}

/* ---------- attribution ---------- */
.leaflet-control-attribution {
  background: rgba(6, 15, 34, 0.62) !important;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  color: rgba(255, 255, 255, 0.5) !important;
  font-size: 10px; letter-spacing: 0.02em;
  padding: 3px 9px; border-radius: 999px;
  margin: 0 0 12px 12px !important;
}
.leaflet-control-attribution a { color: rgba(255, 255, 255, 0.72) !important; text-decoration: none; }
.leaflet-control-attribution a:hover { color: #fff !important; text-decoration: underline; }

/* ---------- wheel hint + touch gate ---------- */
.map__hint {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.96);
  z-index: 700; pointer-events: none;
  background: rgba(6, 15, 34, 0.86);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border: 1px solid rgba(127, 178, 255, 0.3);
  color: #fff; font-size: 0.9284rem; font-weight: 500;
  padding: 10px 18px; border-radius: 999px;
  opacity: 0; transition: opacity 0.24s ease, transform 0.24s ease;
}
.map__hint.is-on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.map__gate {
  position: absolute; inset: 0; z-index: 650;
  display: grid; place-items: center; cursor: pointer;
  background: rgba(6, 15, 34, 0.2); border: 0;
  font: inherit; color: #fff; width: 100%;
}
.map__gate span {
  background: rgba(10, 27, 61, 0.88);
  border: 1px solid rgba(127, 178, 255, 0.32);
  padding: 9px 18px; border-radius: 999px;
  font-size: 0.9284rem; font-weight: 600;
  box-shadow: 0 14px 30px -16px rgba(3, 9, 24, 0.9);
}

/* ---------- fallback when tiles or the library cannot load ---------- */
.map__fallback[hidden], .map__gate[hidden] { display: none; }
.map__fallback {
  position: absolute; inset: 0; z-index: 600;
  display: grid; place-items: center; text-align: center;
  padding: clamp(1.4rem, 3vw, 2.4rem); color: #fff;
}
.map__fallback img { width: 46px; height: 41px; margin: 0 auto 1rem; opacity: 0.9; }
.map__fallback strong {
  display: block; font-family: var(--font-display);
  font-size: 1.12rem; margin-bottom: 0.4rem;
}
.map__fallback address {
  font-style: normal; color: rgba(255, 255, 255, 0.8);
  font-size: 1.019rem; line-height: 1.55; margin-bottom: 1.2rem;
}

@media (prefers-reduced-motion: reduce) {
  .map__canvas, .map-recenter, .map__hint { transition: none; }
  .map-pin__pulse { animation: none; }
}

/* the label crowds the pin once the frame narrows */
@media (max-width: 760px) {
  .leaflet-tooltip.map-tag { display: none; }
  .map-pin { transform: scale(0.9); transform-origin: 50% 100%; }
  /* the credit line is bottom-left and the zoom control bottom-right; on a
     narrow frame the full Esri string is long enough to run underneath the
     control, so cap its width and let it wrap instead of overlapping */
  .leaflet-control-attribution {
    max-width: calc(100% - 96px); line-height: 1.34;
    margin: 0 0 10px 10px !important;
    border-radius: 11px; padding: 4px 10px;
  }
  .map__gate span { font-size: 0.8988rem; padding: 8px 16px; }
}

/* ==========================================================================
   404 page
   Reuses the shared .page-hero shell, so only the oversized numeral, the
   emblem medallion and the link cards need their own rules.
   ========================================================================== */
.nf__code {
  display: block; margin: 0 0 0.5rem;
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(4.4rem, 12.5vw, 9rem); line-height: 0.84;
  letter-spacing: -0.045em;
  color: var(--blue-300);
}
/* Gradient fill is progressive enhancement only: the solid colour above stays
   as the fallback, because a browser without background-clip:text would render
   `color: transparent` as an invisible heading. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .nf__code {
    background-image: linear-gradient(166deg, #ffffff 6%, var(--blue-300) 46%, rgba(127, 178, 255, 0.32) 98%);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
  }
}
.nf__figure {
  position: relative; margin: 0;
  display: grid; place-items: center;
  aspect-ratio: 1; width: 100%; max-width: 380px; justify-self: center;
}
.nf__ring {
  position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 32% 26%, rgba(47, 107, 255, 0.5), transparent 58%),
              linear-gradient(160deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.03));
  border: 1px solid rgba(255, 255, 255, 0.18);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: 0 40px 90px -34px rgba(3, 9, 24, 0.8);
}
.nf__ring::after {
  content: ""; position: absolute; inset: -14px; border-radius: 50%;
  border: 1px solid rgba(127, 178, 255, 0.22);
  animation: nfPulse 3.4s ease-in-out infinite;
}
@keyframes nfPulse {
  0%, 100% { transform: scale(1); opacity: 0.55; }
  50%      { transform: scale(1.045); opacity: 0.16; }
}
.nf__figure img {
  position: relative; z-index: 2; width: 46%; height: auto;
  filter: drop-shadow(0 14px 26px rgba(3, 9, 24, 0.55));
}
.nf-links .card { display: block; }
.nf-links .card h3 { margin: 0.85rem 0 0.45rem; }
.nf-links .card p { margin: 0; color: var(--ink-mute); }
.nf-links .card:hover { transform: translateY(-4px); box-shadow: var(--shadow-float); border-color: var(--blue-300); }
.nf-links .card__icon { color: var(--blue-600); }

@media (max-width: 860px) {
  /* No grid-row override here: DOM order is copy then emblem, and that is the
     order it must stack in. Promoting the emblem filled the whole first screen
     with a lion and pushed the actual "404" message below the fold. */
  .nf__figure { max-width: 190px; margin-top: 0.4rem; }
}
@media (prefers-reduced-motion: reduce) {
  .nf__ring::after { animation: none; }
}


/* =====================================================================
   v3.5.0 — image-background hero, floating pill header, partner band
   ===================================================================== */
:root {
  /* Gambetta carries a true italic; Iowan/Georgia are the metric fallbacks so a
     failed webfont still renders a serif italic rather than a slanted sans. */
  --font-serif: "Gambetta", "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --head-h: 98px;
}
/* Anchor targets must clear the fixed pill. Without this every in-page jump
   (#services, #process, #faq) lands with its heading hidden behind the header. */
html { scroll-padding-top: calc(var(--mast-h, var(--head-h)) + 14px); }
/* The #top scroll sentinel is an empty inline <span> at the very start of
   <body>. Left inline it would generate an anonymous line box and push the
   whole page down by one line-height, so it is forced to zero. */
#top { display: block; height: 0; }

/* inner pages: the navy page-hero now runs under the floating pill */
.page-hero { padding-top: calc(var(--mast-h, var(--head-h)) + clamp(1.4rem, 3vw, 2.4rem)); }

.hero__pills { justify-content: flex-start; gap: 0.5rem; margin-bottom: 1.15rem; }
/* the old dot separator belonged to the centred inline row; chips replace it */
.hero__pills span:not(:last-child)::after { content: none; display: none; }

/* ---------- rotating glass CTA medallion ---------- */
.hero__badge {
  position: absolute;
  right: clamp(1rem, 3vw, 2.1rem);
  top: 46%;
  z-index: 3;
  width: 134px; height: 134px;
  border-radius: 50%;
  display: grid; place-items: center;
  text-decoration: none;
  color: #fff;
  /* navy glass, not a white tint: the medallion floats over the brightest part
     of the photograph, and a white tint lifted the backdrop behind its own ring
     text to 2.87:1. Tinting toward navy instead measures 6.9:1. */
  background: rgba(6, 15, 34, 0.44);
  border: 1px solid rgba(255, 255, 255, 0.26);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 24px 52px -26px rgba(0, 0, 0, 0.8);
  transition: transform 0.35s ease, background 0.35s ease;
}
.hero__badge:hover { transform: scale(1.045); background: rgba(6, 15, 34, 0.58); }
.hero__badge-ring {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  animation: mfBadgeSpin 24s linear infinite;
}
.hero__badge-ring text {
  fill: rgba(255, 255, 255, 0.95);
  font-family: var(--font-display);
  font-size: 8.64px;
  font-weight: 500;
  letter-spacing: 0.055em;
}
.hero__badge-core {
  display: grid; place-items: center;
  width: 54px; height: 54px;
  border-radius: 50%;
  background: var(--blue-500);
  color: #fff;
  box-shadow: 0 12px 26px -12px rgba(47, 107, 255, 0.9);
}
@keyframes mfBadgeSpin { to { transform: rotate(360deg); } }

/* ---------- Trusted partners band ---------- */
.partners {
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding-block: clamp(2.1rem, 4vw, 3.1rem);
}
.partners__title {
  text-align: center;
  margin: 0 0 clamp(1.2rem, 2.6vw, 1.9rem);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.8774rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.partners__row {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: center;
  column-gap: clamp(1.5rem, 3.8vw, 3.3rem);
  row-gap: clamp(1.6rem, 2.6vw, 2.6rem);
}
.partners__row li { display: flex; align-items: center; }
.partners__row img {
  display: block;
  /* --pw is a per-brand optical width: the logos are normalised by ink mass, not
     by bounding box, so a thin serif wordmark and a bold boxed one read at the
     same visual weight instead of one dwarfing the other. */
  width: calc(var(--pw) * var(--pscale, 1));
  height: auto;
  /* --pop is a per-brand resting opacity. 0.52 suits the neutral/dark wordmarks,
     but a light brand colour turns pale under grayscale(1) and drops out of the
     row, so those get a measured bump to land in the same contrast band. */
  filter: grayscale(1) opacity(var(--pop, 0.52));
  transition: filter 0.35s ease, transform 0.35s ease;
}
.partners__row li:hover img { filter: grayscale(0) opacity(1); transform: translateY(-2px); }
.p-moo { --pw: 192.6px; }
.p-nlg { --pw: 159.5px; }
.p-for { --pw: 121.6px; }
.p-fg  { --pw: 174.5px; }
.p-an  { --pw: 81.1px; }
.p-aig { --pw: 86.2px; }
.p-jh  { --pw: 143.3px; }
.p-nw  { --pw: 145.4px; }
.p-pl  { --pw: 98.9px; }
.p-lfg { --pw: 119.5px; }
/* The Standard is a solid-fill flag, not a wordmark: its ink density (0.46) sits
   outside the range the width model was fitted on, so the density term is clamped
   to the fitted maximum and given a small legibility allowance. */
.p-std { --pw: 78.2px; }
.p-na  { --pw: 135.8px; }

/* ---------- hero responsive ---------- */
@media (max-width: 1100px) {
  .hero__badge { display: none; }
  .hero__copy { max-width: 34rem; }
}
@media (max-width: 860px) {
  /* Fill the screen edge to edge. The old `min(96svh, 780px)` failed twice
     over: the 780px cap bound before the percentage did on every current
     iPhone viewport, freezing the hero at 780px no matter how tall the screen
     was (measured white below the CTAs: 72px on iPhone 16, 94px on 16 Pro,
     176px on 16 Pro Max), and the 96% stopped 4% short even when the cap did
     not bind (another 30px). Both read as a section cut off rather than an
     intentional edge.

     `svh` is the deliberate unit here. It is the viewport with Safari's
     toolbar expanded, which is the state on load, so the hero fills exactly
     what is visible and never reflows. `dvh` tracks the toolbar live and would
     resize the hero mid-scroll, jumping three lines of display type. `lvh` is
     the toolbar-collapsed height, so on load the hero would overshoot and put
     both CTAs behind the toolbar. `svh`'s only cost is coming up short once
     the toolbar collapses, and the navy canvas on `html` covers that. */
  .hero__inner { min-height: 100svh; }
  .partners__row { --pscale: 0.8; gap: 1.4rem 2rem; }
}

/* The trust points stack from 1100px down, so the panel treatment starts there
   too — not at 860px, which left tablet widths showing the old capsule stack. */
@media (max-width: 1100px) {

  /* Stacked one per row, each trust point was its own bordered glass capsule
     sitting directly beneath two full-width buttons — five near-identical
     rounded rectangles down the column, so the trust points read as more
     buttons. Fix is to move the surface up a level: one panel owns the
     background, border and radius, and the rows are separated by hairlines.
     A divided list is unmistakably information; a stack of capsules is not. */
  /* v3.5.5 — the divided panel still read as a third CTA. Measured at 390px:
     it stood 204px tall, 20.8% of the 978px hero, with row titles at 15.92px/700
     against a 16.06px lede — supporting copy set at 99% of the size of the
     paragraph it supports, and heavier, inside its own bordered card at exactly
     the buttons' width. Three levers, all of them subtraction: the panel gives up
     its surface entirely and keeps only the hairlines, so it is a caption on the
     hero rather than an object sitting on it; the type drops a full step to sit
     clearly below the lede; and the rows tighten. */
  .hero__trust {
    grid-template-columns: 1fr;
    gap: 0;
    /* match the copy column's measure — at 700px the panel otherwise spans the
       full shell, wider than the paragraph above it, and the rows read as empty */
    max-width: 34rem;
    margin-top: clamp(1.45rem, 5vw, 2rem);
    border: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  .trust {
    /* without the circular chip the glyph has no mass of its own, so centring it
       against a two-line block leaves it floating; hang it off the title line */
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.62rem 0.1rem;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    font-size: 0.8346rem;
    line-height: 1.35;
  }
  .trust + .trust { border-top: 1px solid rgba(255, 255, 255, 0.085); }
  /* the circled icon chip is what made each row look pressable; keep the glyph,
     drop the chip */
  /* scoped to the hero: `.trust__icon` is reused by the contact lists on all
     four pages, which must keep their chips */
  .hero__trust .trust__icon { width: 18px; height: 18px; margin-top: 1px; border-color: transparent; background: none; color: var(--blue-300); }
  .hero__trust .trust__icon svg { width: 17px; height: 17px; }
  .trust strong { font-size: 0.8881rem; font-weight: 600; letter-spacing: -0.005em; margin-bottom: 0; }

}
@media (max-width: 560px) {
  .hero__actions .btn { width: 100%; justify-content: center; }
  .partners__row { --pscale: 0.72; }

  /* v3.5.6 — trust points hidden on phones by request. Kept at 561px and above,
     where the row still has the width to sit as a quiet caption. `display:none`
     rather than a visual-only hide: the three claims are restated in the services
     and "why" sections, so nothing is lost to a screen reader by removing them
     from the hero on the narrowest layout. */
  .hero__trust { display: none; }

  /* v3.5.9 — the category row goes too. At 390px the three chips need 376.9px of
     line (354.5 of chip plus two 11.2px gaps) against 353.2px of shell, so they
     wrap to two rows: 88.5px tall, 11.3% of the 780px hero, before their 1.4rem
     margin. Lightening them instead is not available — these chips sit over the
     bright window wall of the vertical hero frame, and with the fill removed white
     text measures 3.07:1, 3.30:1 and 3.75:1 against the brightest pixel behind
     each one, all failing AA. That is exactly why they were given a navy glass
     base at ≤1200px. The choice is therefore bulky or illegible, and neither earns
     11% of a phone hero for three words already in the nav and repeated as full
     cards in the services section. Held at 561px and up, where the row fits on one
     line (376.9px against 515.2px of shell at 560px) and reads as a quiet label. */
  .hero__pills { display: none; }

  /* The panel plus its top margin was carrying about 186px at the foot of the
     hero. `.hero__inner` is `justify-content: flex-end`, so simply hiding it
     makes the hero end 186px sooner and leaves the buttons floating mid-frame
     with the fold right under them. Hand that space back to the CTA block
     instead: the copy keeps its position, the gap opens between the check list
     and the buttons, and the buttons land low in the frame within thumb reach. */
  .hero__checks { margin-bottom: 2.4rem; }
  .hero__actions { margin-top: clamp(2.6rem, 11vw, 4.2rem); }
}

/* ---------- Partner row: five orphan bands ----------
   The 12 logos were scaled up 15% in v3.5.25 (every --pw multiplied by 1.15,
   so the ink-mass normalisation between brands is preserved exactly). Bigger
   logos move every wrap point, which retired the four bands fitted for the old
   widths and opened four new ones. A 1px sweep of 320-1920 found them and
   nothing else: 401-411, 496-544, 561-593, 845-860 and 1052-1092. Everything
   from 1120 to 1920 is orphan-free at the inherited gap, and horizontal
   overflow is 0 at all 1601 widths, so the row still fits the shell at the
   larger size.

   For each band the viable gap set was measured per width, one width at a time.
   Two properties of that data drive the fixes below. First, the viable set is
   an unbroken range from 12px up to a per-width maximum, so the tightest
   constraint in a band is simply the smallest of those maxima. Second, lowering
   the gap does not always close a band - it can slide it downward, because a
   narrower gap lets an earlier row absorb one more logo and the orphan reforms
   at a lower width. Every value below is verified against the whole band, not
   against its edges.

   401-411 (11px): wrapped 2+3+3+3+1 and 3+3+2+3+1. Per-width maxima run 26px
   at 401 to 31px at 411, so a flat 26px clears the band with no ramp needed.
   Curiously 33-36px also wrap cleanly here while 27-32px do not - the row
   jumps to a different, orphan-free configuration once the gap is wide enough -
   but going up would need a gap wider than the inherited one, so this goes down.

   496-544 (49px): wrapped 4+3+4+1, the widest band and the tightest. The
   smallest maximum is 17px at 496, and a flat 17px would be a 15px drop from
   the inherited 32px - visible as a sudden squeeze. The maxima climb almost
   linearly across the band instead (17px to 31px), so this ramps and holds
   roughly 0.5px of margin under the limit at every width.

   561-593 (33px): also 4+3+4+1, same linear structure, maxima 22px to 31px.
   Ramped on the same principle. 545-560 sits between this band and the one
   above it and is orphan-free at the inherited gap, so the two are left as
   separate queries rather than merged into one wide ramp.

   845-860 (16px): wrapped 5+6+1. Maxima 26px to 29px, so a flat 26px pulls
   row 1 up to 6 and gives 6+6 across the band.

   1052-1092 (41px): wrapped 5+6+1, the only band above 860px and so the only
   one overriding the fluid clamp rather than the flat gap. Maxima 33px to 41px
   against an inherited 39.98px at 1052, ramped to land within 1px of the
   inherited value at the top edge. This band was nearly missed twice: the first
   1px sweep did detect it, but a reporting bug in the sweep script dropped the
   final contiguous run of bad widths, so only the four bands below it printed.
   The arithmetic gave it away - 11+49+33+16 accounts for 109 of the 150 bad
   widths the same script had counted, and the missing 41 were exactly this
   band. Trust the counts, not the summary.

   The first four sit below 860px, where the gap is the flat `gap: 1.4rem 2rem`
   rather than the fluid clamp. Media queries add no specificity, so all five
   win on source order alone - they must stay after the rules they undo. Do not
   hoist them. */
@media (min-width: 401px) and (max-width: 411px) {
  .partners__row { column-gap: 26px; }
}
@media (min-width: 496px) and (max-width: 544px) {
  .partners__row { column-gap: clamp(16.5px, calc(29.167vw - 128.17px), 30.5px); }
}
@media (min-width: 561px) and (max-width: 593px) {
  .partners__row { column-gap: clamp(21.5px, calc(28.125vw - 136.28px), 30.5px); }
}
@media (min-width: 845px) and (max-width: 860px) {
  .partners__row { column-gap: 26px; }
}
@media (min-width: 1052px) and (max-width: 1092px) {
  .partners__row { column-gap: clamp(32.5px, calc(20vw - 177.9px), 40.5px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__badge-ring { animation: none; }
  .hero__badge:hover { transform: none; }
  .partners__row li:hover img { transform: none; }
  .btn:hover .btn__arrow { transform: none; }
}

/* Breakpoint matched to the <picture> art direction at 1200px, so the scrim
   always describes the image it is actually sitting over. Below here the copy
   occupies most of the hero height and the horizontal scrim that protects the
   desktop copy column does almost nothing for it, so the vertical veil carries
   the contrast on its own: the room stays legible as atmosphere behind the
   text rather than competing with it. */
@media (max-width: 1200px) {
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(6, 15, 34, 0.6) 0%, rgba(6, 15, 34, 0.5) 16%,
        rgba(6, 15, 34, 0.68) 40%, rgba(6, 15, 34, 0.88) 72%, rgba(6, 15, 34, 0.97) 100%);
  }
  /* the vertical frame puts every face in the upper two thirds, so bias the
     crop upward and let the empty rug area fall off the bottom */
  .hero__bg img { object-position: 50% 22%; }
  .hero h1 { text-shadow: 0 2px 22px rgba(3, 9, 24, 0.78); }
}

/* Below 560px no 9:16 frame can cover the hero box without cropping sideways:
   the hero is ~1018px tall at 390px wide, an aspect of 0.38 against the image's
   0.563, so `cover` eats roughly 150px off each side and bisects the faces at
   both edges. Measured, not guessed — the crop geometry is checked against
   detected face boxes at every breakpoint.

   So on phones the photograph stops covering. Its box is given the image's own
   aspect ratio, which means `cover` has nothing left to crop and all six faces
   survive intact, and it is masked out at the bottom so it dissolves into the
   navy the lower copy sits on instead of ending on a hard horizontal edge. */
@media (max-width: 560px) {
  .hero__bg {
    inset: 0 0 auto 0;
    height: auto;
    aspect-ratio: 941 / 1672;
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 56%,
      rgba(0, 0, 0, 0.5) 80%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 56%,
      rgba(0, 0, 0, 0.5) 80%, transparent 100%);
  }
  .hero__bg img { object-position: 50% 50%; }
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(6, 15, 34, 0.5) 0%, rgba(6, 15, 34, 0.42) 14%,
        rgba(6, 15, 34, 0.66) 36%, rgba(6, 15, 34, 0.86) 56%,
        rgba(6, 15, 34, 0.96) 74%, #060f22 100%);
  }
}

/* On very wide viewports a hero capped at 846px is a letterbox strip, and
   `cover` then discards ~90% of the frame's height, which trims the standing
   figure nearest the top edge. Letting the hero grow keeps the whole group in
   shot and gives the photograph room to read as a photograph. */
@media (min-width: 2100px) {
  .hero__inner { min-height: min(94svh, 1080px); }
}

/* Measured fix. On the vertical frame the chips land over the bright window
   wall, where the veil alone left white-on-photo at 2.64:1 — a fail. Darkening
   the veil enough to rescue them would also dim the faces directly behind, so
   the chip carries its own contrast instead of borrowing it from the scrim:
   a navy glass base rather than a white tint. Re-measured at 5.3:1. */
@media (max-width: 1200px) {
  .hero__pills span {
    color: #fff;
    background: rgba(6, 15, 34, 0.72);
    border-color: rgba(255, 255, 255, 0.26);
  }
}

/* ============================================================
   OUR TEAM (about) — v3.5.4
   ============================================================ */
.team{
  --team-gap:clamp(1.1rem,1.7vw,1.5rem);
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--team-gap);
  margin-top:clamp(2.1rem,3.5vw,3rem);
}
.team__card{
  position:relative;margin:0;
  flex:0 1 calc(25% - 1.125rem);min-width:212px;max-width:400px;
}
.team__photo{
  /* Taller than the 4/5 source by the height the plate needs, so the plate can
     sit wholly inside the frame without the head clearance changing. Previously
     the card carried 1.15rem of bottom padding and the plate hung 18.4px past the
     photo's bottom edge onto the white page: 49px of frosted panel sampling the
     photograph and 18px of it sampling white, which reads as two stacked
     rectangles rather than one piece of glass. */
  /* isolation:isolate confines any z-index inside the frame. Without it the
     overlays below sat in the CARD's stacking context, where the plate is
     z-index:auto — so a z-index:1 on them painted the tint and the scrim OVER the
     plate, dragging its measured fill from a near-white rgba(244,249,255,.8) down
     to 125,136,157 and taking the role text with it. The overlays do not need a
     z-index at all: they are positioned and the img is not, so they already paint
     above it. */
  position:relative;isolation:isolate;aspect-ratio:4/5.28;overflow:hidden;
  border-radius:var(--r-md);background:var(--surface-alt);
  /* Cool bloom + hairline ring instead of the neutral card shadow. Two shadows
     doing different jobs: a tight navy one that still reads as contact with the
     page, and a wide, very soft blue one that lifts the card off the white. The
     inset ring is the "glass" edge — 1px of blue at 18% is enough to catch the
     eye at the corner radius without becoming a visible border. */
  box-shadow:
    inset 0 0 0 1px rgba(47,107,255,.18),
    0 2px 6px -2px rgba(10,27,61,.20),
    0 16px 34px -20px rgba(10,27,61,.34),
    0 30px 64px -34px rgba(47,107,255,.50);
  transition:box-shadow .5s cubic-bezier(.2,.7,.2,1);
}
/* The seven headshots arrive with unrelated backgrounds — dark green, white,
   outdoor daylight, black, grey, blue-grey — and that mismatch, not the framing,
   is what stops the grid reading as one set. A flat blue wash would fix it and
   turn every face sickly, so the tint is a radial that is fully transparent over
   the head (the face sits at roughly 50% 34% in every crop, by the same landmark
   maths that drove the cropping) and reaches full strength only in the corners,
   where the backgrounds actually differ. Skin midtones are untouched. */
.team__photo::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(76% 60% at 50% 34%,
      rgba(20,52,120,0) 40%, rgba(20,52,120,.13) 72%, rgba(16,44,108,.30) 100%);
  transition:opacity .5s ease;
}
.team__photo::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(6,15,34,0) 52%,rgba(6,15,34,.30) 100%);
}
.team__photo img{
  width:100%;height:100%;display:block;object-fit:cover;
  /* a touch of cool: pulls the warm outdoor and yellow-grey studio frames toward
     the navy the rest of the site is built on, without desaturating skin */
  filter:saturate(.97) contrast(1.035) hue-rotate(-3deg);
  transition:transform .65s cubic-bezier(.2,.7,.2,1),filter .5s ease;
}
.team__card:hover .team__photo img{transform:scale(1.045);filter:saturate(1.02) contrast(1.05) hue-rotate(-3deg)}
.team__card:hover .team__photo{
  box-shadow:
    inset 0 0 0 1px rgba(47,107,255,.30),
    0 2px 6px -2px rgba(10,27,61,.22),
    0 20px 40px -20px rgba(10,27,61,.36),
    0 38px 78px -34px rgba(47,107,255,.68);
}
.team__card:hover .team__photo::before{opacity:.72}
.team__plate{
  position:absolute;left:.9rem;right:1.9rem;bottom:.8rem;
  display:flex;flex-direction:column;gap:.1rem;
  padding:.72rem .95rem .78rem;
  border-radius:var(--r-sm);
  background:rgba(247,250,255,.92);
  border:1px solid rgba(207,224,255,.85);
  box-shadow:0 18px 40px -24px rgba(10,27,61,.44),0 0 0 1px rgba(47,107,255,.07);
  transition:transform .5s cubic-bezier(.2,.7,.2,1);
}
@supports (backdrop-filter:blur(10px)){
  .team__plate{background:rgba(244,249,255,.8);backdrop-filter:blur(16px) saturate(1.5)}
}
.team__card:hover .team__plate{transform:translateY(-3px)}
.team__name{
  font-family:var(--font-display);font-weight:600;
  font-size: clamp(1.019rem, 0.936rem + 0.1872vw, 1.123rem);
  line-height:1.25;color:var(--ink);letter-spacing:-.01em;
}
.team__role{
  font-size: clamp(0.856rem, 0.8025rem + 0.1284vw, 0.9363rem);
  line-height:1.35;color:var(--ink-soft);
}
/* Explicit flex row break after the lead card. Setting the lead to flex-basis:100%
   is not enough on its own: the flex base size is clamped by max-width before the
   line-breaking step, so a lead capped at 400px reports a 400px hypothetical size,
   leaves room on the line, and a second card joins it — measured as rows [2,3,2].
   A zero-height 100%-basis item ends the line unconditionally, independent of any
   cap on the lead.

   The break occupies a line of its own, so the space below the lead is two row-gaps
   rather than one: 27.2px against 13.6px between the pairs. A negative margin does
   not reduce it — margin-top:-1gap, -2gap and top+bottom all measured 27.2px,
   because a negative margin on a flex item does not shrink its line's cross size.
   Kept as deliberate separation between the lead and the rest of the group rather
   than papered over with CSS that does nothing. */
.team__break{display:none}

.team__card--lead .team__plate{
  background:rgba(6,15,34,.86);border-color:rgba(127,178,255,.42);
  box-shadow:0 18px 40px -24px rgba(6,15,34,.6),0 0 0 1px rgba(47,107,255,.16);
}
@supports (backdrop-filter:blur(10px)){
  .team__card--lead .team__plate{background:rgba(6,15,34,.74)}
}
.team__card--lead .team__name{color:#fff}
.team__card--lead .team__role{color:var(--blue-300)}

/* Seven cards is prime, so every column count except four leaves an orphan on the
   last row (3+3+1, 2+2+2+1). David spans the full first row from 1000px down, which
   makes every remaining count divide cleanly: 1+3+3 and 1+2+2+2. He is the lead card
   anyway, so the arrangement and the hierarchy agree. */
@media (max-width:1000px){
  .team__card{flex-basis:calc(33.333% - 1rem)}
  /* two classes, so it survives the plain .team__card basis in the later blocks */
  .team .team__card--lead{flex-basis:100%}
  .team__break{display:block;flex:0 0 100%;height:0}
}
@media (max-width:780px){
  /* `min-width` must go to 0 here. It was set to 212px for the desktop grid, and
     212*2 + 17.6 of gap needs 441.6px of shell — more than the shell has at any
     viewport below 480px. Between 431 and 479 the basis said 50% but the min-width
     forced 212px, the pair could not fit, and the grid silently collapsed to a
     single column. That band covers real phones (iPhone 16 Pro Max reports 440),
     which is why this read as "not in two columns" on a device while measuring
     correctly at 390. */
  .team__card{flex-basis:calc(50% - .75rem);min-width:0}
  .team__plate{right:1.4rem}
}
/* Below 475px the two-up card drops under ~212px wide, where the plate's insets,
   padding and type — sized for the wider card — leave only ~100px of measure and
   push a two-line name plus a three-line role past 40% of the photo height, across
   the face. Everything in the plate steps down together. */
@media (max-width:475px){
  .team{--team-gap:.85rem}
  .team__card{flex-basis:calc(50% - .425rem)}
  .team__card--lead{max-width:330px}
  .team__card:not(.team__card--lead) .team__plate{
    left:.5rem;right:.6rem;bottom:.55rem;gap:.02rem;
    padding:.48rem .56rem .54rem;border-radius:8px;
  }
  .team__card:not(.team__card--lead) .team__name{font-size: 0.902rem;line-height:1.2}
  .team__card:not(.team__card--lead) .team__role{font-size: 0.7722rem;line-height:1.28}
}

/* ==========================================================================
   THANK YOU PAGE (thanks.html)
   Reached only by a successful POST from the consultation form. Everything
   here reuses existing components -- .page-hero, .cards/.card, .section-head --
   so the only additions are the confirmation tick, a numeric variant of the
   card icon, and the three "reach David directly" rows.
   ========================================================================== */

/* Confirmation tick, sitting between the eyebrow and the h1 in the dark hero.
   Deliberately quiet: the headline already says "Thank you", so this is a
   glance-level confirmation, not the loudest thing on the page. */
.tnx__check {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  margin: 1.15rem 0 1.25rem;
  border-radius: 50%;
  color: #fff;
  background: linear-gradient(180deg, var(--blue-500), var(--blue-600));
  box-shadow: 0 0 0 8px rgba(47, 107, 255, 0.16), 0 14px 30px rgba(6, 15, 34, 0.45);
}

/* .card__icon holds a 24px SVG everywhere else on the site; here it holds a
   digit, so it needs an explicit type ramp or it inherits body size and sits
   too small inside the 50px tile. */
.card__icon--num {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* .cards is tuned for FOUR items -- its two existing uses (the About pillars and
   the 404 links) both hold four, so a two-column resolution is 2+2 and reads
   fine. This page holds three, where two columns is 2+1 and strands the third.
   Measured: the base auto-fit gives two columns from 640px to ~999px, and the
   1000-1365px override forces two on purpose. So opt out of both and allow only
   one column or three, never two.

   Both rules below are single-class, the same specificity as the .cards rules
   they undo, so they only win by sitting later in the file. Do not hoist them.

   1000px is where three 290px tracks plus two ~20px gaps clear the shell. Below
   that the stack is capped and centred, otherwise a lone card stretches to 800px
   at tablet widths and the three-line body copy reads as one long ribbon. */
.cards--three {
  grid-template-columns: 1fr;
  max-width: 620px;
  margin-inline: auto;
}
@media (min-width: 1000px) {
  .cards--three {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: none;
  }
}

.tnx-direct__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Three items, so auto-fit is the wrong tool: it resolves to two columns from
     640px to ~975px and orphans the third. One column or three, nothing in
     between. Three tracks clear the shell from ~980px, but the breakpoint is
     pinned to 1000px to match .cards--three above -- otherwise there is a 20px
     band where this section is three across while the section above it is still
     a single stack. */
  grid-template-columns: 1fr;
  gap: clamp(1rem, 2vw, 1.5rem);
}
@media (min-width: 1000px) {
  .tnx-direct__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.tnx-direct__list li {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: clamp(1.05rem, 1.8vw, 1.35rem);
}
.tnx-direct__icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: rgba(47, 107, 255, 0.1);
  color: var(--blue-600);
}
.tnx-direct__list li > span:last-child {
  display: grid;
  gap: 2px;
  min-width: 0;
}
.tnx-direct__list strong {
  font-family: var(--font-display);
  font-size: 0.8438rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* Base CSS is `a { color: inherit; text-decoration: none }`, so without this
   these three would render as plain text and read as labels, not links. */
.tnx-direct__list a {
  color: var(--blue-600);
  /* 0.9375rem, not the 1.04rem the contact page uses: the email address is the
     longest string on the page and the narrowest track this component ever gets
     is ~187px of text width (three columns at 980px). At body size the domain
     alone needs ~200px and wrapped mid-word. A <wbr> after the @ gives it a
     clean two-line break; this size makes the second line fit. */
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  /* Last resort only. The <wbr> is the intended break point; this just stops a
     long string from punching out of the card if the address ever changes. */
  overflow-wrap: break-word;
}
.tnx-direct__list a:hover,
.tnx-direct__list a:focus-visible { color: var(--blue-500); }
