/* ==========================================================================
   RankForImpact — site stylesheet
   Authority today. Opportunity tomorrow.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts (self-hosted variable subsets — no third-party requests)
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Cormorant Garamond';
  src: url("../fonts/cormorant.c47ff12887a3.woff2") format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cormorant Garamond';
  src: url("../fonts/cormorant-italic.3e2c4175a96d.woff2") format('woff2');
  font-weight: 300 700;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Jost';
  src: url("../fonts/jost.928f4210aa48.woff2") format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* The brand is a single light palette by design — pin the scheme so a
     dark-mode OS does not invert form controls and scrollbars. */
  color-scheme: light;

  /* Brand */
  --green: #123328;
  --green-deep: #0d2620;
  --green-tint: #1b4335;
  --gold: #c19a63;
  --gold-soft: #c9a877;
  --gold-ink: #c19a63; /* accent text on cream */
  --gold-light: #c9a877; /* gold for use on dark green */

  /* Neutrals — cream-hi is the page ground, cream the warmer accent band */
  --cream: #f4f0e8;
  --cream-hi: #faf8f3;
  --cream-lo: #ece5d7;
  --ink: #1c1c1a;
  --body: #55554f;
  --muted: #5b5b55;
  --line: #ddd7cb;
  --line-soft: #e7e2d6;

  /* On dark */
  --on-dark: #ffffff;
  --on-dark-muted: rgba(255, 255, 255, 0.72);
  --on-dark-line: rgba(255, 255, 255, 0.16);

  /* Type */
  --serif: 'Cormorant Garamond', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --sans: 'Jost', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;

  /* Rhythm */
  --gutter: clamp(1.25rem, 5vw, 4rem);
  --maxw: 1240px;
  --section-y: clamp(4.5rem, 9vw, 8.5rem);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 104px; /* clears the sticky header on anchor jumps */
}

body {
  margin: 0;
  background: var(--cream-hi);
  color: var(--body);
  font-family: var(--sans);
  font-size: clamp(0.95rem, 0.2vw + 0.9rem, 1.0rem);
  font-weight: 300;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

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

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

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

/* appearance and margin are reset alongside the rest because the booking
   triggers are real <button> elements wearing .btn, and iOS Safari otherwise
   rounds their corners and repaints the background regardless of what .btn
   asks for. */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}

::selection {
  background: var(--gold);
  color: var(--green);
}

:focus-visible {
  outline: 2px solid var(--gold-ink);
  outline-offset: 3px;
}

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

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: -100px;
  left: var(--gutter);
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--green);
  color: var(--on-dark);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.skip-link:focus {
  top: 0.75rem;
}

/* --------------------------------------------------------------------------
   4. Layout utilities
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap--narrow {
  max-width: 960px;
}

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

.section--tight {
  padding-block: clamp(3rem, 6vw, 5.5rem);
}

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

.dark {
  background: var(--green);
  color: var(--on-dark-muted);
}

.dark h1,
.dark h2,
.dark h3,
.dark h4,
.dark strong {
  color: var(--on-dark);
}

/* Keeps a short phrase whole when a heading wraps. "SEO & AEO" needs it: broken
   across lines it strands an ampersand at one end and an acronym at the other. */
.nb {
  white-space: nowrap;
}

.tone-lo {
  background: var(--cream);
}
.tone-hi {
  background: var(--cream-hi);
}
.tone-deep {
  background: var(--cream-lo);
}

/* --------------------------------------------------------------------------
   5. Type components
   -------------------------------------------------------------------------- */
.eyebrow {
  display: block;
  margin: 0 0 1.5rem;
  color: var(--gold-ink);
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  line-height: 1.6;
  text-transform: uppercase;
}

.dark .eyebrow,
.eyebrow--light {
  color: var(--gold-light);
}

.display,
.h1,
.h2,
.h3 {
  font-family: var(--serif);
  font-weight: 300;
  color: var(--ink);
  letter-spacing: -0.005em;
  text-wrap: balance;
}

.display {
  font-size: clamp(2.6rem, 6.2vw, 4.6rem);
  line-height: 1.04;
}

.h1 {
  font-size: clamp(2.3rem, 5vw, 3.7rem);
  line-height: 1.08;
}

.h2 {
  font-size: clamp(1.85rem, 3.6vw, 3rem);
  line-height: 1.14;
}

.h3 {
  font-size: clamp(1.3rem, 1.8vw, 1.65rem);
  line-height: 1.25;
}

.h4 {
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.01em;
  line-height: 1.5;
}

.accent {
  font-style: italic;
  color: var(--gold-ink);
}

.dark .accent,
.accent--light {
  color: var(--gold-light);
}

.lede {
  max-width: 44ch;
  font-size: clamp(1rem, 0.3vw + 0.95rem, 1.08rem);
  line-height: 1.8;
}

.prose > * + * {
  margin-top: 1.15rem;
}

.prose--wide .lede {
  max-width: 56ch;
}

.rule {
  width: 56px;
  height: 1px;
  margin: 2rem 0;
  background: var(--gold);
  border: 0;
}

.center {
  text-align: center;
}
.center .lede {
  margin-inline: auto;
}
.center .eyebrow {
  text-align: center;
}

/* --------------------------------------------------------------------------
   6. Icons
   -------------------------------------------------------------------------- */
.icon {
  width: 1.4em;
  height: 1.4em;
  flex: none;
}

.icon--lg {
  width: 30px;
  height: 30px;
  color: var(--green);
}

.dark .icon--lg {
  color: var(--gold-light);
}

.icon-ring {
  display: grid;
  place-items: center;
  width: 66px;
  height: 66px;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--green);
}

.dark .icon-ring {
  border-color: var(--on-dark-line);
  color: var(--on-dark);
}

.icon-ring .icon {
  width: 26px;
  height: 26px;
}

/* --------------------------------------------------------------------------
   7. Buttons
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: var(--green);
  --btn-fg: #fff;
  --btn-bd: var(--green);
  --btn-wipe: var(--green-deep);
  --btn-fg-hover: var(--btn-fg);
  --btn-bd-hover: var(--btn-bd);
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1.05rem 1.85rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  font-family: var(--sans);
  font-size: 0.71rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color 0.35s var(--ease), border-color 0.35s var(--ease),
    transform 0.35s var(--ease), box-shadow 0.35s var(--ease);
}

/* Negative-z pseudo paints above the background but below the label, so the
   hover colour wipes in from the left without touching the text. */
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--btn-wipe);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.5s var(--ease);
}

.btn .icon {
  width: 1.15em;
  height: 1.15em;
  transition: transform 0.35s var(--ease);
}

.btn:hover {
  color: var(--btn-fg-hover);
  border-color: var(--btn-bd-hover);
  transform: translateY(-2px);
}
.btn:hover::before {
  transform: scaleX(1);
}
.btn:hover .icon {
  transform: translateX(4px);
}
.btn:active {
  transform: translateY(1px);
}

.btn--dark:hover {
  box-shadow: 0 16px 30px -18px rgba(18, 51, 40, 0.6);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bd: var(--line);
  --btn-wipe: var(--green);
  --btn-fg-hover: #fff;
  --btn-bd-hover: var(--green);
}

.btn--gold {
  --btn-bg: var(--gold);
  --btn-fg: #fff;
  --btn-bd: var(--gold);
  --btn-wipe: #b18a52;
}
.btn--gold:hover {
  box-shadow: 0 16px 30px -18px rgba(193, 154, 99, 0.7);
}

.btn--light {
  --btn-bg: var(--cream-hi);
  --btn-fg: var(--green);
  --btn-bd: var(--cream-hi);
  --btn-wipe: var(--green-deep);
  --btn-fg-hover: #fff;
  --btn-bd-hover: var(--green-deep);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2.5rem;
}

/* Text link with arrow */
.link {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--green);
  font-size: 0.71rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.link .icon {
  width: 1.2em;
  height: 1.2em;
  transition: transform 0.35s var(--ease);
}

.link:hover .icon {
  transform: translateX(5px);
}

.dark .link,
.link--light {
  color: var(--gold-light);
}

/* --------------------------------------------------------------------------
   8. Brand lockup
   -------------------------------------------------------------------------- */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
}

.brand__mark {
  position: relative;
  display: block;
  flex: none;
  width: auto;
  height: 52px;
  transition: height 0.35s var(--ease);
}

/* The monogram artwork. Hidden until site.js confirms the file actually
   loaded, so a missing file never shows a broken-image icon. The PNG carries
   ~8% internal padding, so a slight negative margin optically aligns it
   with the wordmark. */
.brand__mark--art {
  display: none;
  width: auto;
  height: 52px;
  margin: -3px 0;
}

.brand.has-logo .brand__mark--art {
  display: block;
}

.brand.has-logo .brand__mark--type {
  display: none;
}

/* Typographic fallback lockup (used until the monogram artwork lands) */
.brand__mark--type {
  font-family: var(--serif);
  font-size: 2.45rem;
  font-weight: 400;
  line-height: 1;
  color: var(--green);
  padding-right: 0.42em;
}

.brand__mark--type::after {
  content: '';
  position: absolute;
  right: 0.06em;
  bottom: 0.14em;
  width: 0.19em;
  height: 0.19em;
  background: var(--gold);
  border-radius: 50%;
}

.brand__word {
  font-family: var(--serif);
  font-size: 1.19rem;
  font-weight: 500;
  color: var(--green);
  letter-spacing: 0.13em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

/* "For" carries the gold in the wordmark. */
.brand__word em {
  font-style: normal;
  color: var(--gold);
}

.dark .brand__mark--type,
.dark .brand__word,
.brand--light .brand__mark--type,
.brand--light .brand__word {
  color: var(--cream-hi);
}

/* The footer mark. No plate: the supplied artwork had its flat ground cut to
   transparency, so it sits straight on the green rather than needing a light
   panel to stay legible the way the old dark-ink lockup did. Taller than the
   header's, since the footer has room and this is the page's last word. */
.brand--footer {
  gap: 1rem;
}

.brand--footer .brand__mark--art {
  height: clamp(64px, 8vw, 84px);
  margin: 0;
}

.brand--footer .brand__word {
  font-size: 1.28rem;
}

/* --------------------------------------------------------------------------
   9. Header
   -------------------------------------------------------------------------- */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--cream);
  border-bottom: 1px solid rgba(0, 0, 0, 0.04);
  transition: background 0.35s var(--ease), box-shadow 0.35s var(--ease);
}

.header.is-stuck {
  background: rgba(244, 240, 232, 0.9);
  backdrop-filter: blur(10px);
  box-shadow: 0 12px 34px -26px rgba(18, 51, 40, 0.5);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding-block: 1.375rem;
  transition: padding 0.35s var(--ease);
}

/* The bar tightens as the page scrolls, and the mark shrinks with it. */
.header.is-stuck .header__inner {
  padding-block: 0.75rem;
}

.header.is-stuck .brand__mark {
  height: 42px;
}

/* Reading progress, pinned to the header's lower edge. */
.scroll-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  overflow: hidden;
}

.scroll-progress span {
  display: block;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, var(--gold), #dfc49a);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 2.4vw, 2.4rem);
}

.nav__link {
  position: relative;
  padding-block: 0.4rem;
  color: var(--ink);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}

.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--gold);
  transition: width 0.35s var(--ease);
}

.nav__link:hover::after,
.nav__link[aria-current='page']::after,
.nav__link.is-active::after {
  width: 100%;
}

.nav__link[aria-current='page'],
.nav__link.is-active {
  color: var(--green);
}

.header__cta {
  padding: 0.95rem 1.5rem;
}

/* The nav carries its own CTA for the mobile drawer; hidden on the desktop bar,
   where .header__cta sits outside the nav instead. */
.nav > .btn {
  display: none;
}

.nav-toggle {
  display: none;
  padding: 0.5rem;
  margin-right: -0.5rem;
  color: var(--green);
}

.nav-toggle .icon {
  width: 26px;
  height: 26px;
}

.nav-toggle__close {
  display: none;
}

body.nav-open .nav-toggle__open {
  display: none;
}
body.nav-open .nav-toggle__close {
  display: block;
}

@media (max-width: 960px) {
  .nav-toggle {
    display: block;
    order: 3;
  }

  /* Anchored to the header's own lower edge rather than a hard-coded offset,
     so it stays correct as the bar shrinks on scroll. */
  .nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 1rem var(--gutter) 2.5rem;
    background: var(--cream-hi);
    border-top: 1px solid var(--line-soft);
    box-shadow: 0 20px 40px -24px rgba(22, 48, 42, 0.25);
    transform: translateY(-8px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s var(--ease), transform 0.3s var(--ease),
      visibility 0.3s;
  }

  body.nav-open .nav {
    transform: none;
    opacity: 1;
    visibility: visible;
  }

  .nav__link {
    width: 100%;
    padding-block: 1.05rem;
    border-bottom: 1px solid var(--line-soft);
    font-size: 0.78rem;
  }

  .nav__link::after {
    display: none;
  }

  .header__cta {
    display: none;
  }

  .nav > .btn {
    display: flex;
    width: 100%;
    justify-content: space-between;
    margin-top: 1.75rem;
  }
}

/* --------------------------------------------------------------------------
   10. Image frames + art-direction placeholders
   -------------------------------------------------------------------------- */
.frame {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(110% 90% at 76% 10%, rgba(255, 252, 245, 0.55), transparent 62%),
    linear-gradient(148deg, #ddd3c2 0%, #c9bda6 46%, #a3947a 100%);
  isolation: isolate;
}

/* Absolutely filled rather than `width/height: 100%` in flow. Most frames take
   their height from `min-height` (the row, duo and hero media columns) rather
   than a resolved `height`, and a percentage height against a parent whose own
   height is content-derived resolves to `auto` — so the image kept its intrinsic
   height and the frame's gradient showed through beneath it, by 141px on the
   narrowest services row. Every frame gets its height from min-height,
   aspect-ratio, `--fill` or absolute positioning, never from this image, so
   taking it out of flow costs nothing. `.frame__ph` is already inset: 0. */
.frame > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focus, 50%) center;
}

/* A wide photograph in a tall frame loses roughly half its width to the crop,
   so the subject may not sit at the centre of what survives. --focus moves the
   kept region without touching the markup. */
.frame--focus-right {
  --focus: 58%;
}
.frame--focus-left {
  --focus: 42%;
}

.frame--r1 {
  aspect-ratio: 1 / 1;
}
.frame--r43 {
  aspect-ratio: 4 / 3;
}
.frame--r32 {
  aspect-ratio: 3 / 2;
}
.frame--r169 {
  aspect-ratio: 16 / 9;
}
.frame--r54 {
  aspect-ratio: 5 / 4;
}
.frame--tall {
  aspect-ratio: 4 / 5;
}
.frame--fill {
  height: 100%;
  min-height: 100%;
}

.frame--dark {
  background: linear-gradient(155deg, #1e4536 0%, #123328 48%, #0b1d18 100%);
}

/* Per-photograph placeholder tints. Each stands in for a specific shot, so the
   holding page reads as art direction rather than a row of identical blocks.
   A real file at the frame's src takes over and hides all of this. */
.frame--leaf {
  background: linear-gradient(115deg, #dfe4d3, #b9c4a5 55%, #8fa07c);
}

/* Placeholder rendered only while the photograph is absent. */
.frame__ph {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.65rem;
  padding: clamp(1.1rem, 2vw, 1.75rem);
  color: rgba(22, 48, 42, 0.66);
}

.frame.is-empty .frame__ph {
  display: flex;
}

.frame.is-empty > img {
  display: none;
}

.frame__ph::before {
  content: 'R.';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -58%);
  font-family: var(--serif);
  font-size: clamp(3.25rem, 7vw, 6.5rem);
  line-height: 1;
  color: rgba(22, 48, 42, 0.13);
  pointer-events: none;
}

.frame__label {
  position: relative;
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  line-height: 1.5;
  text-transform: uppercase;
  opacity: 0.75;
}

.frame__note {
  position: relative;
  max-width: 34ch;
  font-size: 0.78rem;
  font-weight: 300;
  line-height: 1.55;
  letter-spacing: 0.01em;
}

.frame--dark .frame__ph {
  color: rgba(235, 232, 224, 0.72);
}
.frame--dark .frame__ph::before {
  color: rgba(235, 232, 224, 0.12);
}

/* Purely decorative band imagery: keep the tone, drop the caption. */
.cta__bg .frame__label,
.cta__bg .frame__note,
.cta__leaf .frame__label,
.cta__leaf .frame__note,
.cta__bg .frame__ph::before,
.cta__leaf .frame__ph::before {
  display: none;
}

/* --------------------------------------------------------------------------
   11. Hero — split (text + full-bleed image)
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  background: var(--cream);
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  align-items: stretch;
  min-height: min(84vh, 780px);
}

.hero__copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(3rem, 7vw, 6.5rem) clamp(2rem, 5vw, 4.5rem)
    clamp(3rem, 7vw, 6.5rem) var(--gutter);
  max-width: calc(var(--maxw) / 2 + 2rem);
  margin-left: auto;
}

.hero__media {
  position: relative;
}

.hero__media .frame {
  height: 100%;
  min-height: 340px;
}

.hero--reverse .hero__grid > .hero__copy {
  order: 2;
  margin-left: 0;
  margin-right: auto;
  padding-inline: clamp(2rem, 5vw, 4.5rem) var(--gutter);
}

.hero--reverse .hero__grid > .hero__media {
  order: 1;
}

/* Shorter hero for interior pages carrying a single paragraph of intro copy */
.hero--compact .hero__grid {
  min-height: min(58vh, 520px);
}

/* Hero variant: copy overlays a wide image */
.hero--overlay .hero__grid {
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.15fr);
  min-height: min(80vh, 720px);
}

/* --------------------------------------------------------------------------
   11b. Hero — full-bleed video with the copy centred on top
   -------------------------------------------------------------------------- */
.hero--video {
  display: grid;
  place-items: center;
  min-height: min(88vh, 840px);
  /* Deep green stands in until the first frame paints, so the band never
     flashes cream behind white type. */
  background: var(--green-deep);
  overflow: hidden;
}

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

.hero__video video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Scrim: the footage carries a pale sky, so the type has to hold on the
   brightest frame of the loop, not the average one. The core sits under the
   copy and feathers out, which keeps the ridgeline and canopy readable while
   every line above it clears its contrast target. */
.hero__video::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse 82% 74% at 50% 52%,
      rgba(9, 25, 21, 0.8) 0%,
      rgba(9, 25, 21, 0.55) 60%,
      rgba(9, 25, 21, 0.06) 100%
    ),
    linear-gradient(
      to bottom,
      rgba(13, 38, 32, 0.62) 0%,
      rgba(13, 38, 32, 0.36) 45%,
      rgba(13, 38, 32, 0.7) 100%
    );
}

.hero--video .hero__stage {
  position: relative;
  z-index: 1;
  max-width: 980px;
  padding-block: clamp(4rem, 10vw, 7rem);
  text-align: center;
}

/* Gold on cream carries the eyebrow everywhere else, but over footage this
   size it cannot reach 4.5:1 without flattening the video into a dark plate.
   White does, and the gold stays where it matters — on "opportunity." */
.hero--video .eyebrow {
  color: var(--on-dark);
}

.hero--video .display {
  text-shadow: 0 2px 34px rgba(9, 25, 21, 0.45);
}

.hero--video .lede {
  max-width: 54ch;
  margin-inline: auto;
}

.hero--video .btn-row {
  justify-content: center;
}

@media (max-width: 900px) {
  .hero--video {
    min-height: min(80vh, 640px);
  }

  .hero__grid,
  .hero--overlay .hero__grid {
    grid-template-columns: 1fr;
    min-height: 0;
  }

  .hero__copy,
  .hero--reverse .hero__grid > .hero__copy {
    order: 1;
    max-width: none;
    margin: 0;
    padding: clamp(2.75rem, 9vw, 4rem) var(--gutter);
  }

  .hero__media,
  .hero--reverse .hero__grid > .hero__media {
    order: 2;
  }

  .hero__media .frame {
    aspect-ratio: 16 / 11;
    min-height: 0;
  }
}

/* --------------------------------------------------------------------------
   12. Service cards (3-up)
   -------------------------------------------------------------------------- */
.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.625rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

/* Four services across on a wide screen, then a 2x2 block before stacking —
   four columns below ~1140px leaves too little room for the serif titles. */
.cards--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem;
}

.cards--4 .card {
  padding: 2.25rem 1.75rem 2rem;
}

@media (max-width: 1140px) {
  .cards--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.625rem;
  }
  .cards--4 .card {
    padding: 2.5rem 2.25rem 2.25rem;
  }
}

.card {
  display: flex;
  flex-direction: column;
  padding: 2.5rem 2.25rem 2.25rem;
  background: #fff;
  border: 1px solid var(--line);
  transition: border-color 0.25s var(--ease), translate 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}

/* `translate`, not a transform — the scroll reveal owns transform on this
   element. See the note in section 22. */
.card:hover {
  border-color: var(--gold);
  translate: 0 -3px;
  box-shadow: 0 18px 40px -28px rgba(18, 51, 40, 0.35);
}

.card .icon--lg {
  margin-bottom: 1.875rem;
}

/* .icon--lg is itself the <svg>, so the transition belongs on it directly. */
.card .icon--lg {
  transition: transform 0.45s var(--ease);
}

.card:hover .icon--lg {
  transform: translateY(-4px);
}

/* Gold rule draws in under the title on hover. */
.card__title {
  position: relative;
  display: inline-block;
  margin-bottom: 1rem;
  font-family: var(--serif);
  font-size: 1.375rem;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.3;
}

.card__title::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.45s var(--ease);
}

.card:hover .card__title::after {
  transform: scaleX(1);
}

.card__body {
  margin-bottom: 1.75rem;
  color: var(--muted);
  font-size: 0.9375rem;
  line-height: 1.75;
}

.card .link {
  margin-top: auto;
}

@media (max-width: 900px) {
  .cards {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------------------
   13. Principles / value columns
   -------------------------------------------------------------------------- */
.split-heading {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 2.15fr);
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: start;
}

.pillars {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  row-gap: 3.75rem;
}

/* Two-across variant (the contact page's next steps). Declared before the
   responsive rules below so those still collapse it — an inline
   grid-template would not. */
.pillars--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Every pillar carries a divider, cleared on the first of each row. The
   row-start selector tracks the column count at each breakpoint below. */
.pillar {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: 0 1.875rem;
  border-left: 1px solid var(--line);
}

.pillar:nth-child(5n + 1) {
  padding-left: 0;
  border-left: 0;
}

.pillar .icon--lg {
  color: var(--green);
  transition: transform 0.4s var(--ease);
}

.pillar:hover .icon--lg {
  transform: translateY(-3px);
}

.pillar__title {
  font-family: var(--serif);
  font-size: 1.3125rem;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.28;
}

.pillar__body {
  max-width: 25ch;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.75;
}

.dark .pillar {
  border-left-color: var(--on-dark-line);
}
.dark .pillar__title {
  color: var(--on-dark);
}
/* .pillar__body sets --muted directly, which would otherwise beat the colour
   .dark passes down and leave dark grey text on dark green. */
.dark .pillar__body {
  color: var(--on-dark-muted);
}

/* Compact variant — icon + two-line label, no body copy */
.pillars--compact {
  align-items: center;
  text-align: center;
}

.pillars--compact .pillar {
  align-items: center;
  gap: 1.35rem;
  padding-block: 0.5rem;
}

.pillars--compact .pillar__title {
  font-size: 1.12rem;
}

/* 5 → 3 columns: restore the divider on the old row-start, clear the new. */
@media (max-width: 1200px) {
  .pillars {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .pillar:nth-child(5n + 1) {
    padding-left: 1.875rem;
    border-left: 1px solid var(--line);
  }
  .pillar:nth-child(3n + 1) {
    padding-left: 0;
    border-left: 0;
  }
}

@media (max-width: 1000px) {
  .split-heading {
    grid-template-columns: 1fr;
  }
}

/* 3 → 2 columns */
@media (max-width: 860px) {
  .pillars {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 3.25rem;
  }
  .pillar:nth-child(3n + 1) {
    padding-left: 1.875rem;
    border-left: 1px solid var(--line);
  }
  .pillar:nth-child(odd) {
    padding-left: 0;
    border-left: 0;
  }
}

/* A two-item row must not inherit the generic 3-column step above, which would
   leave an empty third column. Declared after those media queries so it wins,
   and before the single-column rule below so that still takes over. */
@media (min-width: 601px) {
  .pillars--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Single column: no dividers at all. `.pillars .pillar` matches the
   specificity of the nth-child rules above and wins on source order. */
@media (max-width: 600px) {
  .pillars {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 2.625rem;
  }
  .pillars .pillar {
    padding: 0;
    border-left: 0;
  }
}

/* Tiled variant (the homepage's "Why RankForImpact", on dark green).
   Each column becomes a card in its own right, so the dividers and their
   per-breakpoint row-start bookkeeping above are all replaced by a gap. These
   rules sit after every one of those so they win on source order at each
   breakpoint — a plain `.pillars--tiles .pillar` ties on specificity with
   `.pillars .pillar`, and later wins.

   Flex rather than the grid the other variants use, so a short row centres
   instead of being anchored left with a hole at its end. Six tiles happen to
   divide evenly at every breakpoint here, but that is a property of the content,
   not of the layout: `justify-content: center` is what kept five readable (three
   then two, and two then two then one once it dropped to pairs) and it is what
   will absorb a seventh. Do not swap this back to a grid on the strength of the
   count being even today. */
.pillars--tiles {
  --tile-gap: 1.25rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--tile-gap);
}

/* Three across. `flex: 0 1 <basis>` rather than a grow factor, so a short row
   keeps its tiles the same width as a full one instead of stretching them. */
.pillars--tiles .pillar {
  flex: 0 1 calc((100% - 2 * var(--tile-gap)) / 3);
  padding: 2.5rem 1.875rem 2.25rem;
  /* Lifted just off the green rather than a flat panel, so the tile reads as
     raised without introducing a fourth surface colour. */
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--on-dark-line);
  border-top: 2px solid var(--gold);
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease),
    translate 0.25s var(--ease);
}

/* Gold already owns the top edge; hover carries it around the other three.
   `translate` rather than a transform: see the note in section 22. */
.pillars--tiles .pillar:hover {
  background: rgba(255, 255, 255, 0.07);
  border-color: var(--gold);
  translate: 0 -3px;
}

/* `.pillar .icon--lg` sets the green used on cream and would otherwise beat
   the gold `.dark .icon--lg` passes down, leaving dark green on dark green. */
.pillars--tiles .pillar .icon--lg {
  color: var(--gold-light);
}

/* The gold rule the service cards draw under their title on hover. It spans
   the title box rather than the text, exactly as `.card__title::after` does —
   a flex item is stretched to the column width whatever its display value. */
.pillars--tiles .pillar__title {
  position: relative;
}

.pillars--tiles .pillar__title::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.45s var(--ease);
}

.pillars--tiles .pillar:hover .pillar__title::after {
  transform: scaleX(1);
}

/* The 25ch cap on `.pillar__body` was set for the borderless 5-column grid,
   where a column was narrower than the cap and it never bit. Both wide variants
   are broader than 25ch, so the cap stopped every caption short — around 60% of
   a tile's inner width, barely half a column on the two-up — and left a ragged
   void down the right of each one. Let the column set the measure: 35ch in a
   tile, 46ch on the two-up, both comfortable for copy this short. */
.pillars--tiles .pillar__body,
.pillars--2 .pillar__body {
  max-width: none;
}

/* Two across, leaving the fifth tile centred on its own row. */
@media (max-width: 860px) {
  .pillars--tiles .pillar {
    flex-basis: calc((100% - var(--tile-gap)) / 2);
  }
}

@media (max-width: 600px) {
  .pillars--tiles .pillar {
    flex-basis: 100%;
  }
}

/* --------------------------------------------------------------------------
   Mobile: a collapsed grid reads as one centred stack rather than a column of
   left-aligned fragments. Section heads come with it, so alignment does not
   change halfway down a section.
   -------------------------------------------------------------------------- */
@media (max-width: 600px) {
  .pillars .pillar,
  .cards .card {
    align-items: center;
    text-align: center;
  }

  /* The two variant selectors are listed so this ties with the uncapped rule
     above and wins on source order. Once the text is centred it needs a measure
     again, or a full-width column sets lines too long to track back. */
  .pillar__body,
  .pillars--tiles .pillar__body,
  .pillars--2 .pillar__body,
  .card__body {
    max-width: 36ch;
    margin-inline: auto;
  }
}

/* --------------------------------------------------------------------------
   14. Alternating feature rows (services / approach steps)
   -------------------------------------------------------------------------- */
.rows {
  display: flex;
  flex-direction: column;
}

.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 0;
}

/* The row centres its items so the copy sits vertically centred, but that left
   this column at its own content height — so the frame's `height: 100%` had
   nothing definite to resolve against and the photograph stopped at its
   min-height, banding cream above and below it by up to 188px. Stretching just
   this item gives the frame the full row height while the copy stays centred.
   `.duo` and `.hero__grid` already stretch, which is why only rows showed it. */
.row__media {
  position: relative;
  align-self: stretch;
  min-height: 100%;
}

.row__media .frame {
  height: 100%;
  min-height: clamp(260px, 34vw, 420px);
}

.row__copy {
  display: flex;
  gap: clamp(1.25rem, 2.5vw, 2.25rem);
  padding: clamp(2.5rem, 5vw, 4.5rem) clamp(1.5rem, 4vw, 4rem);
}

.row__aside {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  flex: none;
}

.row__num {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--gold-ink);
  letter-spacing: 0.16em;
}

/* Doubled class so this outranks the `.row__main p` size rule. */
.row__num.row__num--display {
  font-family: var(--serif);
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1;
}

.row__main {
  min-width: 0;
}

.row__main > * + * {
  margin-top: 1.1rem;
}

/* Each service closes on its own booking trigger. It needs more air than the
   1.1rem between paragraphs above, or it reads as another line of body copy. */
.row__cta {
  margin-top: 1.875rem;
}

.row__main .h3 {
  margin-bottom: 0.35rem;
}

.row__main p {
  font-size: 0.93rem;
  line-height: 1.78;
  max-width: 52ch;
}

.row__main strong {
  color: var(--ink);
  font-weight: 600;
}

/* Row layout: media right (default alternates via nth-child) */
.row:nth-child(even) .row__media {
  order: 1;
}
.row:nth-child(even) .row__copy {
  order: 2;
}

/* Icon and step number read as one horizontal cluster beside the heading. */
.row__aside {
  flex-direction: row;
  align-items: flex-start;
}

.row--stepped .row__aside {
  flex-direction: column;
  align-items: center;
}

/* Vertical connector line for numbered approach steps */
.row--stepped .row__aside::after {
  content: '';
  flex: 1;
  width: 1px;
  min-height: 60px;
  background: var(--line);
}

@media (max-width: 900px) {
  .row {
    grid-template-columns: 1fr;
  }
  .row__media,
  .row:nth-child(even) .row__media {
    order: 1;
  }
  .row__copy,
  .row:nth-child(even) .row__copy {
    order: 2;
  }
  .row__media .frame {
    aspect-ratio: 16 / 10;
    min-height: 0;
  }
  .row__copy {
    padding-inline: var(--gutter);
  }
  .row--stepped .row__aside::after {
    display: none;
  }
  .row__aside {
    flex-direction: row;
    align-items: center;
    gap: 1.1rem;
  }
}

/* --------------------------------------------------------------------------
   15. Two-up split (image + copy, e.g. About mission / approach)
   -------------------------------------------------------------------------- */
.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
}

.duo__copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(3rem, 6vw, 5.5rem) clamp(2rem, 4vw, 4rem);
}

.duo__copy--start {
  padding-left: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
}

.duo__copy--end {
  padding-right: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
}

.duo__media .frame {
  height: 100%;
  min-height: clamp(300px, 40vw, 520px);
}

@media (max-width: 900px) {
  .duo {
    grid-template-columns: 1fr;
  }
  .duo__copy {
    padding: clamp(2.75rem, 9vw, 4rem) var(--gutter);
  }
  .duo__copy--start,
  .duo__copy--end {
    padding-inline: var(--gutter);
  }
  .duo--media-first .duo__media {
    order: -1;
  }
  .duo__media .frame {
    aspect-ratio: 16 / 11;
    min-height: 0;
  }
}

/* --------------------------------------------------------------------------
   16. Quote band
   -------------------------------------------------------------------------- */
.quote {
  padding-block: clamp(3.5rem, 7vw, 6rem);
  text-align: center;
  background: var(--green-deep);
}

.quote .icon {
  width: 30px;
  height: 30px;
  margin: 0 auto 1.5rem;
  color: var(--gold-light);
}

.quote blockquote {
  font-family: var(--serif);
  font-size: clamp(1.7rem, 3.6vw, 2.9rem);
  font-weight: 300;
  color: var(--on-dark);
  line-height: 1.22;
  text-wrap: balance;
}

.quote blockquote em {
  color: var(--on-dark);
}

.quote__rule {
  width: 1px;
  height: 44px;
  margin: 2rem auto 0;
  background: var(--on-dark-line);
}

/* --------------------------------------------------------------------------
   17. CTA bands
   -------------------------------------------------------------------------- */
.cta {
  position: relative;
  overflow: hidden;
  background: var(--cream);
}

/* .cta is declared after .dark and both are one class deep, so the cream would
   otherwise win and leave a dark band's white text on a cream ground. */
.cta.dark {
  background: var(--green);
}

.cta__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.75rem, 4vw, 3.5rem);
  padding-block: clamp(2.75rem, 5vw, 4rem);
}

.cta__copy {
  min-width: min(100%, 26ch);
  flex: 1;
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}

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

.cta__bg .frame {
  height: 100%;
}

.cta--image .cta__inner {
  padding-block: clamp(4rem, 9vw, 7.5rem);
}

.cta--image::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
    100deg,
    rgba(13, 31, 26, 0.92) 0%,
    rgba(13, 31, 26, 0.78) 45%,
    rgba(13, 31, 26, 0.42) 100%
  );
}

/* Split CTA: heading block + copy + action, divided by a rule */
.cta--triple .cta__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.95fr) auto;
  gap: clamp(1.75rem, 3.5vw, 3rem);
  align-items: center;
}

.cta--triple .cta__mid {
  padding-left: clamp(1.5rem, 3vw, 2.5rem);
  border-left: 1px solid var(--on-dark-line);
  font-size: 0.93rem;
  line-height: 1.8;
}

/* Its own line. `.cta__actions` is a flex row, sized for buttons sitting side
   by side, and this note is not a button — without a full basis it lands beside
   the trigger instead of under it. */
.cta__foot {
  flex-basis: 100%;
  margin-top: 0.9rem;
  font-size: 0.84rem;
  line-height: 1.6;
}

/* Foliage bled into the left edge of the light CTA band, faded out with a mask
   so it reads as texture rather than a hard-edged panel. */
/* The only CTA band that is not dark, and on the homepage it now follows the
   beige mission section — same cream, so without a rule the two run together as
   one undifferentiated stretch. The foliage bleed alone does not carry the
   seam, since it only covers the left 230px. */
.cta--leaf {
  border-top: 1px solid var(--line);
}

.cta--leaf .cta__inner {
  padding-block: 3.125rem;
}

.cta__leaf {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 0;
  width: 230px;
  opacity: 0.85;
  -webkit-mask-image: linear-gradient(90deg, #000 55%, transparent);
  mask-image: linear-gradient(90deg, #000 55%, transparent);
}

.cta__leaf .frame {
  height: 100%;
}

.cta--leaf .cta__copy {
  margin-left: 180px;
}

.cta--leaf .cta__copy .h2 {
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: 0.5rem;
}

@media (max-width: 900px) {
  .cta--leaf .cta__inner {
    flex-direction: column;
    align-items: flex-start;
    padding-block: 3.5rem;
  }
  .cta--leaf .cta__copy {
    margin-left: 0;
  }
  .cta__leaf {
    display: none;
  }
}

@media (max-width: 900px) {
  .cta--triple .cta__inner {
    grid-template-columns: 1fr;
  }
  .cta--triple .cta__mid {
    padding-left: 0;
    border-left: 0;
  }
}

/* --------------------------------------------------------------------------
   18. Insights cards
   -------------------------------------------------------------------------- */
.posts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: clamp(1.75rem, 3vw, 2.75rem);
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

.post {
  display: flex;
  flex-direction: column;
  background: var(--cream-hi);
  border: 1px solid var(--line-soft);
  transition: translate 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.post:hover {
  translate: 0 -5px;
  box-shadow: 0 26px 50px -32px rgba(22, 48, 42, 0.4);
}

.post__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 0.9rem;
  padding: clamp(1.5rem, 2.5vw, 2rem);
}

.post__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.6rem;
  color: var(--gold-ink);
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* Any child, not just a span — the date is a <time>, and keying the separator
   to spans skipped it.

   Trailing rather than leading, which matters once there are three items: at a
   card's width the line wraps, and a leading separator starts the second line
   with a stranded dot. Trailing leaves it at the end of the first line, where it
   reads as intended. */
.post__meta > *:not(:last-child)::after {
  content: '·';
  margin-left: 0.6rem;
  color: var(--line);
}

.post__title {
  font-family: var(--serif);
  font-size: 1.32rem;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.28;
}

.post__excerpt {
  flex: 1;
  font-size: 0.9rem;
  line-height: 1.7;
}

/* --------------------------------------------------------------------------
   19. Booking embed (Cal.com — contact page)
   -------------------------------------------------------------------------- */

/* The booker opens in a Cal-hosted modal on click, so this panel only has to
   present the invitation — no height is reserved for an inline widget. */
.booker {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  padding: clamp(2.25rem, 5vw, 3.5rem) clamp(1.5rem, 4vw, 3rem);
  background: var(--cream);
  border: 1px solid var(--line);
  text-align: center;
}

/* Holding the calendar rather than a button, so the panel gives up most of its
   side padding — the month view needs the width more than the frame does. */
.booker--inline {
  padding: clamp(0.75rem, 2vw, 1.25rem) clamp(0.5rem, 1.5vw, 1rem)
    clamp(1.5rem, 3vw, 2rem);
}

/* Cal measures its own iframe and sets the height, so nothing is fixed here.
   min-height only reserves room so the page does not jump when the embed
   arrives, and there is deliberately no overflow: scroll — the booker scrolls
   inside itself, and a second scrollbar around it fights the first. */
.booker__embed {
  width: 100%;
  min-height: 620px;
}

@media (max-width: 600px) {
  .booker__embed {
    min-height: 520px;
  }
}

.booker__note {
  max-width: 34ch;
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.7;
}

.booker__note a {
  color: var(--green);
  border-bottom: 1px solid var(--line);
  transition: border-color 0.3s var(--ease);
}

.booker__note a:hover {
  border-bottom-color: var(--gold);
}

/* Reassurances under the two next steps. The section exists to answer "what
   happens to my details", so the answer is stated outright rather than implied.
   Ticks run green, not the usual gold: gold on this band measures 2.29:1, and
   these are meant to be read rather than admired. */
.reassure {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem 2.25rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: clamp(1.75rem, 3vw, 2.25rem);
  border-top: 1px solid var(--line);
}

.reassure li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.reassure .icon {
  width: 1.05em;
  height: 1.05em;
  color: var(--green);
}

/* --------------------------------------------------------------------------
   19b. Blog post — photograph behind the title, then the piece
   --------------------------------------------------------------------------
   Built on the homepage's full-bleed hero rather than beside it: same centred
   stage, same scrim idea, an <img> where that one has a <video>.

   The photograph is a crop of the part of the image carrying no words. Every one
   of these flat-lays has the article's own title printed on a card in the middle
   of the frame, and the H1 lands exactly there. Scrimming does not solve it —
   darkening drops the whole band's luminance without touching the local contrast
   between dark type and its pale card, so the printed title stays readable at
   any strength that still leaves a photograph. Cropping is what solves it, and
   the crops live in assets/img/post-*.jpg. */
.hero--post {
  display: grid;
  place-items: center;
  min-height: min(72vh, 660px);
  background: var(--green-deep);
  overflow: hidden;
}

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

.hero__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Lighter than the homepage's, and it can be: these crops are soft plaster,
   ceramic and leaf shadow with no bright sky to hold type against. Still a
   feathered core under the copy rather than a flat wash, so the corners keep
   some of the photograph. */
.hero__shot::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse 78% 72% at 50% 50%,
      rgba(9, 25, 21, 0.74) 0%,
      rgba(9, 25, 21, 0.5) 62%,
      rgba(9, 25, 21, 0.1) 100%
    ),
    linear-gradient(
      to bottom,
      rgba(13, 38, 32, 0.5) 0%,
      rgba(13, 38, 32, 0.34) 45%,
      rgba(13, 38, 32, 0.62) 100%
    );
}

.hero--post .hero__stage {
  position: relative;
  z-index: 1;
  max-width: 900px;
  padding-block: clamp(3.5rem, 9vw, 6rem);
  text-align: center;
}

/* A post title is prose, not a slogan, so it sets smaller than the homepage's
   display line and takes a reading-length measure. */
.hero--post .display {
  font-size: clamp(2rem, 4.4vw, 3.35rem);
  line-height: 1.1;
  max-width: 30ch;
  margin-inline: auto;
  text-shadow: 0 2px 34px rgba(9, 25, 21, 0.45);
}

.hero--post .lede {
  max-width: 56ch;
  margin: 1.5rem auto 0;
}

.hero--post .btn-row {
  justify-content: center;
  margin-top: 2rem;
}

/* White, not the gold the cards use, for the same reason the homepage hero's
   eyebrow is white: at this size gold over a photograph only just clears 4.5:1
   on the palest of these crops (measured 4.56:1), and that is too thin a margin
   to rest on. The gold stays on the cards, where the ground is cream. */
.post__meta--hero {
  justify-content: center;
  margin-bottom: 1.5rem;
  color: var(--on-dark);
}

.post__meta--hero > *:not(:last-child)::after {
  color: rgba(255, 255, 255, 0.4);
}

/* Reading measure. Narrower than .wrap--narrow, which is sized for headings and
   two-up blocks rather than a column of body copy. */
.wrap--reading {
  max-width: 760px;
}

.article-band {
  background: var(--cream-hi);
}

.article > * + * {
  margin-top: 1.35rem;
}

.article p {
  color: var(--body);
  font-size: 1.0625rem;
  line-height: 1.78;
}

.article h2 {
  margin-top: 2.75rem;
  font-family: var(--serif);
  font-size: clamp(1.5rem, 2.6vw, 1.95rem);
  font-weight: 300;
  line-height: 1.22;
  letter-spacing: -0.005em;
  color: var(--ink);
}

/* These posts lead many sections with a bolded thesis sentence, so strong is
   doing structural work and gets the brand's ink rather than extra weight
   piled on the serif. */
.article strong {
  font-weight: 500;
  color: var(--ink);
}

/* A short gold rule instead of a bullet — the same marker the rest of the site
   uses to open a thought, rather than a disc borrowed from the browser. */
.article ul {
  padding-left: 1.35rem;
  list-style: none;
}

.article li {
  position: relative;
  color: var(--body);
  font-size: 1.0625rem;
  line-height: 1.78;
}

.article li + li {
  margin-top: 0.65rem;
}

.article li::before {
  content: '';
  position: absolute;
  left: -1.35rem;
  top: 0.85em;
  width: 8px;
  height: 1px;
  background: var(--gold);
}

.article a {
  color: var(--green);
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color 0.2s var(--ease), text-decoration-color 0.2s var(--ease);
}

.article a:hover {
  color: var(--gold-ink);
  text-decoration-color: var(--gold-ink);
}

.article__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid var(--line);
}

.article__by {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

@media (max-width: 900px) {
  .hero--post {
    min-height: min(66vh, 540px);
  }
}

/* --------------------------------------------------------------------------
   20. Footer
   -------------------------------------------------------------------------- */
.footer {
  padding-top: clamp(3rem, 6vw, 4.5rem);
  background: var(--green);
  color: var(--on-dark-muted);
  font-size: 0.87rem;
}

/* The lockup, then how to reach us, then the rule. Centred: with the link
   columns gone, a left-aligned pair sat in the first third and left the rest of
   the footer empty. */
.footer__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.75rem, 4vw, 2.5rem);
  padding-bottom: clamp(2.5rem, 5vw, 3.5rem);
  text-align: center;
}

.footer__contact a:hover {
  color: var(--gold-light);
}

.footer__contact li {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
}

.footer__contact li + li {
  margin-top: 0.9rem;
}

.footer__contact--row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.9rem 2.5rem;
}

/* Cancels the stacked spacing above: side by side they need none, and a wrapped
   row gets its spacing from the flex gap instead. */
.footer__contact--row li + li {
  margin-top: 0;
}

.footer__contact .icon {
  width: 1.15em;
  height: 1.15em;
  margin-top: 0.3em;
  color: var(--gold-light);
}

/* LinkedIn's mark, set in the body face rather than drawn: Simple Icons no
   longer distributes the logo, and the site draws no third-party brand artwork
   of its own. Sized and coloured to sit in the same column as the mail and
   map-pin icons beside it, so the three read as one list. */
.footer__in {
  flex: none;
  /* 1.15em to match the icons beside it — and no font-size of its own, or that
     em would resolve against the smaller size and leave the glyph a pixel
     narrower than they are, nudging its label out of line. */
  width: 1.15em;
  margin-top: 0.3em;
  color: var(--gold-light);
  font-family: var(--sans);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-align: center;
}

.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem 1.75rem;
  padding-block: 1.6rem;
  border-top: 1px solid var(--on-dark-line);
  font-size: 0.76rem;
  letter-spacing: 0.04em;
}

.footer__bar a:hover {
  color: var(--gold-light);
}

/* --------------------------------------------------------------------------
   20b. Interaction — the homepage's hover language, applied site-wide
   The vocabulary established on the homepage is: photography scales gently,
   circled icons fill gold, and a gold rule draws in under the title. These
   rules carry it to the components that only appear on the other pages.
   -------------------------------------------------------------------------- */

.frame {
  transition: transform 0.7s var(--ease);
}

/* A frame clips its own contents, so scaling it needs the parent to clip. */
.row__media,
.duo__media,
.post {
  overflow: hidden;
}

/* A feature row is one interactive unit — hovering its copy moves its image.
   The about-page splits are editorial rather than card-like, so there only the
   image itself responds. */
.row:hover .row__media .frame,
.duo__media:hover .frame,
.post:hover .frame {
  transform: scale(1.04);
}

.icon-ring {
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease),
    color 0.4s var(--ease);
}

.row:hover .icon-ring {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--green);
}

.row:hover .icon--lg,
.post:hover .icon--lg {
  transform: translateY(-4px);
}

/* Row and article titles take the gold rule the service cards use. */
.row__main .h3,
.post__title {
  position: relative;
  display: inline-block;
}

.row__main .h3::after,
.post__title::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.45s var(--ease);
}

.row:hover .row__main .h3::after,
.post:hover .post__title::after {
  transform: scaleX(1);
}

.post:hover {
  border-color: var(--gold);
}


/* --------------------------------------------------------------------------
   21. Ripple diagram — one business, the effects that follow
   Two layouts from one block of markup. By default — small screens, no
   JavaScript, print — the branches are a plain stacked list with every step
   visible. From 900px up, the diagram claims the same list as labels pinned
   over a canvas that draws the curves between them. That layout is gated on
   .ripple--on, which site.js sets only once it holds a drawable canvas, so a
   script that never runs leaves the readable list rather than pinned labels
   with nothing behind them. The drawing is decoration only — no wording lives
   inside the canvas.
   -------------------------------------------------------------------------- */
.ripple-band {
  /* One local token for the band colour: the branch labels sit on plates of
     the same colour so their text stays readable where a curve passes behind
     it, and a plate that drifted from the band would show as a patch. */
  --band: var(--cream-lo);
  background: var(--band);
  border-top: 1px solid var(--line-soft);
  /* The fan's lowest label lands at roughly 84% of the diagram's height, so the
     diagram already ends with about 100px of clear air inside itself. A full
     section stride under that read as a hole rather than breathing room, so the
     bottom padding is cut and the diagram's own tail serves as the margin.
     Trimming the diagram's height instead would not have helped: the labels are
     placed as percentages of it, so the empty share stays the same. */
  padding-bottom: clamp(1rem, 2vw, 1.75rem);
}

.ripple {
  position: relative;
  margin-top: clamp(2rem, 4vw, 3.25rem);
}

/* Hidden until the diagram layout takes over, so the stacked list is never
   pushed down by an empty canvas. */
.ripple__canvas {
  display: none;
  width: 100%;
  height: 100%;
}

.ripple__origin {
  position: relative;
  display: inline-block;
  margin-bottom: 1.75rem;
  padding-left: 1.15rem;
  color: var(--ink);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  line-height: 1.6;
  text-transform: uppercase;
}

.ripple__origin::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 8px;
  height: 8px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--green);
}

.ripple__branches {
  display: grid;
  gap: 1.75rem 2.5rem;
}

@media (min-width: 560px) {
  .ripple__branches {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.ripple__branch {
  position: relative;
  padding-left: 1.15rem;
  border-left: 1px solid var(--line);
}

.ripple__branch::before {
  content: '';
  position: absolute;
  left: -3px;
  top: 0.55rem;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold);
}

.ripple__name {
  display: block;
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.2rem;
  line-height: 1.3;
  transition: color 0.4s var(--ease);
}

.ripple__branch:hover .ripple__name,
.ripple__branch:focus-visible .ripple__name {
  color: var(--gold-ink);
}

.ripple__steps {
  margin-top: 0.35rem;
}

.ripple__step {
  color: var(--body);
  font-size: 0.8rem;
  line-height: 1.65;
}

/* -- Diagram layout -------------------------------------------------------- */
/* Shared between the two orientations. Everything that differs — which way the
   fan spreads, which endpoint each branch uses, and where its labels sit — is
   in the two blocks below. --orient is how site.js learns which one is live, so
   the breakpoint is written once, here in the stylesheet. */
/* The diagram carries no rules bracketing it: the fan is the only thing in the
   section below its heading, so it needs no frame to read as one figure. */

.ripple--on .ripple__canvas {
  display: block;
}

/* The canvas draws the origin node itself, so the label only names it. Every
   label in the diagram — this one, the six names, the eighteen effects — is put
   where site.js measured room for it: --lx/--ly are the offset from the point
   the label names, chosen to clear the other labels and the curves. */
.ripple--on .ripple__origin {
  position: absolute;
  left: var(--ox, 8%);
  top: var(--oy, 50%);
  margin: 0;
  padding: 0 0.5rem;
  background: radial-gradient(closest-side, var(--band) 62%, transparent 100%);
  white-space: nowrap;
  transform: translate(calc(var(--lx, 0px) - 50%), calc(var(--ly, 0px) - 50%));
}

.ripple--on .ripple__origin::before {
  display: none;
}

/* The list covers the whole drawing, so it hands pointer events back to
   everything underneath and only the labels themselves take them. */
.ripple--on .ripple__branches {
  position: absolute;
  inset: 0;
  display: block;
  gap: 0;
  pointer-events: none;
}

/* The branch collapses to its endpoint: a zero-size anchor every label on the
   branch is placed against, in the canvas's own coordinates. The endpoint
   itself is set per orientation and read back by site.js for the curve, so a
   label and its curve cannot drift apart. */
.ripple--on .ripple__branch {
  position: absolute;
  width: 0;
  height: 0;
  padding: 0;
  background: none;
  border: 0;
  opacity: 0;
  transition: opacity 0.8s var(--ease) calc(var(--i, 0) * 130ms + 350ms);
}

.ripple--on .ripple__branch::before {
  display: none;
}

.ripple--on.is-live .ripple__branch {
  opacity: 1;
}

/* The branch being read comes forward, so its effects sit over the rest of the
   fan rather than tangling with it — each label carries a plate in the band's
   own colour, which is what hides the line and any neighbour behind the
   words. */
.ripple--on .ripple__branch:hover,
.ripple--on .ripple__branch:focus-within {
  z-index: 2;
}

/* The name is the hover target, and the only part of the branch with a box. It
   carries on the way its branch was heading, as far as there is room to. */
.ripple--on .ripple__name {
  position: absolute;
  left: 0;
  top: 0;
  width: max-content;
  padding: 0.25rem 0.5rem;
  background: radial-gradient(closest-side, var(--band) 62%, transparent 100%);
  text-align: center;
  pointer-events: auto;
  transform: translate(calc(var(--lx, 0px) - 50%), calc(var(--ly, 0px) - 50%));
  transition: color 0.4s var(--ease), opacity 0.4s var(--ease);
}

/* While one branch is traced the others step back. site.js sets is-tracing,
   since it is the thing that knows which branch the pointer or the keyboard
   is on. */
.ripple--on.is-tracing .ripple__name {
  opacity: 0.38;
}

.ripple--on.is-tracing .ripple__branch:hover .ripple__name,
.ripple--on.is-tracing .ripple__branch:focus-within .ripple__name {
  opacity: 1;
}

/* The anchor has no box to ring, so focus shows on the name instead. */
.ripple--on .ripple__branch:focus-visible {
  outline: 0;
}

.ripple--on .ripple__branch:focus-visible .ripple__name {
  outline: 2px solid var(--gold-ink);
  outline-offset: 3px;
}

/* The three effects are not a list here — each one names a node along its
   branch, set beside the curve rather than on it. */
.ripple--on .ripple__steps {
  margin: 0;
}

.ripple--on .ripple__step {
  position: absolute;
  left: 0;
  top: 0;
  width: max-content;
  padding: 0.1rem 0.4rem;
  background: radial-gradient(closest-side, var(--band) 62%, transparent 100%);
  color: var(--muted);
  line-height: 1.5;
  opacity: 0;
  pointer-events: none;
  transform: translate(calc(var(--lx, 0px) - 50%), calc(var(--ly, 0px) - 50%));
  transition: opacity 0.45s var(--ease);
}

/* Lit in the order they happen, matching the rings the canvas sends out from
   the same three points. */
.ripple--on .ripple__branch:hover .ripple__step,
.ripple--on .ripple__branch:focus-visible .ripple__step {
  opacity: 1;
}

.ripple--on .ripple__step:nth-child(2) {
  transition-delay: 0.12s;
}

.ripple--on .ripple__step:nth-child(3) {
  transition-delay: 0.24s;
}

/* -- Wide: the fan spreads to the right ------------------------------------ */
@media (min-width: 900px) {
  /* The floor is set by the labels, not the drawing: the highest node sits at
     12% of the band and hangs its name above itself, which wants some 50px of
     clear air. */
  .ripple--on {
    --orient: landscape;
    height: clamp(580px, 46vw, 640px);
  }

  .ripple--on .ripple__branch {
    left: var(--x);
    top: var(--y);
  }

  .ripple--on .ripple__name {
    max-width: 11rem;
  }

  .ripple--on .ripple__step {
    max-width: 9rem;
    font-size: 0.72rem;
    white-space: nowrap;
  }
}

/* -- Narrow: the same fan, turned to spread downward ----------------------- */
/* A phone has height to spare and no width, so the diagram rotates rather than
   falling back to a list: the origin moves to the top, the branches descend
   alternating sides, names hang below their node and effects sit outboard of
   the vine where there is room for them. */
@media (max-width: 899px) {
  .ripple--on {
    --orient: portrait;
    height: clamp(520px, 145vw, 700px);
  }

  .ripple--on .ripple__branch {
    left: var(--mx);
    top: var(--my);
  }

  .ripple--on .ripple__name {
    max-width: 8rem;
    font-size: 1.05rem;
  }

  /* The fan is tighter here, so a traced effect can land across a neighbour's
     name. Sent further back than on a wide screen, the neighbour reads as
     receded rather than half-covered. */
  .ripple--on.is-tracing .ripple__name {
    opacity: 0.14;
  }

  /* Two short lines beat one long one at this width, so the effects wrap. */
  .ripple--on .ripple__step {
    max-width: 6rem;
    font-size: 0.68rem;
    white-space: normal;
  }
}

/* --------------------------------------------------------------------------
   22. Scroll reveal
   -------------------------------------------------------------------------- */
/* Gated behind .js (set by site.js) so the page renders fully visible if the
   script never runs — an ungated rule leaves everything permanently at
   opacity 0 for no-JS readers. */
/* The reveal owns `transform` on these elements, and its settled rule is both
   more specific and later in the file than any component's `:hover` — so a card
   that lifted with `transform: translateY(-3px)` stopped lifting the moment it
   was revealed, which is every visitor with JavaScript. Hover lifts therefore
   use `translate`, an independent property that composes with `transform`
   instead of replacing it, and it is listed here so the reveal's own
   `transition` shorthand does not strip its timing. */
.js .reveal,
.js [data-reveal] {
  opacity: 0;
  /* Travel is a custom property so a component can dial it down without
     restating the whole rule and re-entering the specificity argument above. */
  transform: translateY(var(--reveal-y, 22px));
  /* The stagger delay is attached per property rather than through
     transition-delay, which would also hold up a hover lift by however long
     this element's place in the group happens to be. */
  transition: opacity 0.85s var(--ease) var(--d, 0ms),
    transform 0.85s var(--ease) var(--d, 0ms), translate 0.3s var(--ease);
}

/* Long-form copy travels less. 22px under a card reads as the card arriving;
   the same distance under a paragraph reads as the text jumping, and a reader
   working down an article meets one of these every few seconds rather than once
   per section. Only the distance changes — overriding the duration here would
   have to restate the whole shorthand, since a lone transition-duration would
   also stretch the hover lift's timing. */
.js .article > [data-reveal],
.js .article__foot[data-reveal] {
  --reveal-y: 10px;
}

.js .reveal.is-in,
.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* Headline lines ride up from behind their own clipping mask. */
.mask-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.04em;
}

.mask-line > span {
  display: block;
}

.js .mask-line > span {
  transform: translateY(108%);
  transition: transform 1.05s cubic-bezier(0.2, 0.75, 0.2, 1);
  transition-delay: var(--d, 0ms);
}

.js [data-mask].is-in .mask-line > span {
  transform: none;
}

.js h1 .mask-line:nth-child(1) > span {
  --d: 60ms;
}
.js h1 .mask-line:nth-child(2) > span {
  --d: 180ms;
}
.js h1 .mask-line:nth-child(3) > span {
  --d: 300ms;
}
.js h2[data-mask] .mask-line > span {
  --d: 40ms;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .js .reveal,
  .js [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .js .mask-line > span {
    transform: none;
    transition: none;
  }
  /* Every hover movement is cancelled; colour changes are kept, since they
     convey the same affordance without motion. */
  .btn:hover,
  .row:hover .row__media .frame,
  .duo__media:hover .frame,
  .post:hover .frame,
  .card:hover .icon--lg,
  .row:hover .icon--lg,
  .post:hover .icon--lg,
  .pillar:hover .icon--lg,
  .detail:hover .icon {
    transform: none;
  }
  /* The card lifts ride on `translate` rather than `transform`, so cancelling
     them means cancelling that property instead. */
  .card:hover,
  .post:hover,
  .pillars--tiles .pillar:hover {
    translate: none;
  }
  /* The ripple labels are positioned by transform, so they sit outside the
     reveal rules above and carry their own staggered fade. Only the duration
     is squashed by the blanket rule, not the delay, so the stagger is dropped
     here rather than leaving a reader waiting a second for the last label. */
  .ripple--on .ripple__branch {
    opacity: 1;
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   23. Print
   -------------------------------------------------------------------------- */
@media print {
  .header,
  .footer,
  .cta,
  .nav-toggle {
    display: none;
  }
  body {
    background: #fff;
  }
  .reveal {
    opacity: 1;
    transform: none;
  }
  /* Paper gets the stacked branch list: the canvas would print as a blank
     block, and pinned labels would collapse on top of each other. Selectors
     match the .js diagram rules class for class, since those are the ones
     being undone. */
  .ripple--on .ripple__canvas {
    display: none;
  }
  .ripple--on {
    height: auto;
    border: 0;
  }
  .ripple--on .ripple__branches {
    position: static;
    display: block;
  }
  .ripple--on .ripple__origin,
  .ripple--on .ripple__branch,
  .ripple--on .ripple__name,
  .ripple--on .ripple__step {
    position: static;
    width: auto;
    height: auto;
    max-width: none;
    opacity: 1;
    transform: none;
    background: none;
    text-align: left;
    white-space: normal;
  }
  .ripple--on .ripple__branch {
    margin-bottom: 1rem;
  }
}
