/*
 * GENERATED FILE - do not edit.
 * Built from the sources listed in inc/css-bundle.php.
 * Edit those; on a dev install this rebuilds itself on the next page load.
 */
/* ==== assets/css/fonts.css ==== */
/**
 * Self-hosted webfonts.
 *
 * Replaces the render-blocking request to fonts.googleapis.com. That request
 * cost a DNS lookup, TLS handshake and round trip to a third-party origin
 * before the browser could paint anything — measured at roughly a third of the
 * 1,750ms "render-blocking requests" figure in the Aug 2026 Lighthouse run.
 * It also put Google in the page's consent story for no benefit.
 *
 * Besley and DM Sans are VARIABLE fonts: one file each covers the whole 400-700
 * range the design uses, replacing four static weight files apiece. DM Mono has
 * no variable release, so 400 and 500 ship as separate files.
 *
 * Files come from the Fontsource build of the Google Fonts originals
 * (@fontsource-variable/besley, @fontsource-variable/dm-sans, @fontsource/dm-mono,
 * all v5.3.0), which are byte-identical to what Google serves. Licences are in
 * assets/fonts/. All three are SIL Open Font License 1.1 — redistribution from
 * our own domain is explicitly permitted.
 *
 * `unicode-range` is what keeps this cheap: a visitor typing only Latin-1 never
 * downloads the latin-ext files at all. Do not remove it.
 *
 * No italic faces are declared because the stylesheets use none — `grep -r
 * "font-style: *italic" assets/css/` returns zero. If a design ever needs one,
 * add the face here rather than letting the browser synthesise an oblique.
 *
 * @package newenglander
 */

/* -- Besley (headings) — variable, 400-700 ------------------------------- */

@font-face {
  font-family: "Besley";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/besley-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Besley";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/besley-latin-ext-var.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* -- DM Sans (body) — variable, 400-700 ---------------------------------- */

@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dm-sans-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dm-sans-latin-ext-var.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* -- DM Mono (eyebrows, labels, numerals) — static 400 / 500 ------------- */

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/dm-mono-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/dm-mono-latin-ext-400.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/dm-mono-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/dm-mono-latin-ext-500.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ==== assets/css/tokens.css ==== */
:root {
  --ne-color-background: #f0ebe9;
  --ne-color-surface: #f8f5f3;
  --ne-color-surface-strong: #ffffff;
  --ne-color-text: #000000;
  --ne-color-muted: rgba(0, 0, 0, 0.72);
  --ne-color-border: rgba(0, 0, 0, 0.14);
  --ne-color-border-strong: rgba(0, 0, 0, 0.26);
  --ne-color-accent: #3b432b;
  --ne-color-accent-strong: #2f3522;
  --ne-color-accent-contrast: #f0ebe9;
  --ne-color-hemlock: #303e28;
  --ne-color-fieldstone: #3a3a3a;
  --ne-color-limestone: #d9d9d9;
  --ne-color-overlay: rgba(0, 0, 0, 0.35);
  --ne-color-overlay-soft: rgba(0, 0, 0, 0.2);
  --ne-color-shadow: rgba(15, 16, 12, 0.08);

  /* Hairlines. These were previously written as literal rgba() inside block
     markup — 68 times between them — so changing a divider meant a find and
     replace across the patterns. On a dark field the black-alpha rules vanish,
     hence the inverse pair. */
  --ne-color-rule: rgba(0, 0, 0, 0.14);
  --ne-color-rule-strong: rgba(0, 0, 0, 0.2);
  --ne-color-rule-inverse: rgba(240, 235, 233, 0.18);
  --ne-color-rule-inverse-strong: rgba(240, 235, 233, 0.22);
  --ne-color-contrast-soft: rgba(240, 235, 233, 0.4);

  /* Text selection. The moss tint is invisible on a dark field — it is moss at
     20% sitting on near-identical moss — so dark sections flip to linen. */
  --ne-color-selection: rgba(59, 67, 43, 0.2);
  --ne-color-selection-inverse: rgba(240, 235, 233, 0.28);

  --ne-font-body: "DM Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --ne-font-heading: "Besley", Georgia, "Times New Roman", serif;
  --ne-font-mono: "DM Mono", ui-monospace, "SFMono-Regular", "Cascadia Mono", "Segoe UI Mono", monospace;

  --ne-font-size-xs: 0.75rem;
  --ne-font-size-sm: 0.875rem;
  --ne-font-size-base: 1rem;
  --ne-font-size-md: 1.125rem;
  --ne-font-size-lg: 1.375rem;
  --ne-font-size-display: clamp(1.75rem, 4vw, 2.625rem);
  --ne-font-size-xl: clamp(2.5rem, 5vw, 4.5rem);
  --ne-font-size-2xl: clamp(3rem, 6vw, 5.75rem);

  --ne-line-height-tight: 1.05;
  --ne-line-height-heading: 1.08;
  --ne-line-height-body: 1.5;
  --ne-line-height-loose: 1.7;
  --ne-letter-spacing-tight: -0.02em;
  --ne-letter-spacing-wide: 0.12em;

  --ne-space-2xs: 0.25rem;
  --ne-space-xs: 0.5rem;
  --ne-space-sm: 1rem;
  --ne-space-md: 1.5rem;
  --ne-space-lg: 2.5rem;
  --ne-space-xl: 4rem;
  --ne-space-card-gap: 3.125rem;
  --ne-space-hero-gap: 3.875rem;
  --ne-space-section-half: 4.6875rem;
  --ne-space-section: 9.375rem;
  --ne-space-section-gap: 11.9375rem;

  --ne-radius-sm: 0.3125rem;
  --ne-radius-md: 0.625rem;
  --ne-shadow-sm: 0 20px 40px var(--ne-color-shadow);
  --ne-transition-base: 180ms ease;

  --ne-container-width: 90rem;
  --ne-wide-width: 83rem;
  --ne-content-width: 48rem;
  --ne-grid-gap: 2rem;
  --ne-site-gutter: clamp(1.5rem, 5vw, 4.5rem);
  --ne-header-height: 5.5rem;
  --ne-button-padding-block: 0.3125rem;
  --ne-button-padding-inline: 0.9375rem;

  /* WordPress block editor preset variables — generated from theme.json at runtime */
  --wp--preset--spacing--xs: 0.5rem;
  --wp--preset--spacing--sm: 1rem;
  --wp--preset--spacing--md: 1.5rem;
  --wp--preset--spacing--lg: 2.5rem;
  --wp--preset--spacing--xl: 4rem;
  --wp--preset--spacing--section-half: 4.6875rem;
  --wp--preset--spacing--section: 9.375rem;
  --wp--preset--spacing--section-gap: 11.9375rem;
}

@media (max-width: 63.9375rem) {
  :root {
    --ne-space-section: 6rem;
    --ne-space-section-half: 3.25rem;
    --ne-space-section-gap: 5rem;
    --ne-space-card-gap: 2rem;
  }
}

@media (max-width: 47.9375rem) {
  :root {
    --ne-font-size-xl: clamp(2.2rem, 10vw, 3.5rem);
    --ne-font-size-2xl: clamp(2.5rem, 12vw, 4rem);
    --ne-space-section: 4.5rem;
    --ne-space-section-half: 2.5rem;
    --ne-space-section-gap: 3.5rem;
    --ne-site-gutter: 1rem;
    --ne-header-height: 4.5rem;
  }
}


/* ==== assets/css/base.css ==== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  min-height: 100%;
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--ne-color-background);
  color: var(--ne-color-text);
  font-family: var(--ne-font-body), sans-serif;
  font-size: var(--ne-font-size-base);
  line-height: var(--ne-line-height-body);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: var(--ne-color-accent);
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.14em;
  transition: color var(--ne-transition-base), background-color var(--ne-transition-base), border-color var(--ne-transition-base), opacity var(--ne-transition-base);
}

a:hover,
a:focus-visible {
  color: var(--ne-color-accent-strong);
}

button,
input,
select,
textarea {
  font: inherit;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  font-family: var(--ne-font-heading), serif;
  font-weight: 400;
  line-height: var(--ne-line-height-heading);
  letter-spacing: var(--ne-letter-spacing-tight);
}

/* Heading scale. Sizes live in theme.json so the block editor's own controls
   agree with the front end; the per-level *treatment* has to live here because
   base.css loads after the global-styles inline block and the element selectors
   tie on specificity — whatever this file sets wins. Keep the two in sync.

   With h2 capped at 30px there isn't room to signal six levels by size alone,
   so each step down changes a different axis: size (h3), weight (h4), then
   family (h5/h6). h5 and h6 leave Besley deliberately — below ~18px the serif
   reads as small body copy rather than as a heading. */

h2 {
  line-height: 1.2;
}

h3 {
  line-height: 1.25;
}

h4 {
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

h5,
h6 {
  font-family: var(--ne-font-body), system-ui, sans-serif;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: normal;
}

/* Flow rhythm under a heading.

   WordPress' flow layout already puts --wp--style--block-gap (24px) between
   every sibling. That is right between two paragraphs but too loose directly
   under a heading: a heading and the copy it introduces should read as one
   unit, not as two. These tighten the gap per level — the heading's own
   half-leading adds a few px on top, so the optical gap runs slightly larger
   than the number.

   Scoped to the block wrappers on purpose, and measured rather than assumed.
   A bare `h3 + *` (0,0,2) LOSES: WordPress generates a per-container layout
   rule carrying a real class — `.wp-block-group-is-layout-constrained > * + *`
   at (0,1,0) — which sets the 24px gap. Adding the wrapper takes these to
   (0,1,1) so they win without !important. If a heading gap ever silently
   reverts to 24px, the container it sits in is probably not on this list.

   Inline margins in content still beat both; those are stripped separately. */

:is(.wp-block-group, .wp-block-column, .wp-block-cover__inner-container, .entry-content) > :is(h1, h2) + * {
  margin-top: var(--ne-space-md);
}

:is(.wp-block-group, .wp-block-column, .wp-block-cover__inner-container, .entry-content) > h3 + * {
  margin-top: var(--ne-space-sm);
}

:is(.wp-block-group, .wp-block-column, .wp-block-cover__inner-container, .entry-content) > :is(h4, h5, h6) + * {
  margin-top: var(--ne-space-xs);
}

p,
ul,
ol,
dl,
blockquote,
figure {
  margin: 0;
}

ul,
ol {
  padding-left: 1.25rem;
}

:focus-visible {
  outline: 3px solid var(--ne-color-accent);
  outline-offset: 3px;
}

/* Light-field default. Dark sections override this in components.css. */
::selection {
  background: var(--ne-color-selection);
}

.skip-link:focus-visible,
.site-navigation__toggle:focus-visible,
.wp-block-button__link:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline-offset: 4px;
}

button {
  cursor: pointer;
}

input,
select,
textarea {
  width: 100%;
  padding: 0.875rem 1rem;
  border: 1px solid var(--ne-color-border-strong);
  border-radius: var(--ne-radius-sm);
  background: var(--ne-color-surface-strong);
  color: var(--ne-color-text);
}

textarea {
  min-height: 9rem;
  resize: vertical;
}

label {
  display: inline-block;
  margin-bottom: var(--ne-space-xs);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
}

hr,
.wp-block-separator {
  border: 0;
  border-top: 1px solid var(--ne-color-border);
}

blockquote {
  padding-left: var(--ne-space-md);
  border-left: 2px solid var(--ne-color-accent);
}

figcaption {
  margin-top: var(--ne-space-xs);
  color: var(--ne-color-muted);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.site {
  min-height: 100vh;
}


/* ==== assets/css/layout.css ==== */
.ne-container {
  width: min(100% - (2 * var(--ne-site-gutter)), var(--ne-container-width));
  margin-inline: auto;
}

.ne-container--wide {
  width: min(100% - (2 * var(--ne-site-gutter)), var(--ne-wide-width));
  margin-inline: auto;
}

.ne-content-width {
  width: min(100%, var(--ne-content-width));
}

/* == Measure utilities ==
   Cap the line length of body copy so paragraphs never run the full width
   of a section. Apply in the block editor via
   Block → Advanced → Additional CSS class(es).
   Centered blocks (has-text-align-center) also get centered automatically. */
.ne-measure {
  max-width: 60ch;
}

.ne-measure--narrow {
  max-width: 46ch;
}

.ne-measure--wide {
  max-width: 72ch;
}

.ne-measure.has-text-align-center,
.ne-measure--narrow.has-text-align-center,
.ne-measure--wide.has-text-align-center {
  margin-inline: auto;
}

.site-main {
  padding-block-end: var(--ne-space-section);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 1000;
  border-top: 0;
  border-bottom: 1px solid var(--ne-color-border);
  background: rgba(240, 235, 233, 0.92);
  backdrop-filter: blur(18px);
}

/* Homepage: fixed transparent header overlaying the hero */
.home .site-header {
  position: fixed;
  width: 100%;
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
  transition: background 0.35s ease, border-color 0.35s ease, backdrop-filter 0.35s ease;
}

/* Push the sticky/fixed header down below the WP admin bar when logged in.
   Applies to every page (not just the homepage) so the header never hides
   beneath the admin bar. */
body.admin-bar .site-header {
  top: 32px;
}

/* Mobile: JS (main.js) dynamically adjusts top as the admin bar scrolls away.
   The 46px here is just the initial/no-JS fallback. */
@media screen and (max-width: 782px) {
  body.admin-bar .site-header {
    top: 46px;
  }
}

/* Homepage: scrolled past hero — cream with blur */
.home .site-header.is-scrolled {
  background: rgba(240, 235, 233, 0.95);
  border-bottom-color: var(--ne-color-border);
  backdrop-filter: blur(18px);
}

.site-footer {
  background: var(--ne-color-accent-strong);
  border-top: 1px solid rgba(240, 235, 233, 0.18);
  border-bottom: 0;
}

.site-main > .ne-container,
.site-main > .ne-container--wide {
  padding-top: var(--ne-space-section-half);
}

.page-shell,
.archive-shell,
.single-shell {
  display: grid;
  gap: var(--ne-space-section);
}

.page-hero {
  position: relative;
  isolation: isolate;
  min-height: min(70vh, 62.75rem);
  display: flex;
  align-items: flex-end;
  overflow: clip;
  background: var(--ne-color-background);
}

.page-hero--compact {
  min-height: auto;
}

.page-hero--text {
  align-items: center;
  min-height: auto;
  padding-block: var(--ne-space-section);
}

.page-hero__media,
.page-hero__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.page-hero__media img {
  object-fit: cover;
}

.page-hero__overlay {
  position: absolute;
  inset: 0;
  background: var(--ne-color-overlay);
}

.page-hero--soft-overlay .page-hero__overlay {
  background: var(--ne-color-overlay-soft);
}

.page-hero__inner {
  position: relative;
  z-index: 1;
  width: min(100% - (2 * var(--ne-site-gutter)), var(--ne-wide-width));
  margin-inline: auto;
  padding-block: clamp(8rem, 17vh, 12rem) var(--ne-space-section);
}

.page-hero--text .page-hero__inner {
  padding-block: 0;
}

.page-hero__content {
  display: grid;
  gap: var(--ne-space-hero-gap);
  width: min(100%, 43rem);
}

.page-hero__kicker,
.page-header__kicker,
.entry-meta,
.community-meta,
.person-profile__detail dt,
.testimonial-card__source {
  color: currentColor;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
}

.page-hero__title,
.page-title,
.entry-title {
  font-size: var(--ne-font-size-xl);
}

.page-hero__summary,
.archive-description,
.page-intro {
  max-width: 34rem;
  color: currentColor;
  font-size: var(--ne-font-size-md);
  line-height: var(--ne-line-height-loose);
}

.page-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ne-space-sm);
}

.page-header,
.entry-header {
  display: grid;
  gap: var(--ne-space-md);
}

.page-header {
  padding-bottom: var(--ne-space-lg);
  border-bottom: 1px solid var(--ne-color-border);
}

.entry-content,
.page-content,
.archive-description {
  max-width: var(--ne-content-width);
}

.single-entry,
.page-entry {
  display: grid;
  gap: var(--ne-space-lg);
}

.single-entry .entry-content,
.page-entry .entry-content {
  max-width: none;
}

.single-entry .entry-content > *,
.page-entry .entry-content > * {
  max-width: var(--ne-content-width);
}

.single-entry .entry-content > .alignwide,
.page-entry .entry-content > .alignwide,
.single-entry .entry-content > .alignfull,
.page-entry .entry-content > .alignfull {
  max-width: none;
}

/* == Services section: 5-image mosaic == */
/* Layout: 2-col grid on a 6-row track. Left sub-col = 2 equal photos    */
/* (a, b), right sub-col = 3 equal photos (c, d, e).                      */
.ne-mosaic-5 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(6, 1fr);
  gap: var(--ne-space-xs);
  /* Height scales with width so the photos keep their ratios and never
     stretch as the column narrows. */
  aspect-ratio: 8 / 9;
}

.ne-mosaic-5 img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Left sub-column: two equal-height images */
.ne-mosaic-5 .m-a { grid-column: 1; grid-row: 1 / 4; }
.ne-mosaic-5 .m-b { grid-column: 1; grid-row: 4 / 7; }

/* Right sub-column: three equal-height images */
.ne-mosaic-5 .m-c { grid-column: 2; grid-row: 1 / 3; }
.ne-mosaic-5 .m-d { grid-column: 2; grid-row: 3 / 5; }
.ne-mosaic-5 .m-e { grid-column: 2; grid-row: 5 / 7; }

/* -- Same mosaic, built from core Image blocks -------------------------------
   The homepage version is a Custom HTML block: five bare <img> elements
   carrying .m-a … .m-e, placed by the rules above. That renders correctly but
   is only editable by hand-pasting URLs into the HTML — no media picker, and
   no srcset unless you paste that too.

   The block-pattern version (newenglander/split-text-left-mosaic5) instead
   wraps the mosaic in a Group with this class and fills it with wp:image
   blocks, so the photos are swappable in the editor like any other image.
   Core wraps each one in a <figure>, so the grid items are figures rather
   than imgs and cannot use the class-based placement above — position does
   the work instead.

   Both forms are supported on purpose. Do not delete the .m-* rules while the
   homepage still uses the HTML form. Order is the layout here: reordering the
   Image blocks in the editor reshuffles the mosaic. */
.ne-mosaic-5 > figure {
  /* Core gives figures a default margin; the grid gap owns spacing here. The
     min-* zeroing stops a tall photo from forcing its row past the 1fr track
     (grid items default to min-height:auto). */
  margin: 0;
  min-width: 0;
  min-height: 0;
}

.ne-mosaic-5 > figure > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Left sub-column: two equal-height photos */
.ne-mosaic-5 > figure:nth-child(1) { grid-column: 1; grid-row: 1 / 4; }
.ne-mosaic-5 > figure:nth-child(2) { grid-column: 1; grid-row: 4 / 7; }

/* Right sub-column: three equal-height photos */
.ne-mosaic-5 > figure:nth-child(3) { grid-column: 2; grid-row: 1 / 3; }
.ne-mosaic-5 > figure:nth-child(4) { grid-column: 2; grid-row: 3 / 5; }
.ne-mosaic-5 > figure:nth-child(5) { grid-column: 2; grid-row: 5 / 7; }

.single-entry .entry-content > .wp-block-group.alignwide,
.page-entry .entry-content > .wp-block-group.alignwide,
.single-entry .entry-content > .wp-block-columns.alignwide,
.page-entry .entry-content > .wp-block-columns.alignwide {
  max-width: var(--ne-wide-width);
}

.single-entry .entry-content > .alignfull,
.page-entry .entry-content > .alignfull,
.single-entry .entry-content > .wp-block-cover.alignfull,
.page-entry .entry-content > .wp-block-cover.alignfull {
  max-width: none;
}

/* ── Canvas template: gutter-aware widths for block-composed pages ─────────
   Canvas pages (page-canvas.php) output raw block content with no
   .entry-content wrapper, so top-level blocks have no width limit and
   full-width section groups constrain their inner content with max-width but
   no side gutter — which lets content touch the screen edges as the viewport
   narrows, and lets a bare block span the whole page. These rules give block
   pages the same behaviour as .ne-container: a wide max-width plus a gutter
   that always holds. */

/* Full-width section groups (patterns): keep the full-bleed background, but
   inset the inner content by the site gutter so it never touches the edges. */
.site-main--canvas > .wp-block-group.alignfull {
  padding-inline: var(--ne-site-gutter);
}

/* Children explicitly set to full width (e.g. a header image) still break out
   past the gutter to reach the screen edges. */
.site-main--canvas > .wp-block-group.alignfull > .alignfull {
  width: calc(100% + 2 * var(--ne-site-gutter));
  max-width: calc(100% + 2 * var(--ne-site-gutter));
  margin-inline: calc(-1 * var(--ne-site-gutter));
}

/* Any other top-level block (a bare Columns block, heading, etc.) is held to
   the wide content width with the same gutter, so it lines up with the pattern
   sections instead of spanning the full page.

   .ne-contact is exempt: the bottom "Get in Touch" section is a PHP template
   part, not a block, so it carries no .alignfull class and would otherwise be
   caught here and boxed in — while the same section renders full-bleed on the
   homepage, which uses .site-main--homepage.

   .ne-bird-reveal is exempt for the same reason — it arrives via the
   [ne_bird_reveal] shortcode, so it has no .alignfull either. Boxing it does
   more than narrow the photo: the clip-path is userSpaceOnUse and main.js
   centres the bird on window.innerWidth / 2, which is only the photo's centre
   while the photo spans the viewport. Constrain it and the bird drifts right
   by half the gutter. Added 2026-08-14 when the homepage moved into page 39. */
.site-main--canvas > :not(.alignfull):not(.ne-contact):not(.ne-bird-reveal) {
  width: min(100% - (2 * var(--ne-site-gutter)), var(--ne-wide-width));
  margin-inline: auto;
}

/* Section vertical rhythm: full-width section groups (the ne-section pattern
   wrapper) carry their block padding via this class rather than inline styles,
   so consecutive sections can collapse cleanly without an override. */
/* ── Section rhythm ────────────────────────────────────────────────────────
   The whole system lives here. `.ne-section` and `.ne-section--half` used to
   be declared a second time, identically, in utilities.css — two homes for one
   decision, which is how they drift apart. Keep them next to the adjacency
   rule that depends on them. */
.ne-section {
  padding-block: var(--ne-space-section);
}

.ne-section--half {
  padding-block: var(--ne-space-section-half);
}

/* Collapse doubled vertical spacing between stacked sections: a section that
   directly follows another drops its top padding, so the gap between them is
   one section's padding instead of two. Sections with their own background
   keep their top padding so their coloured band isn't clipped.

   This only fires when BOTH neighbours carry `.ne-section`. A section that
   hardcodes its padding inline instead opts itself out, and the section after
   it keeps a full second helping — which is what produced the 225px gap under
   "Simple, Honest Pricing". */
.ne-section + .ne-section:not(.has-background) {
  padding-top: 0;
}

@media (max-width: 63.9375rem) {
  .site-main {
    padding-block-end: var(--ne-space-section-half);
  }

  .page-hero__inner {
    padding-block: 7rem var(--ne-space-section-half);
  }
}

@media (max-width: 47.9375rem) {
  .page-hero {
    min-height: 32rem;
  }

  .page-hero__content {
    gap: var(--ne-space-lg);
    width: 100%;
  }

  .page-hero__summary,
  .archive-description,
  .page-intro {
    font-size: var(--ne-font-size-base);
  }
}


/* ==== assets/css/components.css ==== */
/* ============================================================
   EYEBROW RULE — scroll-scrubbed line above section titles
   ============================================================
   The 1px line under the "Services" / "Who We Are" eyebrow fills
   in horizontally from the outer edge of the page inward, driven
   directly by scroll position (like the bird reveal): as you
   scroll down toward the section, --ne-rule-progress moves 0 → 1
   and the line grows to its full length. Left column grows from
   the left edge; right column grows from the right edge.
*/
/* ============================================================
   DISPLAY HEADING — the homepage's oversized h2s
   ============================================================
   Deliberate exception to the 30px h2 in the scale. The homepage sections lead
   with a statement rather than a label, so they carry more weight than an
   ordinary section heading. Previously each one carried its own inline clamp
   (2.5rem / 2.75rem / 3.25rem max) and they never agreed; this is the single
   value they now share.

   Capped at 42px (2.625rem) from roughly 1050px up, easing to 28px on small
   screens — a hard 42px would swamp a 375px viewport. Applied via the class,
   not inline, so it stays adjustable in one place. */
.ne-display {
  font-size: var(--ne-font-size-display);
  line-height: 1.1;
}

/* The eyebrow LABEL itself. Until now every pattern hand-rolled this as inline
   styles on a paragraph — mono family, 0.75rem, 0.12em tracking, uppercase —
   41 times across 24 files. This is that recipe, named. Deliberately NOT tied
   to h6: eyebrows are labels, not headings, and promoting them would put a
   spurious level into the document outline for screen readers.

   Composes with .ne-eyebrow-rule below: the rule goes on the wrapping group,
   this goes on the paragraph inside it. Colour is inherited on purpose so
   dark sections (.ne-hero-cover, .site-main--dark) can recolour it without an
   inline override. */
.ne-eyebrow {
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

.ne-eyebrow-rule {
  position: relative;
  /* The rule owns its own spacing. Every instance used to set these inline and
     they had drifted to three different values — 24px, 64px and 8px — so the
     same component sat differently in each section. 24px is the one that
     reads right. Inline values still win, so any block that genuinely needs a
     different gap can still say so; it just no longer has to. */
  padding-bottom: var(--ne-space-xs);
  margin-bottom: var(--ne-space-md);
}

.ne-eyebrow-rule::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(0, 0, 0, 0.14);
  transform: scaleX(1); /* default (no JS): full length */
  transform-origin: left center;
}

/* Keep the eyebrow label hugging the rule's start edge regardless of the
   container layout it's dropped into. A constrained group would otherwise
   center the inner label (auto inline margins) while the rule spans full
   width, leaving the label floating in the middle. */
.ne-eyebrow-rule > * {
  margin-left: 0 !important;
  margin-right: auto !important;
}

.ne-eyebrow-rule--right > * {
  margin-left: auto !important;
  margin-right: 0 !important;
  text-align: right;
}

/* Right-hand column: line originates at the outer (right) edge */
.ne-eyebrow-rule--right::after {
  transform-origin: right center;
}

/* With JS: length is scrubbed by scroll progress set on the host element */
.js-enabled .ne-eyebrow-rule::after {
  transform: scaleX(var(--ne-rule-progress, 0));
}

@media (prefers-reduced-motion: reduce) {
  .js-enabled .ne-eyebrow-rule::after {
    transform: scaleX(1);
  }
}

/* Header: always a single row — logo left, toggle right */
.site-header__inner {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--ne-space-sm);
  padding-block: 0;
  min-height: 3.5rem;
}

/* Footer: stacked on mobile, row on desktop */
.site-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-md);
  padding-block: var(--ne-space-md);
}

.site-branding {
  display: flex;
  align-items: center;
  gap: var(--ne-space-sm);
}

.site-logo {
  flex: 0 0 auto;
}

.site-logo img {
  max-height: 3rem;
  width: auto;
}

.site-title {
  margin: 0;
  font-size: 1.375rem;
  line-height: 1;
}

.site-title a {
  color: inherit;
  text-decoration: none;
}

.site-description {
  color: var(--ne-color-muted);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.menu {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.menu a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  color: inherit;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.08em;
  text-decoration: none;
  text-transform: uppercase;
}

.menu a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.35rem;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--ne-transition-base);
}

.menu a:hover::after,
.menu a:focus-visible::after,
.current-menu-item > a::after,
.current_page_item > a::after,
.current-menu-ancestor > a::after {
  transform: scaleX(1);
}

.site-navigation__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ne-color-text);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-left: auto;
}

/* One SVG, three <rect>s — see the note in header.php. Sized in rem so the icon
   still tracks the root font size; because the bars scale together inside a
   single viewBox they always rasterise identically to each other. */
.site-navigation__toggle-icon {
  display: block;
  width: 1.125rem;
  height: 0.5625rem; /* 9/18 of the width, matching the viewBox */
  overflow: visible; /* the rotated X extends past the 18x9 box */
  flex: none;
}

.site-navigation__toggle-line {
  fill: currentColor;
  /* SVG transforms default to the user-space origin, not the shape's centre,
     so the X would pivot around the icon's top-left without these. */
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--ne-transition-base), opacity var(--ne-transition-base);
}

/* crispEdges keeps the flat bars sharp but would jag the 45deg strokes. */
.site-header.is-menu-open .site-navigation__toggle-icon {
  shape-rendering: auto;
}

.site-header.is-menu-open .site-navigation__toggle-line:nth-child(1) {
  transform: translateY(4px) rotate(45deg);
}

.site-header.is-menu-open .site-navigation__toggle-line:nth-child(2) {
  opacity: 0;
}

.site-header.is-menu-open .site-navigation__toggle-line:nth-child(3) {
  transform: translateY(-4px) rotate(-45deg);
}

/* ============================================
   Header: Dual logo states
   ============================================ */

.site-logo-link {
  display: flex;
  align-items: center;
  text-decoration: none;
}

/* Default: show horizontal lockup, hide bird-mark */
.site-logo__mark {
  display: none;
  height: 2.75rem;
  width: auto;
  /* Force pure white regardless of SVG fill color */
  filter: brightness(0) invert(1);
}

/* 2.7rem, not 2rem: the woodland-moss lockup is a taller crop (1296 x 286.83),
   so height-locking at 2rem shrank the wordmark ~26%. This restores it. */
.site-logo__lockup {
  display: block;
  height: 2.7rem;
  width: auto;
}

/* Homepage at top of page (hero visible): show bird mark, hide lockup */
.home .site-header:not(.is-scrolled) .site-logo__mark {
  display: block;
}

.home .site-header:not(.is-scrolled) .site-logo__lockup {
  display: none;
}

/* Homepage transparent state: white text on dark hero */
.home .site-header:not(.is-scrolled) {
  color: #f0ebe9;
}

.home .site-header:not(.is-scrolled) .site-navigation__toggle {
  color: #f0ebe9;
}

.home .site-header:not(.is-scrolled) .site-navigation__toggle {
  font-weight: 700;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}

/* Desktop header layout */
@media (min-width: 64rem) {
  .site-header__inner {
    flex-direction: row;
    align-items: center;
    gap: var(--ne-space-lg);
    padding-block: 0;
    min-height: 4.5rem;
  }

  /* Keep toggle visible on desktop */
  .site-navigation__toggle {
    display: inline-flex;
  }
}

.entry-summary,
.entry-content,
.page-content {
  margin-top: var(--ne-space-sm);
}

.entry-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  color: var(--ne-color-muted);
}

.entry-summary-card {
  display: grid;
  gap: var(--ne-space-md);
  padding: clamp(1.5rem, 2vw, 2rem);
  border: 1px solid var(--ne-color-border);
  border-radius: var(--ne-radius-md);
  background: var(--ne-color-surface-strong);
  box-shadow: var(--ne-shadow-sm);
}

.entry-summary-card .entry-title {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
}

.entry-summary-card .entry-summary {
  color: var(--ne-color-muted);
}

.community-card {
  display: grid;
  overflow: clip;
  border: 1px solid var(--ne-color-border);
  border-radius: var(--ne-radius-md);
  background: var(--ne-color-surface-strong);
  box-shadow: var(--ne-shadow-sm);
}

.community-card__media img,
.community-featured-image img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.community-card__body,
.community-featured-image,
.community-placeholder,
.community-empty-state {
  padding: clamp(1.25rem, 2vw, 1.75rem);
}

.community-group {
  display: grid;
  gap: var(--ne-space-lg);
}

.community-group__header {
  display: grid;
  gap: var(--ne-space-sm);
}

.community-group__title {
  font-size: clamp(2rem, 4vw, 3.25rem);
}

.community-group__description {
  color: var(--ne-color-muted);
  max-width: 38rem;
}

.community-group__description > * + * {
  margin-top: var(--ne-space-xs);
}

.community-group__list {
  display: grid;
  gap: var(--ne-grid-gap);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.community-card__media {
  display: block;
}

.community-card__header {
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-xs);
}

.community-card__title a {
  color: inherit;
  text-decoration: none;
}

.community-card__title {
  font-size: clamp(1.5rem, 3vw, 2rem);
}

.community-card__excerpt {
  color: var(--ne-color-muted);
}

.community-card__link {
  margin-top: var(--ne-space-sm);
}

.community-placeholder,
.community-empty-state {
  display: grid;
  gap: var(--ne-space-sm);
  border: 1px solid var(--ne-color-border);
  background: var(--ne-color-surface);
}

.community-group__empty {
  margin: 0;
  padding: var(--ne-space-md);
  border: 1px solid var(--ne-color-border);
  border-radius: var(--ne-radius-md);
  background: var(--ne-color-surface);
  color: var(--ne-color-muted);
}

/* Image credit under the single-neighborhood featured photo. Small, right
   aligned, Fieldstone — it should read as a footnote to the picture, not as a
   caption competing with the body copy. Deliberately single-page only: the
   archive cards stay clean, and the credit lives with the full-size image. */
.community-image-credit {
  margin-top: var(--ne-space-xs);
  text-align: right;
  font-family: var(--ne-font-body);
  font-size: var(--ne-font-size-xs);
  line-height: 1.4;
  color: var(--ne-color-fieldstone);
  text-wrap: pretty;

  /* base.css styles every figcaption as a mono uppercase label. That reads as a
     tag, not a sentence — and these credits carry proper names ("John Phelan",
     "User:Magicpiano") that uppercase mangles. Reset to body type here only. */
  text-transform: none;
  letter-spacing: normal;
}

.community-image-credit a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
  transition: color var(--ne-transition-base);
}

.community-image-credit a:hover,
.community-image-credit a:focus-visible {
  color: var(--ne-color-accent);
}

/* ============================================================
   NEIGHBORHOODS ARCHIVE (community CPT)
   ============================================================ */

/* Hero — centred kicker + heading, full-width image below */
.ne-nb-hero {
  padding-top: var(--ne-space-section-half);
}

.ne-nb-hero__text {
  width: min(100% - (2 * var(--ne-site-gutter)), var(--ne-wide-width));
  margin-inline: auto;
  text-align: center;
  display: grid;
  justify-items: center;
  gap: var(--ne-space-md);
}

.ne-nb-hero__kicker {
  margin: 0;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

.ne-nb-hero__title {
  margin: 0;
  max-width: 20ch;
  font-size: clamp(2.25rem, 4.5vw, 3.75rem);
  line-height: 1.08;
}

.ne-nb-hero__intro {
  margin: 0;
  max-width: 54ch;
  color: var(--ne-color-muted);
}

.ne-nb-hero__media {
  margin-top: var(--ne-space-xl);
  width: 100%;
}

.ne-nb-hero__media img {
  display: block;
  width: 100%;
  height: clamp(18rem, 42vw, 34rem);
  object-fit: cover;
}

/* Region jump-links — below the hero, above the first region */
.ne-nb-jump {
  padding-block: var(--ne-space-lg);
}

.ne-nb-jump__text {
  margin: 0;
  text-align: center;
  color: var(--ne-color-muted);
}

.ne-nb-jump__label {
  margin-right: 0.25em;
}

.ne-nb-jump__text a {
  color: var(--ne-color-accent);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  transition: color 0.2s ease;
}

.ne-nb-jump__text a:hover,
.ne-nb-jump__text a:focus-visible {
  color: var(--ne-color-accent-strong);
}

/* Sections */
.ne-nb {
  display: grid;
  gap: var(--ne-space-section);
  padding-top: var(--ne-space-lg);
  padding-bottom: var(--ne-space-section);
}

/* Offset anchor targets so they clear the sticky header */
.ne-nb-section {
  scroll-margin-top: 6rem;
}

/* Shared 3-column grid: Name | Photo | Description = 2 : 2 : 2.5 */
.ne-nb-grid {
  display: grid;
  grid-template-columns: 2fr 2fr 2.5fr;
  gap: var(--ne-space-lg);
  align-items: start;
}

/* Community-type eyebrow — sits above the section's only hairline */
.ne-nb-section__eyebrow {
  margin: 0 0 var(--ne-space-sm);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

/* Section header: name in col 1, description spans cols 2-3.
   The single hairline sits ABOVE the community type (border-top). */
.ne-nb-section__header {
  margin-bottom: var(--ne-space-xl);
  padding-top: var(--ne-space-md);
  border-top: 1px solid var(--ne-color-border);
}

.ne-nb-section__title {
  margin: 0;
  font-size: clamp(1.75rem, 3vw, 2.75rem);
  line-height: 1.1;
}

.ne-nb-section__desc {
  grid-column: 2 / -1;
  color: var(--ne-color-muted);
}

.ne-nb-section__desc > * {
  margin: 0;
}
.ne-nb-section__desc > * + * {
  margin-top: var(--ne-space-sm);
}

/* Neighborhood rows — no dividers; spacing comes from the list gap */
.ne-nb-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--ne-space-xl);
}

.ne-nb-item__name {
  margin: 0;
  font-size: clamp(1.5rem, 2vw, 1.75rem);
  line-height: 1.15;
}

.ne-nb-item__name a {
  color: inherit;
  text-decoration: none;
  transition: color 0.2s ease;
}

.ne-nb-item__name a:hover,
.ne-nb-item__name a:focus-visible {
  color: var(--ne-color-accent);
}

.ne-nb-item__photo {
  display: block;
  overflow: hidden;
}

.ne-nb-item__photo img,
.ne-nb-item__photo-placeholder {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.ne-nb-item__photo img {
  transition: transform 0.4s ease;
}

.ne-nb-item__photo:hover img {
  transform: scale(1.03);
}

.ne-nb-item__photo-placeholder {
  background: var(--ne-color-border);
}

.ne-nb-item__desc {
  color: var(--ne-color-muted);
}

.ne-nb-item__desc p {
  margin: 0;
}

.ne-nb-empty {
  padding-block: var(--ne-space-section);
  text-align: center;
}

/* Stack on narrow screens */
@media (max-width: 47.9375rem) {
  .ne-nb-grid {
    grid-template-columns: 1fr;
    gap: var(--ne-space-sm);
  }

  .ne-nb-section__desc {
    grid-column: 1 / -1;
  }

  .ne-nb-item__photo img,
  .ne-nb-item__photo-placeholder {
    aspect-ratio: 16 / 9;
  }
}

.people-archive {
  padding-bottom: var(--ne-space-section);
}

.people-archive-list {
  display: grid;
  gap: var(--ne-grid-gap);
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 55rem) {
  .people-archive-list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 36rem) {
  .people-archive-list {
    grid-template-columns: 1fr;
  }
}

.person-card {
  display: grid;
  border: 1px solid var(--ne-color-border);
  border-radius: var(--ne-radius-md);
  overflow: clip;
  background: var(--ne-color-surface-strong);
  box-shadow: var(--ne-shadow-sm);
}

.person-card__media,
.person-card__media img {
  display: block;
}

.person-card__media img,
.person-featured-image img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

.person-card__body,
.person-featured-image,
.person-empty-state {
  padding: clamp(1.25rem, 2vw, 1.75rem);
}

.person-card__title,
.person-card__title a {
  color: inherit;
  text-decoration: none;
}

.person-card__role,
.person-profile__role {
  margin: 0;
  color: var(--ne-color-muted);
}

.person-card__summary {
  color: var(--ne-color-muted);
}

.person-card__contact,
.person-social-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.person-card__license-item {
  width: 100%;
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.06em;
  color: var(--ne-color-muted);
}

.person-profile__details {
  display: grid;
  gap: var(--ne-space-md);
  margin: 0;
}

.person-profile__detail dd {
  margin: 0;
  color: var(--ne-color-muted);
}

.person-empty-state {
  display: grid;
  gap: var(--ne-space-sm);
  border: 1px solid var(--ne-color-border);
  background: var(--ne-color-surface);
}

.testimonial-featured {
  max-width: 56rem;
  margin-inline: auto;
  text-align: center;
  padding-top: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
  border-bottom: 0.5px solid var(--ne-color-border);
}

.testimonial-featured__mark {
  display: block;
  font-family: var(--ne-font-heading), Georgia, serif;
  font-size: 4rem;
  line-height: 0.5;
  color: var(--ne-color-accent);
}

.testimonial-featured__quote {
  max-width: 40rem;
  margin: 1.25rem auto 1.75rem;
  padding: 0;
  border: 0;
  font-family: var(--ne-font-heading), Georgia, serif;
  font-size: clamp(1.6rem, 3.4vw, 2.5rem);
  font-style: normal;
  font-weight: 400;
  line-height: 1.28;
  color: var(--ne-color-text);
  text-wrap: balance;
}

.testimonial-featured__quote strong {
  font-weight: 700;
}

.testimonial-featured__meta {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5em;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

.testimonials-archive-list {
  display: flex;
  flex-direction: column;
  max-width: 56rem;
  margin-inline: auto;
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.testimonial-card + .testimonial-card {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 0.5px solid var(--ne-color-border);
}

.testimonial-card {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2rem);
  align-items: start;
}

.testimonial-card__avatar {
  width: 150px;
  height: 150px;
  border-radius: 0;
  overflow: hidden;
  background: var(--ne-color-surface);
  border: 1px solid var(--ne-color-border);
  display: grid;
  place-items: center;
}

.testimonial-card__avatar-img,
.testimonial-featured-image img {
  width: 100%;
  height: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
  filter: saturate(0.7) sepia(0.35) brightness(1.04) contrast(0.97);
}

.testimonial-card__avatar-fallback {
  font-size: 3rem;
  font-weight: 600;
  color: var(--ne-color-muted);
  text-transform: uppercase;
}

.testimonial-featured-image,
.testimonial-empty-state,
.testimonial-placeholder {
  padding: clamp(1.5rem, 3vw, 2rem);
}

.testimonial-card__body {
  min-width: 0;
}

.testimonial-card__body > * + * {
  margin-top: var(--ne-space-sm);
}

.testimonial-card__rating {
  color: var(--ne-color-accent);
  letter-spacing: 0.12em;
  font-size: 1.15rem;
  line-height: 1;
}

.testimonial-card__content {
  color: var(--ne-color-text);
  font-family: var(--ne-font-heading), Georgia, serif;
  font-size: 1.2rem;
  line-height: 1.5;
}

.testimonial-card__content > * + * {
  margin-top: var(--ne-space-sm);
}

.testimonial-card__meta {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5em;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

.testimonial-card__name {
  color: var(--ne-color-text);
  font-weight: 500;
}

.testimonial-card__sep {
  color: var(--ne-color-accent);
}

.testimonial-card__source {
  color: var(--ne-color-muted);
}

@media (max-width: 30rem) {
  .testimonial-card {
    grid-template-columns: 1fr;
  }
}

.testimonial-empty-state,
.testimonial-placeholder {
  display: grid;
  gap: var(--ne-space-sm);
  border: 1px solid var(--ne-color-border);
  border-radius: var(--ne-radius-md);
  background: var(--ne-color-surface);
}

.page-entry .wp-block-image img,
.single-entry .wp-block-image img,
.page-entry .wp-block-cover__image-background,
.single-entry .wp-block-cover__image-background {
  border-radius: var(--ne-radius-md);
}

.page-entry .wp-block-group,
.page-entry .wp-block-columns,
.page-entry .wp-block-media-text,
.page-entry .wp-block-cover,
.single-entry .wp-block-group,
.single-entry .wp-block-columns,
.single-entry .wp-block-media-text,
.single-entry .wp-block-cover {
  margin-top: var(--ne-space-section-gap);
}

.page-entry .wp-block-columns,
.single-entry .wp-block-columns {
  gap: clamp(1.5rem, 4vw, 4rem);
}

.page-entry .wp-block-button__link,
.single-entry .wp-block-button__link,
.community-card__link a,
.person-social-links__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  padding: var(--ne-button-padding-block) var(--ne-button-padding-inline);
  border: 1px solid var(--ne-color-accent);
  border-radius: var(--ne-radius-sm);
  background: var(--ne-color-accent);
  color: var(--ne-color-accent-contrast);
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.08em;
  text-decoration: none;
  text-transform: uppercase;
}

.page-entry .wp-block-button__link:hover,
.page-entry .wp-block-button__link:focus-visible,
.single-entry .wp-block-button__link:hover,
.single-entry .wp-block-button__link:focus-visible,
.community-card__link a:hover,
.community-card__link a:focus-visible,
.person-social-links__link:hover,
.person-social-links__link:focus-visible {
  background: var(--ne-color-accent-strong);
  border-color: var(--ne-color-accent-strong);
  color: var(--ne-color-accent-contrast);
}

/* ============================================================
   DEFAULT BUTTON — hover fill/border shift + diamond rotate
   ============================================================
   Mirrors the footer Subscribe hover: on hover the dark-green
   fill lightens and the cream border brightens to full strength.
   The text stays cream (no color change). The border is drawn
   with an inset box-shadow so it adds no layout shift and never
   fights an existing border. Scoped to the filled defaults —
   the outline CTA variant sets its own background, so it's left
   alone via :not(.has-background).
*/
.wp-block-button__link.wp-element-button:not(.has-background) {
  box-shadow: inset 0 0 0 1px rgba(240, 235, 233, 0.3);
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
}

.wp-block-button__link.wp-element-button:not(.has-background):hover,
.wp-block-button__link.wp-element-button:not(.has-background):focus-visible {
  background-color: #575d48; /* lighter green fallback */
  background-color: color-mix(in srgb, var(--ne-color-accent) 84%, var(--ne-color-accent-contrast));
  color: var(--ne-color-accent-contrast); /* lock text to cream — no color change */
  box-shadow: inset 0 0 0 1px var(--ne-color-accent-contrast);
}

/* Diamond mark — drawn as a rotated square so it spins around its exact
   centre. (The ◆ glyph's ink sits above the text box's centre, so rotating
   the character made it orbit instead of turning in place.) The literal ◆
   text is kept but hidden for a no-JS / assistive fallback. */
.ne-btn-diamond {
  display: inline-block;
  position: relative;
  top: -0.12rem; /* optical nudge up to sit in line with the all-caps text */
  width: 0.44rem;
  height: 0.44rem;
  vertical-align: middle;
  /* collapse the literal ◆ glyph so only the drawn diamond shows */
  font-size: 0;
  line-height: 0;
}

.ne-btn-diamond::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  transform: rotate(45deg);
  transform-origin: 50% 50%;
  transition: transform 0.45s ease;
}

.wp-block-button__link:hover .ne-btn-diamond::before,
.wp-block-button__link:focus-visible .ne-btn-diamond::before,
.wp-element-button:hover .ne-btn-diamond::before,
.wp-element-button:focus-visible .ne-btn-diamond::before {
  transform: rotate(135deg); /* 45° base + 90° */
}

@media (prefers-reduced-motion: reduce) {
  .ne-btn-diamond::before {
    transition: none;
  }
}

/* ============================================================
   HOMEPAGE SERVICES SPLIT ("For buyers, sellers, investors…")
   ============================================================
   Eyebrow and heading stay at the top; the body copy and Learn More
   button drop to the bottom so they align with the base of the photo
   mosaic beside them.
*/
.ne-services-split .wp-block-column:not(.ne-col-images) {
  display: flex;
  flex-direction: column;
}

.ne-services-split .wp-block-column:not(.ne-col-images) > p:first-of-type {
  margin-top: auto !important;
}

/* Body copy and the CTA button are indented under the heading in the
   Services + Who We Are splits. Amount is a single token — swap
   --ne-space-xl here if the design differs. */
.ne-services-split .wp-block-column:not(.ne-col-images) > p,
.ne-services-split .wp-block-column:not(.ne-col-images) > .wp-block-buttons,
.ne-about-split .wp-block-column > p,
.ne-about-split .wp-block-column > .wp-block-buttons {
  padding-left: var(--ne-space-xl);
}

/* Split block-pattern sections (e.g. Sell page — "Case Study" and "The Selling
   Process"): indent the body group under the eyebrow + heading, matching the
   homepage splits. The source pattern sets this inline; this restores it on
   instances that lost the inline padding. */
.ne-split-col > .wp-block-group + .wp-block-group {
  padding-left: var(--ne-space-xl);
}

/* Mosaic split sections (e.g. Sell — "Why Us"): the eyebrow + heading sit tight
   at the top, and the body group drops to the bottom, level with the image
   mosaic, and is indented. margin-top:auto pushes ONLY the body group down, so
   the eyebrow + heading always stay together at the top (unlike space-between,
   which spreads them). Targets the text column (the one WITHOUT a nested image
   mosaic) structurally, so it applies to existing pages and future inserts. */
.ne-section .wp-block-columns:has(> .wp-block-column > .wp-block-columns)
  > .wp-block-column:not(:has(.wp-block-columns)) {
  display: flex;
  flex-direction: column;
}

.ne-section .wp-block-columns:has(> .wp-block-column > .wp-block-columns)
  > .wp-block-column:not(:has(.wp-block-columns)) > .wp-block-group:last-child {
  margin-top: auto;
  padding-left: var(--ne-space-xl);
}

/* The flex text column no longer collapses margins, so the heading's own top
   margin would double the gap under the eyebrow rule. Zero it — the eyebrow
   group's bottom margin already supplies the gap. */
.ne-section .wp-block-columns:has(> .wp-block-column > .wp-block-columns)
  > .wp-block-column:not(:has(.wp-block-columns)) > .wp-block-heading {
  margin-top: 0;
}

/* Feature list in the body group: each description hugs its heading (the space
   between items is carried by the h3 block gap). !important overrides the
   per-paragraph "space above" set in the editor. */
.ne-section .wp-block-columns:has(> .wp-block-column > .wp-block-columns)
  > .wp-block-column:not(:has(.wp-block-columns)) > .wp-block-group:last-child > p {
  margin-top: var(--ne-space-xs) !important;
}

/* Same three rules again, for splits whose photo side is the five-image mosaic
   (.ne-mosaic-5) rather than a nested 2×2 Columns block.

   The set above finds the text column structurally — "the column that does NOT
   contain a nested Columns block". The 5-up mosaic is a single Group, so a
   section using it slips past every one of those selectors and the text column
   silently loses its bottom-anchoring and indent. Rather than loosen the
   structural test (which would then catch unrelated splits), key these on
   .ne-col-images — the marker class the homepage Services split already puts
   on its mosaic column, and which split-text-left-mosaic5 carries too.

   Both sets can never fire on the same section: one requires a nested Columns
   block in the photo column, the other requires there not to be one. */
.ne-section .wp-block-columns:has(> .ne-col-images)
  > .wp-block-column:not(.ne-col-images) {
  display: flex;
  flex-direction: column;
}

.ne-section .wp-block-columns:has(> .ne-col-images)
  > .wp-block-column:not(.ne-col-images) > .wp-block-group:last-child {
  margin-top: auto;
  padding-left: var(--ne-space-xl);
}

.ne-section .wp-block-columns:has(> .ne-col-images)
  > .wp-block-column:not(.ne-col-images) > .wp-block-heading {
  margin-top: 0;
}

.ne-section .wp-block-columns:has(> .ne-col-images)
  > .wp-block-column:not(.ne-col-images) > .wp-block-group:last-child > p {
  margin-top: var(--ne-space-xs) !important;
}

/* Mobile: stack the columns and move the photo mosaic above the text.

   BREAKPOINT — 781.98px, not the theme's usual 47.9375rem / 767px.

   Two deliberate choices, both easy to "tidy" into bugs:

   1. 781, not 767. Core stacks Columns blocks at max-width 781px with
      `flex-basis: 100% !important`, which beats the inline percentage widths.
      Firing these rules at 767px left a 14px band (768–781px) where the columns
      had already stacked but the indent was still applied and the image still
      sat below the body copy. Matching core keeps stacking and the layout that
      depends on it as one event. Overriding core back to 767px instead would
      mean restating every pattern's flex-basis percentage with !important.

   2. The .98. Core's pair is `max-width: 781px` / `min-width: 782px`, which
      matches NEITHER at a fractional viewport width — 781.5px happens at some
      zoom levels and device pixel ratios. In that gap the columns neither
      stacked nor sat properly side by side. `.98` is the standard complement
      (Bootstrap does the same) and closes it.

   Written in px to match core's unit. Do not convert to rem. */
@media (max-width: 781.98px) {
  .ne-services-split {
    flex-direction: column;
  }

  .ne-services-split > .wp-block-column {
    flex-basis: auto !important;
    width: 100% !important;
  }

  .ne-services-split .ne-col-images {
    order: -1;
  }

  /* No bottom-anchoring needed once stacked */
  .ne-services-split .wp-block-column:not(.ne-col-images) > p:first-of-type {
    margin-top: 0 !important;
  }

  /* Drop the body-copy + button indent once columns stack full-width */
  .ne-services-split .wp-block-column:not(.ne-col-images) > p,
  .ne-services-split .wp-block-column:not(.ne-col-images) > .wp-block-buttons,
  .ne-about-split .wp-block-column > p,
  .ne-about-split .wp-block-column > .wp-block-buttons,
  .ne-split-col > .wp-block-group + .wp-block-group {
    padding-left: 0;
  }

  /* Mosaic splits: the bottom-anchored body group takes its indent from a
     four-class selector, which outranks the reset above and would otherwise
     keep an --ne-space-xl indent on a phone. Restate at matching specificity.

     margin-top goes with it: `auto` bottom-aligns the copy against the photo
     while the two sit side by side, but once the columns wrap it is measured
     against the flex line instead and opens a dead gap above the copy. */
  .ne-section .wp-block-columns:has(> .wp-block-column > .wp-block-columns)
    > .wp-block-column:not(:has(.wp-block-columns)) > .wp-block-group:last-child,
  .ne-section .wp-block-columns:has(> .ne-col-images)
    > .wp-block-column:not(.ne-col-images) > .wp-block-group:last-child {
    margin-top: 0;
    padding-left: 0;
  }
}

/* ============================================================
   IMAGE/TEXT SPLITS ON MOBILE
   ============================================================
   Covers "Split — text left, images right" and "Split — images left, text
   right". Stacked naively, the whole text column lands before or after the
   whole image column, so the reader gets a wall of copy and then a photo with
   no idea what it illustrates.

   Interleave instead: eyebrow + headline, then the image(s), then the body copy
   and CTA. The photo arrives as evidence for the headline rather than as an
   afterthought.

   Selecting the two columns without adding classes to existing page content:

   - The media column is one whose children are ALL images. That deliberately
     excludes the booking sections, where a column mixes copy with a photo, and
     it holds up whether the pattern ships one image or two (live pages have
     both).
   - The text column is either `.ne-split-col` (text-left variant, already
     classed) or the sibling after a media column that contains no image at all
     (images-left variant, which carries no class).

   `:has()` cannot be nested inside `:has()`, so there is no way to scope this
   by the parent columns block — hence the sibling combinator.

   `display: contents` dissolves the text column so its own children become
   flex items of the columns block and can be ordered around the image. */

/* HISTORY — a `.wp-block-columns.is-not-stacked-on-mobile` override used to sit
   here, neutralising a stray class that several patterns and the two homepage
   template parts emitted in their static HTML while their block attribute said
   `isStackedOnMobile: true`. The class won, so those sections never stacked.

   Source markup is fixed, and the copies in the database were cleared with
   tools/strip-is-not-stacked-on-mobile.php, so the override is gone. Revisions
   still carry the class by design — restoring a pre-fix revision reintroduces
   it, and the symptom is a split section that refuses to stack below 782px.
   Re-run that script if it ever comes back. Do NOT fix it by re-saving the page
   in the editor: that promotes the class to an explicit `className` attribute
   instead of removing it (see the script's header for the mechanism). */

/* Breakpoint matches the block above — see the note there on why this is
   781px and not 47.9375rem. */
@media (max-width: 781.98px) {
  /* The media column: either one whose children are all images, or one marked
     .ne-col-images (the five-image mosaic, whose single child is a Group and so
     fails the all-images test). The homepage Services split is excluded — it
     lifts its mosaic to the very top with order:-1 instead of interleaving. */
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))),
  .wp-block-columns:not(.ne-services-split) > .ne-col-images {
    order: 3;
  }

  .ne-split-col,
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))) ~ .wp-block-column:not(:has(.wp-block-image)) {
    display: contents;
  }

  /* Everything from the text column falls below the image unless promoted. */
  .ne-split-col > *,
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))) ~ .wp-block-column:not(:has(.wp-block-image)) > * {
    order: 4;
    flex-basis: 100%;
    width: 100%;
  }

  /* The eyebrow — its own group in both variants. In the text-left variant this
     group also contains the headline, which is why it alone reaches the top. */
  .ne-split-col > .wp-block-group:first-child,
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))) ~ .wp-block-column:not(:has(.wp-block-image)) > .wp-block-group:first-child {
    order: 1;
  }

  .ne-split-col > .wp-block-heading,
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))) ~ .wp-block-column:not(:has(.wp-block-image)) > .wp-block-heading {
    order: 2;
  }

  /* Stacked columns are full-bleed, so body copy that was comfortable in a 45%
     column now runs the width of the viewport — around 85 characters at the top
     of this range, well past a readable measure. Cap it. No effect on a phone
     (60ch exceeds the viewport there); this only bites in the ~600–767px band
     just below the tablet layout. */
  .ne-split-col p,
  .wp-block-column:has(> .wp-block-image):not(:has(> :not(.wp-block-image))) ~ .wp-block-column:not(:has(.wp-block-image)) p,
  .ne-services-split .wp-block-column:not(.ne-col-images) > p,
  .ne-about-split .wp-block-column > p {
    max-width: 60ch;
  }
}

/* ============================================================
   HOMEPAGE SERVICES CARDS (For Buyers / Sellers / Investors)
   ============================================================
   Equalize the text block so the images (and buttons) line up
   across all three columns — the row of text scales to the
   tallest block, then the image + button sit at a shared baseline.
*/
.ne-svc-cards .wp-block-column {
  display: flex;
}

.ne-svc-card {
  display: flex;
  flex: 1;
  flex-direction: column;
  text-align: left;
}

/* ============================================================
   RECRUITING / JOIN THE TEAM (Our Team page, editable via ACF)
   Reuses the team-feature split layout (copy left, photo right) so it
   matches the bios. Sits between the bios and the contact block.
   ============================================================ */
.ne-recruiting__wrap {
  width: min(100% - 2 * var(--ne-site-gutter), var(--ne-container-width));
  margin-inline: auto;
  margin-bottom: var(--ne-space-section);
}

/* Headline stays modest (30px) — not the oversized feature size. */
.ne-recruiting .team-feature__name {
  font-size: 1.875rem;
}

/* Nudge the photo's focal point lower. */
.ne-recruiting .team-feature__media img {
  object-fit: cover;
  object-position: center 80%;
}

/* Body follows the headline rather than pinning to the bottom of the column. */
.ne-recruiting .team-feature__bottom {
  margin-top: var(--ne-space-md);
}

.ne-recruiting__body p + p,
.ne-recruiting__body p + ul {
  margin-top: var(--ne-space-sm);
}

.ne-recruiting__body ul {
  margin: var(--ne-space-sm) 0 0;
  padding-left: 1.25rem;
  display: grid;
  gap: var(--ne-space-xs);
}

/* The group uses WP's constrained layout, which sets margin-inline:auto on
   its children — inside a flex column that centers the shrink-wrapped ones
   (heading, buttons). Force the margins off so everything is left-justified. */
.ne-svc-card > * {
  margin-inline: 0 !important;
  max-width: none;
}

/* The description absorbs the extra height, pushing the image and
   button to a common baseline across the three cards. */
.ne-svc-card > p {
  margin-bottom: auto;
}

/* ============================================================
   HOMEPAGE CONTACT / INTAKE CTA (full-bleed dusk house + form)
   ============================================================ */
.ne-contact {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: min(90vh, 52rem);
  padding-top: var(--ne-space-section);
  padding-bottom: var(--ne-space-xl);
  overflow: clip;
  background: var(--ne-color-accent-strong); /* fallback before image loads */
  /* Anchor target (#get-in-touch): clear the sticky site header so the
     section kicker isn't tucked under it when jumped to. */
  scroll-margin-top: calc(var(--ne-header-height, 5.5rem) + var(--ne-space-md, 1.5rem));
}

/* When the full-bleed contact section is the last thing before the footer,
   drop the main's bottom padding so it meets the footer with no gap. Applies
   to any page that ends with this section (homepage, Our Team, etc.). */
.site-main:has(> .ne-contact:last-child) {
  padding-bottom: 0;
}

.ne-contact__media,
.ne-contact__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.ne-contact__media {
  z-index: -2;
}

.ne-contact__media img {
  object-fit: cover;
}

/* Legibility veil over the photo */
.ne-contact__overlay {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(15, 16, 12, 0.4) 0%, rgba(15, 16, 12, 0.68) 100%);
}

.ne-contact__inner {
  width: min(100% - (2 * var(--ne-site-gutter)), 40rem);
  display: grid;
  gap: var(--ne-space-md);
  text-align: center;
  color: var(--ne-color-accent-contrast);
}

.ne-contact__kicker {
  margin: 0;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: rgba(240, 235, 233, 0.85);
}

.ne-contact__title {
  margin: 0;
  font-size: var(--ne-font-size-display);
  line-height: 1.1;
}

.ne-contact__intro {
  margin: 0 auto;
  max-width: 48ch;
  color: rgba(240, 235, 233, 0.85);
}

.ne-contact__form {
  margin-top: var(--ne-space-md);
  text-align: left;
}

/* Fluent Forms — make it legible on the dark photo */
.ne-contact__form .fluentform label,
.ne-contact__form .fluentform .ff-el-input--label label {
  color: var(--ne-color-accent-contrast) !important;
}

.ne-contact__form .fluentform input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]),
.ne-contact__form .fluentform textarea,
.ne-contact__form .fluentform select {
  background-color: rgba(240, 235, 233, 0.96);
  border: 1px solid rgba(240, 235, 233, 0.5);
  color: #1b1b1b;
}

.ne-contact__form .fluentform input::placeholder,
.ne-contact__form .fluentform textarea::placeholder {
  color: rgba(0, 0, 0, 0.5);
}

/* Submit button — cream on the dark section for contrast */
.ne-contact__form .fluentform .ff-btn-submit,
.ne-contact__form .fluentform button[type="submit"] {
  background: var(--ne-color-accent-contrast) !important;
  color: var(--ne-color-accent) !important;
  border: 1px solid var(--ne-color-accent-contrast) !important;
  font-family: var(--ne-font-mono), monospace;
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
}

/* == Full-screen mobile menu overlay == */

.ne-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  background: var(--ne-color-accent-strong);
  color: var(--ne-color-accent-contrast);
  overflow-y: auto;
  overflow-x: hidden;
  /* Hidden state */
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.ne-menu-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

.ne-menu-overlay__inner {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  min-height: 100dvh;
  padding: var(--ne-space-sm) var(--ne-space-md);
  padding-top: max(var(--ne-space-sm), env(safe-area-inset-top));
  padding-bottom: max(var(--ne-space-lg), env(safe-area-inset-bottom));
  gap: var(--ne-space-md);
}

/* Top bar: cream logo + close button */
.ne-menu-overlay__topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 3.5rem;
  flex-shrink: 0;
}

.ne-menu-overlay__logo-link img {
  display: block;
  height: 30px;
  width: auto;
}

/* Close button — draws an × from two rotated lines */
.ne-menu-overlay__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  margin: -0.5rem;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--ne-color-accent-contrast);
  flex-shrink: 0;
}

.ne-menu-overlay__close-icon {
  position: relative;
  display: block;
  width: 22px;
  height: 22px;
}

.ne-menu-overlay__close-icon::before,
.ne-menu-overlay__close-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}

.ne-menu-overlay__close-icon::before { transform: translateY(-50%) rotate(45deg); }
.ne-menu-overlay__close-icon::after  { transform: translateY(-50%) rotate(-45deg); }

/* Primary nav — big serif items */
.ne-menu-overlay__nav {
  flex-shrink: 0;
  margin-top: var(--ne-space-md);
}

.ne-menu-overlay__nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ne-menu-overlay__nav li {
  border-top: 1px solid rgba(240, 235, 233, 0.12);
}

.ne-menu-overlay__nav li:last-child {
  border-bottom: 1px solid rgba(240, 235, 233, 0.12);
}

.ne-menu-overlay__nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: 0.9rem;
  font-family: var(--ne-font-heading);
  font-size: clamp(1.75rem, 7vw, 2.5rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ne-color-accent-contrast);
  text-decoration: none;
  transition: opacity 0.15s ease;
}

.ne-menu-overlay__nav a::after {
  content: '→';
  font-family: var(--ne-font-body);
  font-size: 1.125rem;
  opacity: 0.4;
  flex-shrink: 0;
  margin-left: var(--ne-space-sm);
}

.ne-menu-overlay__nav a:hover,
.ne-menu-overlay__nav a:focus-visible {
  opacity: 0.65;
  outline: none;
}

/* Staggered fade-in for nav items */
.ne-menu-overlay__nav li {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.22s ease, transform 0.22s ease;
}

.ne-menu-overlay.is-open .ne-menu-overlay__nav li {
  opacity: 1;
  transform: translateY(0);
}

.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(1) { transition-delay: 0.06s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(2) { transition-delay: 0.10s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(3) { transition-delay: 0.14s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(4) { transition-delay: 0.18s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(5) { transition-delay: 0.22s; }
/* The menu is editor-controlled now, so cover more items than the five that
   used to be hardcoded. Anything beyond this still fades in, just unstaggered. */
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(6) { transition-delay: 0.26s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(7) { transition-delay: 0.30s; }
.ne-menu-overlay.is-open .ne-menu-overlay__nav li:nth-child(8) { transition-delay: 0.34s; }

/* Secondary links — small mono text */
.ne-menu-overlay__secondary {
  display: flex;
  gap: var(--ne-space-md);
  flex-shrink: 0;
  margin-top: var(--ne-space-sm);
}

/* wp_nav_menu emits <ul><li>; flatten it so the row looks as it did when
   these were bare anchors. */
.ne-menu-overlay__secondary ul {
  display: flex;
  gap: var(--ne-space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ne-menu-overlay__secondary a {
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(240, 235, 233, 0.5);
  text-decoration: none;
  transition: color 0.15s ease;
}

.ne-menu-overlay__secondary a:hover {
  color: var(--ne-color-accent-contrast);
}

/* Footer: contact info + social — pushed to bottom */
.ne-menu-overlay__footer {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-sm);
  padding-top: var(--ne-space-md);
  border-top: 1px solid rgba(240, 235, 233, 0.12);
}

.ne-menu-overlay__contact {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.ne-menu-overlay__contact a {
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-sm);
  color: rgba(240, 235, 233, 0.65);
  text-decoration: none;
  transition: color 0.15s ease;
}

.ne-menu-overlay__contact a:hover {
  color: var(--ne-color-accent-contrast);
}

.ne-menu-overlay__social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ne-space-sm);
}

.ne-menu-overlay__social a {
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(240, 235, 233, 0.45);
  text-decoration: none;
  transition: color 0.15s ease;
}

.ne-menu-overlay__social a:hover {
  color: var(--ne-color-accent-contrast);
}

/* == Desktop overlay: no backdrop — only the sliding right panel == */
@media (min-width: 64rem) {
  /* Overlay wrapper is invisible and non-blocking — just a clip container */
  .ne-menu-overlay {
    display: block;
    background: transparent;
    overflow: hidden; /* clip the sliding inner panel */
    pointer-events: none; /* clicks pass through to page content */
  }

  /* Keep pointer-events off the wrapper even when open */
  .ne-menu-overlay.is-open {
    pointer-events: none;
  }

  /* WP admin bar is 32px tall on desktop — start the overlay below it */
  body.admin-bar .ne-menu-overlay {
    top: 32px;
  }

  /* Inner panel slides in from the right and captures its own clicks */
  .ne-menu-overlay__inner {
    pointer-events: auto;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(520px, 40vw);
    min-height: unset;
    background: var(--ne-color-accent-strong);
    transform: translateX(100%);
    transition: transform 0.38s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: -16px 0 60px rgba(15, 16, 12, 0.35);
    overflow-y: auto;
  }

  .ne-menu-overlay.is-open .ne-menu-overlay__inner {
    transform: translateX(0);
  }

  /* Taller nav items on the larger canvas */
  .ne-menu-overlay__nav a {
    font-size: clamp(1.75rem, 2.5vw, 2.25rem);
    padding-block: 0.75rem;
  }
}

/* ============================================================
   SITE FOOTER
   ============================================================ */

/* ── Upper content row ── */
.site-footer__upper {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--ne-space-xl);
  padding-block: var(--ne-space-xl);
  color: var(--ne-color-accent-contrast);
}

/* ── Left column: bird mark + nav ── */
.site-footer__left {
  display: flex;
  align-items: flex-start;
  gap: var(--ne-space-lg);
}

.site-footer__bird {
  display: block;
  width: 6.875rem; /* 110px at 90rem (1440px) container — matches Figma */
  height: auto;
  flex-shrink: 0;
}

/* ── Nav links (left column) ── */
.site-footer__nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-xs);
}

.site-footer__nav-list a {
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-accent-contrast);
  text-decoration: none;
  opacity: 0.8;
  transition: opacity 0.2s ease;
}

.site-footer__nav-list a:hover,
.site-footer__nav-list a:focus {
  opacity: 1;
}

/* ── Right column: newsletter + legal ── */
.site-footer__right {
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-md);
  max-width: 32rem;
}

.site-footer__newsletter-label {
  font-family: var(--ne-font-heading);
  font-size: var(--ne-font-size-md);
  color: var(--ne-color-accent-contrast);
  margin: 0;
}

/* ── Newsletter form (Fluent Forms #2) ──
   The markup here is Fluent Forms', not ours, so every rule is scoped under
   .site-footer__form. That scoping is load-bearing: the contact form
   (#get-in-touch) is also a Fluent Form on a dark field, and an unscoped
   override would drag the footer's smaller mono treatment onto it.

   Fluent ships the form as TWO columns — a name group (which splits into first
   and last internally) beside email. Weighting the first column double is what
   turns that into the three equal fields the design calls for, without asking
   the editor to rebuild the form.

   !important appears only on the submit button: the form carries per-form
   custom CSS from the Fluent builder (.ff_btn_style .wpf_has_custom_css),
   which otherwise wins on specificity. */
.site-footer__form {
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-sm);
}

.site-footer__form .frm-fluent-form {
  margin: 0;
}

/* The form's builder styles paint the wrapper Woodland Moss (#3b432b), which
   reads as a faint lighter panel against the footer's darker green. The footer
   supplies the background here. */
.site-footer__form .fluentform {
  background: transparent !important;
  padding: 0 !important;
}

/* Fluent's per-form builder styles ship as
   `.fluentform_wrapper_2.ffs_custom_wrap .ff-el-input--content input` (0,3,1)
   and load after the theme, so plain scoped rules lose. Same fight
   .ne-contact__form already picks, resolved the same way — !important on the
   properties that matter, scoped so it cannot escape the footer. */
.site-footer__form .frm-fluent-form .ff-t-container {
  display: flex !important;
  gap: var(--ne-space-xs);
  margin: 0 !important;
}

.site-footer__form .frm-fluent-form .ff-t-cell {
  flex: 1 1 0 !important;
  width: auto !important;
  min-width: 0;
  padding: 0 !important;
}

/* The name group holds two fields; email holds one. */
.site-footer__form .frm-fluent-form .ff-t-column-1 {
  flex: 2 1 0 !important;
}

.site-footer__form .frm-fluent-form .ff-el-group {
  margin: 0 !important;
}

/* The form hides its labels via display:none, which also hides them from
   screen readers — a placeholder is not an accessible label. Put them back
   visually-hidden instead. */
.site-footer__form .frm-fluent-form .ff-el-form-hide_label .ff-el-input--label {
  position: absolute !important;
  display: block !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.site-footer__form .frm-fluent-form .ff-el-input--content input.ff-el-form-control {
  width: 100%;
  height: auto !important;
  background: transparent !important;
  background-color: transparent !important;
  border: 1px solid rgba(240, 235, 233, 0.35) !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  color: var(--ne-color-accent-contrast) !important;
  font-family: var(--ne-font-mono), monospace !important;
  font-size: var(--ne-font-size-xs) !important;
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  padding: 0.6rem 0.75rem !important;
  outline: none;
  transition: border-color 0.2s ease;
}

.site-footer__form .frm-fluent-form .ff-el-form-control::placeholder {
  color: rgba(240, 235, 233, 0.45) !important;
  text-transform: uppercase;
}

.site-footer__form .frm-fluent-form .ff-el-input--content input.ff-el-form-control:focus {
  background: transparent !important;
  border-color: var(--ne-color-accent-contrast) !important;
  box-shadow: none !important;
  outline: none;
}

.site-footer__form .frm-fluent-form .ff_submit_btn_wrapper {
  margin: var(--ne-space-sm) 0 0 !important;
  text-align: right;
}

.site-footer__form .frm-fluent-form .ff-btn-submit {
  background: transparent !important;
  border: 1px solid rgba(240, 235, 233, 0.45) !important;
  border-radius: 0 !important;
  color: var(--ne-color-accent-contrast) !important;
  cursor: pointer;
  font-family: var(--ne-font-mono), monospace !important;
  font-size: var(--ne-font-size-xs) !important;
  font-weight: 400 !important;
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  padding: 0.6rem 1.25rem !important;
  transition: background 0.2s ease, border-color 0.2s ease;
}

/* The diamond the hand-built button used to carry, kept out of the form's own
   label so the button text stays clean in the Fluent builder. */
.site-footer__form .frm-fluent-form .ff-btn-submit::before {
  content: "\25C6";
  margin-right: 0.45em;
}

/* The form's builder CSS ships
   `form.fluent_form_2 .wpf_has_custom_css.ff-btn-submit:hover { min-width: 100% }`,
   which snaps the button to full width on hover. Almost certainly a stray value
   in the Fluent button hover panel rather than an intent — neutralised here so
   the button keeps its size. Clearing it in the builder would fix it for every
   placement of this form; this override only covers the footer. */
.site-footer__form .frm-fluent-form .ff-btn-submit {
  min-width: 0 !important;
  width: auto !important;
}

.site-footer__form .frm-fluent-form .ff-btn-submit:hover,
.site-footer__form .frm-fluent-form .ff-btn-submit:focus {
  background: rgba(240, 235, 233, 0.1) !important;
  border-color: var(--ne-color-accent-contrast) !important;
  color: var(--ne-color-accent-contrast) !important;
  min-width: 0 !important;
  width: auto !important;
}

/* Validation and confirmation text has to stay legible on the dark field —
   Fluent's defaults are tuned for a white background. */
.site-footer__form .ff-el-is-error .ff-el-form-control {
  border-color: #e5a5a0;
}

.site-footer__form .error,
.site-footer__form .text-danger {
  color: #e5a5a0;
  font-family: var(--ne-font-body);
  font-size: var(--ne-font-size-xs);
  letter-spacing: normal;
  text-transform: none;
}

.site-footer__form .ff-message-success,
.site-footer__form .ff_submit_success {
  background: transparent;
  border: 1px solid rgba(240, 235, 233, 0.35);
  color: var(--ne-color-accent-contrast);
  font-family: var(--ne-font-body);
  font-size: var(--ne-font-size-sm);
  padding: var(--ne-space-sm);
}

/* Legal links */
/* ── Legal / disclosure ──
   Brokerage license on the left, trademark marks on the right. Deliberately the
   quietest type on the page: this is required disclosure, so it has to be
   present and legible, but it should never compete with the newsletter sign-up
   above it. Held at 0.5 opacity — the same weight the old Privacy/Terms links
   carried — rather than shrunk further, because compliance copy that can't be
   read defeats its own purpose.

   `align-items: center` rather than baseline: the marks are images with no text
   baseline to align to, so centring the two columns against each other is the
   only alignment that stays put as the license line changes its wrap count. */
.site-footer__legal {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ne-space-md);
  margin-top: var(--ne-space-xs);
  padding-top: var(--ne-space-md);
  border-top: 1px solid var(--ne-color-rule-inverse);
}

.site-footer__license {
  /* Takes the slack, and may shrink — the marks must not. */
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: rgba(240, 235, 233, 0.5);
}

/* ── REALTOR® and Equal Housing Opportunity marks ──
   Scaled and made semi-transparent, but never recoloured in CSS — the artwork
   itself is already Hearth Linen. These are trademarks and shouldn't be
   restyled beyond size and opacity. */
.site-footer__marks {
  /* Never squeezed by the license text beside it, and never stretched. */
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--ne-space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer__mark {
  display: flex;
  align-items: center;
}

/* Height is set by legibility, not by taste. Both lockups are ~16% wordmark by
   height, so at the 2.25rem these started at, "REALTOR®" and "EQUAL HOUSING
   OPPORTUNITY" resolved to roughly 6px and turned to mush. NAR's rule is that
   the block R, the term REALTOR® and the ® all stay readable at reduced sizes,
   which puts the floor near 3.25rem. Don't shrink these to balance the layout —
   move something else. */
.site-footer__mark img {
  display: block;
  width: auto;
  height: 3.25rem;
  /* Trademarks need real contrast — NAR asks for adequate contrast against the
     background — so these sit brighter than the surrounding legal copy rather
     than fading into it. */
  opacity: 0.78;
  transition: opacity 0.2s ease;
}

.site-footer__mark img:hover {
  opacity: 1;
}

/* Shown only when the SVG hasn't been added yet — the disclosure still reads,
   it just reads as words. */
.site-footer__mark-fallback {
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: rgba(240, 235, 233, 0.5);
}

/* ── Policy links ──
   The links returned 2026-08-14, pointing at /policies/ anchors. They sit in
   their own row BELOW .site-footer__legal rather than inside it: that row is a
   two-column flex (licence takes the slack, marks never shrink) and a third
   child would fight both rules.

   Same muted treatment as the licence line — these are disclosure, not
   navigation, and shouldn't compete with the wordmark beneath them. */
.site-footer__legal a,
.site-footer__legal-links a {
  color: rgba(240, 235, 233, 0.5);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: color 0.2s ease;
}

.site-footer__legal a:hover,
.site-footer__legal a:focus,
.site-footer__legal-links a:hover,
.site-footer__legal-links a:focus {
  color: var(--ne-color-accent-contrast);
}

.site-footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  /* Row gap is smaller than column gap so a wrapped second line sits close
     enough to read as one group. */
  gap: var(--ne-space-xs) var(--ne-space-md);
  margin: var(--ne-space-sm) 0 0;
  padding: 0;
  list-style: none;
  font-family: var(--ne-font-mono), monospace;
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
}

/* ── Full-width wordmark lockup ── */
/*
 * footer-lockup-white.svg is a single combined SVG:
 *   bird (58.44 wide) + gap (40.6) + NEW ENGLANDER (1000 wide)
 *   viewBox: 0 0 1099.04 88.93  → aspect ratio 12.36:1
 * Setting width:100% on the img fills the viewport edge-to-edge.
 */
.ne-footer-lockup {
  width: 100%;
  line-height: 0; /* kill inline gap below img */
}

.ne-footer-lockup__wordmark {
  display: block;
  width: 100%;
  height: auto;
}

/* ─── Bird Clip-Path Scroll Reveal ─── */

/*
 * Tall outer section creates the scroll distance for the animation.
 * The inner sticky container holds the photo and stays pinned to the
 * viewport while the user scrolls through the section height.
 */
.ne-bird-reveal {
  position: relative;
  height: 180vh;
}

/* Zero-size SVG that holds the <clipPath> definition */
.ne-bird-reveal__defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Sticky panel — always 100vh, Hearth Linen background shows through clipped areas */
.ne-bird-reveal__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--ne-color-background);
}

/* Full-bleed nature photo, clipped to the bird shape by default */
.ne-bird-reveal__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  clip-path: url(#ne-bird-clip);
  will-change: clip-path;
}

/* ============================================================
   Team Hero — group photo + flanking text (top of archive-person)
   ============================================================ */

.team-hero {
  padding-top: var(--ne-space-section-half);
  padding-bottom: var(--ne-space-section-half);
}

.team-hero__inner {
  width: min(100% - 2 * var(--ne-site-gutter), var(--ne-container-width));
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.team-hero__eyebrow {
  margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
  text-align: center;
}

.team-hero__body {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
  width: 100%;
}

.team-hero__col {
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}

.team-hero__tagline {
  margin: 0;
  font-family: var(--ne-font-heading);
  font-size: clamp(1rem, 1.6vw, 1.5rem);
  line-height: var(--ne-line-height-heading);
  letter-spacing: var(--ne-letter-spacing-tight);
  color: var(--ne-color-text);
}

.team-hero__media {
  overflow: hidden;
  aspect-ratio: 5 / 6;
}

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

.team-hero__caption {
  margin: clamp(1.5rem, 3vw, 2.5rem) 0 0;
  max-width: 42rem;
  font-size: var(--ne-font-size-sm);
  line-height: var(--ne-line-height-loose);
  color: var(--ne-color-muted);
  text-align: center;
}

@media (max-width: 55rem) {
  .team-hero__body {
    grid-template-columns: 1fr;
  }
  .team-hero__media {
    order: -1;
  }
}

/* ============================================================
   Team Feature — alternating two-column layout (archive-person)
   ============================================================ */

/* Outer container — matches site content width with outer gutters */
.team-features {
  width: min(100% - 2 * var(--ne-site-gutter), var(--ne-container-width));
  margin-inline: auto;
  margin-top: var(--ne-space-section-half);
  margin-bottom: var(--ne-space-section);
}

/* Space between consecutive profiles */
.team-feature + .team-feature {
  margin-top: 3rem;
}

/* Two-column grid */
.team-feature__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas: "content media";
  min-height: 37.5rem; /* 600px */
}

.team-feature--reversed .team-feature__inner {
  grid-template-areas: "media content";
}

/* Content column — top-aligned, bio section pushed to bottom */
.team-feature__content {
  grid-area: content;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 0 clamp(2.5rem, 6vw, 6rem) clamp(2rem, 4vw, 3.5rem);
}

/* Photo column */
.team-feature__media {
  grid-area: media;
  position: relative;
  overflow: hidden;
}

/* Absolutely positioned so a tall portrait fills the cell without
   stretching the row — every feature keeps the grid's ~600px height. */
.team-feature__media img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

/* Jack's portrait: pull the crop up toward the top so his head is in frame
   (was cropping off the top). Tied to his post. */
#post-22 .team-feature__media img {
  object-position: center 18%;
}

/* Kicker — sits above the rule */
.team-feature__kicker {
  margin: 0 0 0.5rem;
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: var(--ne-letter-spacing-wide);
  text-transform: uppercase;
  color: var(--ne-color-muted);
}

/* Dividing rule — full width of text column */
.team-feature__rule {
  border: none;
  border-top: 1px solid var(--ne-color-border);
  margin: 0 0 0.75rem;
  width: 100%;
}

/* Name — directly below the rule */
.team-feature__name {
  margin: 0;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: var(--ne-line-height-heading);
  letter-spacing: var(--ne-letter-spacing-tight);
}

/* Bottom section — bio + contact + social, pushed to column bottom */
.team-feature__bottom {
  margin-top: auto;
  padding-top: var(--ne-space-md);
  display: flex;
  flex-direction: column;
  gap: var(--ne-space-md);
}

/* Bio */
.team-feature__bio {
  color: var(--ne-color-muted);
  line-height: var(--ne-line-height-loose);
}

.team-feature__bio p {
  margin: 0;
}

.team-feature__bio p + p {
  margin-top: var(--ne-space-sm);
}

/* Contact list */
.team-feature__contact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--ne-font-size-sm);
}

.team-feature__license {
  width: 100%;
  font-family: var(--ne-font-mono);
  font-size: var(--ne-font-size-xs);
  letter-spacing: 0.06em;
  color: var(--ne-color-muted);
}

/* Responsive — stack photo above, text below */
@media (max-width: 55rem) {
  .team-feature__inner,
  .team-feature--reversed .team-feature__inner {
    grid-template-columns: 1fr;
    grid-template-areas: "media" "content";
    min-height: auto;
  }

  .team-feature__media {
    min-height: 22rem;
  }

  .team-feature__content {
    padding: var(--ne-space-section-half) var(--ne-site-gutter);
  }
}

/* When JS marks the reveal complete, drop the clip entirely */
.ne-bird-reveal__photo.is-revealed {
  clip-path: none;
}

/* Mobile: reduce scroll distance a little */
@media (max-width: 47.9375rem) {
  .ne-bird-reveal {
    height: 150vh;
  }
}

/* Mobile: upper row stacks */
@media (max-width: 47.9375rem) {
  .site-footer__upper {
    flex-direction: column;
    gap: var(--ne-space-lg);
  }

  .site-footer__right {
    max-width: 100%;
  }

  .site-footer__bird {
    width: 4.5rem;
  }

  /* Stack First / Last / Email. The nested name container needs it too, or
     first and last stay side by side inside a full-width column. */
  .site-footer__form .frm-fluent-form .ff-t-container {
    flex-direction: column !important;
    gap: var(--ne-space-xs);
  }

  /* The license line needs the full width once the footer is a single column —
     side by side with the marks it would wrap to four or five ragged lines. */
  .site-footer__legal {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ne-space-sm);
  }
}

/* ============================================================
   CALENDLY — lazy-loaded booking widget
   ============================================================
   The [ne_calendly] shortcode outputs a .ne-calendly placeholder
   with an inline reserved height (min-width + height set in PHP so
   the space is held before any script runs — no layout shift). JS
   injects the real Calendly widget on scroll. These rules style the
   placeholder and its loading state.
*/
.ne-calendly {
  position: relative;
  width: 100%;
  overflow: hidden;
  /* Inherit the section background so the widget area matches its surroundings. */
  background-color: transparent;
  /* The widget shares the page background, so a hairline is the only thing
     separating it from the section around it. Same token as every other rule
     on the site. It sits on the wrapper rather than the injected iframe so the
     frame is drawn immediately, around the reserved space, and does not pop in
     when Calendly finally loads. Global border-box keeps the outer height at
     the reserved value, so the border costs no layout shift. */
  border: 1px solid var(--ne-color-rule);
  /* When used as an anchor target, offset so it clears the sticky header. */
  scroll-margin-top: calc(var(--ne-header-height, 5.5rem) + var(--ne-space-md, 1.5rem));
}

/* On a dark section the black-alpha hairline disappears; flip to the inverse. */
.site-main--dark .ne-calendly,
.ne-section--dark .ne-calendly,
.ne-hero-cover .ne-calendly {
  border-color: var(--ne-color-rule-inverse);
}

/* Centred loading indicator, shown until the widget is injected. */
.ne-calendly__loading {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ne-space-sm, 0.75rem);
  color: var(--ne-color-muted);
  font-family: var(--ne-font-mono, monospace);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
}

/* Once JS replaces the inner markup, the loading node is gone; if the
   widget host is present, hide any lingering placeholder chrome. */
.ne-calendly[data-ne-calendly="loaded"] {
  background-color: transparent;
}

.ne-calendly__spinner {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  border: 2px solid var(--ne-color-border);
  border-top-color: var(--ne-color-accent);
  animation: ne-calendly-spin 0.8s linear infinite;
}

@keyframes ne-calendly-spin {
  to { transform: rotate(360deg); }
}

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

.ne-calendly__fallback {
  display: inline-block;
  margin: auto;
  color: var(--ne-color-accent);
  font-weight: 600;
}

/* ============================================================
   ACCORDION (core wp-block-accordion) — FAQ styling
   ============================================================
   Serif question, a chevron that points right when collapsed and
   down when open (replacing the default "+"), a hairline divider
   under every item, and a muted sans-serif answer panel. Selectors
   are scoped under .wp-block-accordion to win over core block CSS.
*/

/* Divider under each item (open or closed).

   The item is also the engine of the open/close animation. Core hides the
   panel with `display: none` on `[inert]`, and `display` cannot be transitioned
   — so instead the item becomes a two-row grid (heading, panel) and the panel
   row animates between `0fr` and `1fr`. That interpolates to the panel's real
   content height without anyone having to measure it in JS, and without
   hard-coding a max-height that silently truncates a long answer.

   This works because a core accordion item has exactly two direct children:
   the h3 heading and the panel div. If a future WordPress release adds a third,
   revisit `grid-template-rows` here. */
.wp-block-accordion .wp-block-accordion-item {
  border-bottom: 1px solid var(--ne-color-border);
  /* Remove the block gap between items so the padding around each divider is
     symmetric (the gap otherwise adds space only above each item). */
  margin-block-start: 0;
  margin-top: 0;

  display: grid;
  /* minmax(0, …) rather than a bare 0fr/1fr: a flexible track's automatic
     minimum is its content's min-content size, so without this the row can
     refuse to go below the tallest unbreakable word in the answer and the
     panel snaps open a few dozen pixels before it starts animating. Pinning
     the minimum to 0 guarantees the slide starts from a true zero. */
  grid-template-rows: auto minmax(0, 0fr);
  transition: grid-template-rows 0.22s ease;
}

.wp-block-accordion .wp-block-accordion-item.is-open {
  grid-template-rows: auto minmax(0, 1fr);
}

.wp-block-accordion .wp-block-accordion-heading {
  margin: 0;
}

/* Question row: title left, chevron right */
.wp-block-accordion .wp-block-accordion-heading__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ne-space-md);
  width: 100%;
  padding-block: 0.85rem;
  padding-inline: 0;
  background: transparent;
  border: 0;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.wp-block-accordion .wp-block-accordion-heading__toggle:hover,
.wp-block-accordion .wp-block-accordion-heading__toggle:focus,
.wp-block-accordion .wp-block-accordion-heading__toggle:hover .wp-block-accordion-heading__toggle-title {
  text-decoration: none;
}

.wp-block-accordion .wp-block-accordion-heading__toggle-title {
  font-family: var(--ne-font-heading), Georgia, serif;
  font-size: clamp(1.0625rem, 1.4vw, 1.2rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--ne-color-text);
}

.wp-block-accordion .wp-block-accordion-heading__toggle:hover .wp-block-accordion-heading__toggle-title {
  color: var(--ne-color-muted);
}

/* Chevron icon — hide the default "+" glyph, use an SVG chevron.
   Collapsed: points right. Open: rotate the whole icon 90deg to point down.
   Setting transform on the icon itself overrides the core block's own icon
   rotation (which would otherwise compound with ours). */
.wp-block-accordion .wp-block-accordion-heading__toggle-icon {
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
  font-size: 0;
  background-color: transparent;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 6 15 12 9 18'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 0.8rem 0.8rem;
  transform: rotate(0deg);
  transition: transform 0.25s ease;
}

.wp-block-accordion .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle-icon {
  transform: rotate(90deg);
}

/* Answer panel */
.wp-block-accordion .wp-block-accordion-panel {
  color: var(--ne-color-text);
  font-family: var(--ne-font-body), sans-serif;
  font-size: var(--ne-font-size-base);
  line-height: 1.5;

  /* Required by the 0fr/1fr grid animation: the row can only collapse to zero
     if its content is allowed to be clipped and isn't holding a minimum. */
  overflow: hidden;
  min-height: 0;

  /* NOTHING that affects the panel's own box may change between states, or the
     text moves instead of being revealed. Two offenders had to go:
       1. WordPress's 24px block gap lands here as `margin-block-start`, and
          core zeroes it only while `[inert]` is set. So `inert` flipping off
          added 24px in a single frame as the panel opened, and flipping on
          removed it in a single frame as it closed — the text appeared to fly
          up from the bottom on open and jump on close.
       2. An animated `padding-bottom` moved the text's own edges during the
          slide, which read as sliding rather than being uncovered.
     Both are pinned to zero here in every state. The spacing they used to
     provide is re-created as margins on the panel's children below, where it
     sits INSIDE the clipped area and is revealed with the text instead of
     pushing it around. */
  margin-block-start: 0;
  margin-top: 0;
  padding-top: 0;
  padding-bottom: 0;

  /* A touch of fade. Safe to transition — opacity moves nothing. */
  opacity: 1;
  transition: opacity 0.18s ease;
}

/* Core ships `.wp-block-accordion-panel[inert] { display: none }`, which would
   leave nothing to animate. Override it — `inert` is doing the accessibility
   work regardless: a collapsed panel stays untabbable and hidden from
   assistive tech whether or not it is `display: none`. */
.wp-block-accordion .wp-block-accordion-panel[inert],
.wp-block-accordion .wp-block-accordion-panel[aria-hidden="true"] {
  display: block;
}

.wp-block-accordion .wp-block-accordion-item:not(.is-open) .wp-block-accordion-panel {
  opacity: 0;
}

/* Respect a reduced-motion preference: snap open, no slide. */
@media (prefers-reduced-motion: reduce) {
  .wp-block-accordion .wp-block-accordion-item,
  .wp-block-accordion .wp-block-accordion-panel,
  .wp-block-accordion .wp-block-accordion-heading__toggle-icon {
    transition: none;
  }
}

/* The answer's breathing room lives on its first and last child rather than on
   the panel, so it is part of what gets clipped and revealed. Nothing about the
   panel's own box changes between open and closed, which is what makes the
   slide read as the text being uncovered rather than moving. */
/* 1.5rem reproduces the 24px block gap the panel used to get from WordPress,
   so removing that margin above didn't quietly tighten the design. */
.wp-block-accordion .wp-block-accordion-panel > :first-child {
  margin-top: 1.5rem;
}

.wp-block-accordion .wp-block-accordion-panel > :last-child {
  margin-bottom: 1rem;
}

/* ============================================================
   SPLIT — Text Left, Two Images Right: bottom-aligned copy
   ============================================================
   The left column stretches to the height of the stacked images
   (equal-height columns). Distribute its two groups so the eyebrow
   + heading sit at the top and the body copy + button bottom-align
   against the images. */
.ne-split-col {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

/* ============================================
   Canvas — Dark template (page-canvas-dark.php)
   ============================================
   Dark-mode content field for block-composed pages. Scoped to
   .site-main--dark so it only affects canvas pages using the dark template;
   the sticky linen header and the footer are unaffected. Pair with the dark
   pattern variants (Page Header — Dark, Two-Column Text — Dark), which bake
   the Hearth-Linen text and light hairlines into their own markup. These
   rules cover the field colour plus any generic blocks a page author drops
   in, so plain headings, paragraphs, links and buttons stay legible on the
   dark ground. */

/* Field colour matches the footer + bottom contact CTA (accent-strong,
   #2f3522) so the whole lower page reads as one continuous dark band. */
.site-main--dark {
  background: var(--ne-color-accent-strong);
  color: var(--ne-color-accent-contrast);
}

/* Content text → Hearth Linen (#F0EBE9). NOTE: WordPress' global stylesheet
   emits `.has-text-color{color:… !important}`, and every block that carries a
   custom colour also carries `has-text-color`; that !important rule beats
   plain inline colours. So the dark field must reassert its colours with
   !important and a touch more specificity (scoped to block wrappers, which
   also keeps the contact component below untouched). Per design, ALL light
   text on the dark field is the single Hearth Linen value — no muted greys —
   so headings, body copy, eyebrows, links and bold text are all #F0EBE9.

   `.ne-section--dark` is the portable form of the same thing: put it on any
   group or cover and that subtree becomes a dark field, without needing the
   page to use the dark template. Prefer it for new work — a section that owns
   its own colour does not need 182 inline `color:#f0ebe9` declarations in the
   markup underneath it, which is what the dark patterns used to carry. */
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) :is(h1, h2, h3, h4, h5, h6),
.site-main--dark > :is(h1, h2, h3, h4, h5, h6),
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) p,
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) li,
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) .has-mono-font-family,
.site-main--dark .has-muted-color,
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) strong,
.ne-section--dark,
.ne-section--dark :is(h1, h2, h3, h4, h5, h6),
.ne-section--dark p,
.ne-section--dark li,
.ne-section--dark strong,
.ne-section--dark .has-mono-font-family,
.ne-section--dark .has-muted-color {
  color: var(--ne-color-accent-contrast) !important;
}

.ne-section--dark {
  background: var(--ne-color-accent-strong);
}

/* Inline links: keep them within the linen family and underline for
   affordance, since the light-mode moss link colour has no contrast. */
.site-main--dark :is(.wp-block-group, .wp-block-column, .wp-block-cover) a:not(.wp-element-button):not(.wp-block-button__link),
.ne-section--dark a:not(.wp-element-button):not(.wp-block-button__link) {
  color: var(--ne-color-accent-contrast) !important;
  text-decoration-color: var(--ne-color-contrast-soft);
}

/* Hairline borders flip from black-alpha to linen-alpha so dividers such as
   the eyebrow rule remain visible. Covers both inline-styled group borders
   and the theme's scroll-scrubbed .ne-eyebrow-rule underline. */
.site-main--dark .wp-block-group[style*="border-bottom-color"],
.site-main--dark .wp-block-group[style*="border-top-color"],
.ne-section--dark .wp-block-group[style*="border-bottom-color"],
.ne-section--dark .wp-block-group[style*="border-top-color"] {
  border-bottom-color: var(--ne-color-rule-inverse) !important;
  border-top-color: var(--ne-color-rule-inverse) !important;
}
.site-main--dark .ne-eyebrow-rule::after,
.ne-section--dark .ne-eyebrow-rule::after {
  background: var(--ne-color-rule-inverse);
}

/* Text selection. The light-field default in base.css is Woodland Moss at 20%,
   which disappears here — it is moss on moss. Flip to linen so a drag-select is
   actually visible on the dark ground. ::selection does not inherit, so each
   dark scope needs its own rule. */
.site-main--dark ::selection,
.ne-section--dark ::selection,
.ne-hero-cover ::selection {
  background: var(--ne-color-selection-inverse);
  color: var(--ne-color-accent-contrast);
}

/* Buttons → filled Limestone-grey with dark Woodland-Moss text (per Figma),
   not the hollow outline. Reasserted past the global .has-text-color rule. */
.site-main--dark .wp-block-button .wp-block-button__link {
  background-color: var(--ne-color-limestone) !important;
  color: var(--ne-color-accent) !important;
  border: 0 !important;
  transition: background-color var(--ne-transition-base);
}
.site-main--dark .wp-block-button .wp-block-button__link:hover,
.site-main--dark .wp-block-button .wp-block-button__link:focus {
  background-color: #ffffff !important;
}

/* NOTE: the dark-canvas override that used to live here is gone. The canvas
   width rule in layout.css now exempts .ne-contact directly, which fixes the
   light Canvas pages (Buy, Sell) too rather than just the dark ones. */

/* A surface card dropped onto the dark field gets a subtle raised panel
   instead of the light-mode white. */
.site-main--dark .has-surface-background-color,
.site-main--dark .has-surface-strong-background-color {
  background-color: rgba(240, 235, 233, 0.06) !important;
  color: var(--ne-color-accent-contrast);
}

/* ─── Hero cover ("Page Header — Dark") ────────────────────────────────────
   A full-bleed Cover block used as a page hero. Everything here is scoped to
   the block, NOT to a page template, so the same hero renders identically on
   Canvas, Canvas — Dark and any future template. Add `ne-hero-cover` to the
   Cover block under Advanced → Additional CSS class(es). */

/* 1. Scrim. WordPress renders the Cover dim as a flat `opacity` on the
   background span (`has-background-dim-60`), which is uniform across the
   whole image — on a high-key photo that either leaves the text illegible or
   mutes the entire picture to fix one band of it. A gradient puts the density
   where the text actually sits and leaves the rest of the frame alone. The
   inline dimRatio no longer matters once this class is applied. */
.ne-hero-cover > .wp-block-cover__background {
  opacity: 1;
  background-image: linear-gradient(
    180deg,
    rgba(47, 53, 34, 0.2) 0%,
    rgba(47, 53, 34, 0.62) 55%,
    rgba(47, 53, 34, 0.75) 100%
  );
}

/* 2. Text colour. theme.json sets `styles.color.text: #000000`, so WordPress'
   global stylesheet emits `.has-text-color { color: … !important }` — and
   every block carrying a custom colour also carries `has-text-color`. That
   !important beats the pattern's plain inline `color:#f0ebe9`, so the hero
   copy renders BLACK on the photo. `.site-main--dark` already reasserts linen
   further up this file, which is why the dark template looked fine and the
   light Canvas pages did not. Reassert it here on the block instead, so the
   hero no longer depends on which template the page happens to use. */
.ne-hero-cover :is(h1, h2, h3, h4, h5, h6),
.ne-hero-cover p,
.ne-hero-cover li,
.ne-hero-cover .has-mono-font-family,
.ne-hero-cover strong {
  color: var(--ne-color-accent-contrast) !important;
}

.ne-hero-cover a:not(.wp-element-button):not(.wp-block-button__link) {
  color: var(--ne-color-accent-contrast) !important;
  text-decoration-color: rgba(240, 235, 233, 0.4);
}

/* Hairline rules (e.g. the eyebrow underline) flip to linen-alpha so they
   stay visible against the photo. */
.ne-hero-cover .wp-block-group[style*="border-bottom-color"],
.ne-hero-cover .wp-block-group[style*="border-top-color"] {
  border-bottom-color: rgba(240, 235, 233, 0.18) !important;
  border-top-color: rgba(240, 235, 233, 0.18) !important;
}

/* Buttons match the dark-field treatment: filled Limestone, moss label. */
.ne-hero-cover .wp-block-button .wp-block-button__link {
  background-color: var(--ne-color-limestone) !important;
  color: var(--ne-color-accent) !important;
  border: 0 !important;
}

/* 3. Legibility floor. Invisible over a dark scrim, but keeps the copy
   readable if the photo is later swapped for something brighter. */
.ne-hero-cover .wp-block-cover__inner-container {
  text-shadow: 0 1px 24px rgba(0, 0, 0, 0.35);
}

/* 4. Measure. The light "Page Header" sits inside a constrained group, so its
   eyebrow/title/subhead cap at the theme contentSize (48rem / 768px). A Cover's
   inner container is `is-layout-flow` with no max-width of its own, so the dark
   header ran the full viewport. The pattern now ships
   `layout:{type:constrained,contentSize:48rem}`, but pages built from the older
   markup (and any hero inserted without the `ne-hero-cover` class) still need
   this — hence the positional selector for a full-bleed Cover opening the page. */
.ne-hero-cover > .wp-block-cover__inner-container,
.site-main > .wp-block-cover.alignfull:first-child > .wp-block-cover__inner-container {
  max-width: 48rem;
  margin-left: auto;
  margin-right: auto;
}

/* Safety net: any cover copy that explicitly asked for Hearth Linen inline
   gets it, even if the `ne-hero-cover` class was never added to the block.
   This is what rescues pages built from the pattern before the class existed;
   prefer the class for anything new. */
.wp-block-cover__inner-container :is(h1, h2, h3, h4, h5, h6, p, li)[style*="#f0ebe9" i] {
  color: var(--ne-color-accent-contrast) !important;
}

/* ============================================================
   PROCESS STEPS — GHOST NUMERALS
   ============================================================
   Property Management, "◆ Getting Started": the 01–04 step numbers become
   oversized Besley numerals in a tone just above the section's Hemlock ground,
   sitting BEHIND the h3 so the headline crops them. Reads as texture, not as
   content — which is the whole reason this works on a dark field. The same move
   on a light section would shout.

   SCOPE — deliberately narrow. The section lays its four steps out as a 2×2:
   two flex `is-nowrap` row groups, each holding two steps. This matches a step
   by its shape — a direct child of an `is-nowrap` group that leads with a mono
   paragraph followed by an h3 — which hits exactly those 4 steps and nothing
   else across every page on the site. The vertical "◆ GeT Started" variant
   beside it uses a constrained (block) layout, so it is untouched — which is
   the point while both are being tested.

   That does mean the hook is the LAYOUT, not a class. If the surviving variant
   is ever switched from a flex row to a stack in the editor, this silently
   stops applying. When the A/B is settled, add an explicit class (e.g.
   `ne-steps-ghost`) to the section and re-scope these rules to it. */

/* One step. `padding-top` opens the space the numeral occupies, and tracks the
   same curve as the numeral's font-size — a fixed value would hold the headline
   still while the digits shrank, so the overlap drained away on smaller screens
   (27px at desktop vs 13px at 600px before this). The inline
   `padding-top: var(--wp--preset--spacing--md)` on the block has to be beaten. */
.ne-section .wp-block-group.is-nowrap > .wp-block-group:has(> p.has-mono-font-family + h3) {
  position: relative;
  padding-top: clamp(1.75rem, 3.5vw, 3rem) !important;
}

/* The numeral.
   Every !important here is earning its place, overriding something that is
   itself !important or inline:
     - font-size / letter-spacing → inline styles from the editor
     - font-family → WP's generated `.has-mono-font-family` preset class
     - color        → the dark-section rule that forces Hearth Linen on copy */
.ne-section
  .wp-block-group.is-nowrap
  > .wp-block-group:has(> p.has-mono-font-family + h3)
  > p.has-mono-font-family {
  position: absolute;
  /* Pushed down so the headline crosses the digits around their middle rather
     than clipping the bottom edge — that overlap is the whole idea. Leaves
     ~8px between the numeral's descender box and the body copy below. */
  top: 0.75rem;
  left: -0.06em;
  z-index: 0;
  margin: 0;
  font-family: var(--ne-font-heading) !important;
  font-size: clamp(3.5rem, 8vw, 6.5rem) !important;
  line-height: 0.8;
  letter-spacing: -0.04em !important;
  text-transform: none;
  /* Between the section ground and the accent — a lift, not a colour. The
     fallback is the same value for browsers without color-mix(). */
  color: #363d27 !important;
  color: color-mix(in srgb, var(--ne-color-accent) 62%, var(--ne-color-accent-strong)) !important;
  pointer-events: none;
  user-select: none;
}

/* Headline and body ride above the numeral. */
.ne-section
  .wp-block-group.is-nowrap
  > .wp-block-group:has(> p.has-mono-font-family + h3)
  > :is(h3, p:not(.has-mono-font-family)) {
  position: relative;
  z-index: 1;
}

/* No breakpoint needed — the clamp on padding-top above tracks the clamp on
   font-size, so the numeral and the headline shrink together and the overlap
   stays proportional all the way down. */


/* ==== assets/css/utilities.css ==== */
.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.screen-reader-text:focus {
  top: var(--ne-space-sm);
  left: var(--ne-space-sm);
  width: auto;
  height: auto;
  margin: 0;
  clip: auto;
  padding: 0.75rem 1rem;
  background: var(--ne-color-text);
  color: var(--ne-color-background);
  z-index: 100000;
}

.ne-flow > * + * {
  margin-top: var(--ne-space-md);
}

.ne-flow-sm > * + * {
  margin-top: var(--ne-space-sm);
}

/* .ne-section / .ne-section--half were duplicated here. They now live in
   layout.css beside the `.ne-section + .ne-section` adjacency rule, so the
   section-spacing system is in one place. */

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

