/* ==========================================================================
   Ivaldi Engineering -- brand stylesheet

   Every value below is extracted from the live Squarespace Site Styles, not
   chosen. See PARITY_SPEC.md for the extraction method and the full token
   table. Do not "improve" these numbers -- the target is an exact replica of
   ivaldiengineering.com (Jon, 2026-08-22), verified by tools/parity_check.py.
   ========================================================================== */

:root {
  /* ------------------------------------------------------------------
     BRAND PALETTE -- Ivaldi Brand Guidelines, May 2026.
     Named colours first, then the role tokens the stylesheet actually uses,
     so a colour is declared once and referenced by meaning everywhere else.

     River Water (#33777c) and Lake Water (#46668f) are the guidelines' other
     two secondaries. Both are deliberately UNUSED pending Jon's decision on
     blue -- nothing below references them.
     ------------------------------------------------------------------ */
  --deep-forest:  #1F3B29;  /* primary   */
  --evergreen:    #2E5C3D;  /* primary   */
  --pine:         #3D7A54;  /* logo suite */
  --brand-sage:   #A6C2AE;  /* logo suite */
  --light-sage:   #C6DBCE;  /* logo suite */
  --mist:         #EFE9E5;  /* secondary */
  --sienna-brown: #82421F;  /* secondary */
  --sienna-clay:  #AC5726;  /* logo suite */

  /* Role tokens. Each maps to the closest brand colour that keeps the role
     the live site gave it -- the OKLab distance from the old value is noted,
     so how faithful each substitution is stays visible:

       old #003417 dark green  -> Deep Forest    dE 4.8
       old #437638 mid green   -> Pine           dE 3.4
       old #DFECCD sage        -> Light Sage     dE 5.9  (Mist was nearer at
                                  4.3 but is the section ground below, and
                                  Light Sage keeps sage's green cast)
       old #C96229 orange      -> Sienna Clay    dE 6.3
       old #492411 rust        -> Sienna Brown   dE 15.1 (Deep Forest was
                                  nearer at 9.2 but is green; rust is the
                                  site's dark WARM anchor, so role wins)
       old #F9FEF5 cream       -> Mist           dE 5.5

     PHONES ARE DIFFERENT, Jon 2026-09-28: below 768px every one of these
     goes back to live's own value -- see the ":root" block under "Phone
     palette" at the end of this file. Desktop keeps the brand palette.
  */
  --dark-green: var(--deep-forest);
  --sage: var(--light-sage);
  --orange: var(--sienna-clay);
  --mid-green: var(--pine);
  --rust: var(--sienna-brown);
  --white: #FFFFFF;
  --cream: var(--mist);
  --cream-scrim: #F6F0EC;  /* Mist +3% -- live's scrim is a hair lighter than its ground */
  --bright-cream: var(--mist);
  --text-dark: var(--deep-forest);
  /* rgb channels of the colours above, so translucent rgba() uses follow the
     palette -- and the phone override -- instead of hard-coding it. */
  --deep-forest-rgb: 31, 59, 41;
  --pine-rgb: 61, 122, 84;
  --light-sage-rgb: 198, 219, 206;
  --mist-rgb: 239, 233, 229;
  --cream-scrim-rgb: 246, 240, 236;
  --sienna-brown-rgb: 130, 66, 31;

  /* Layout */
  --max-width: 1389px;     /* maxPageWidth  */
  --page-padding: 3vw;     /* pagePadding -- fluid, not a fixed gutter */
  --logo-height: 71px;     /* header-logo-height */
  --logo-height-mobile: 52px;
  --header-pad-y: 1.1vw;
  --header-pad-y-mobile: 3.1vw;
  /* Derived, not guessed: logo + the vertical padding above and below it.
     ~103px at 1440px wide, matching live's reserved header space. */
  --header-height: calc(var(--logo-height) + 2 * var(--header-pad-y));
  --divider-height: 6vw;
  --pill: 25px;

  /* Type scale. Squarespace serves the flat rem value at >=768px and a fluid
     value below it: calc((V - 1) * calc(.012 * min(100vh, 900px)) + 1rem).
     Both branches are reproduced verbatim in the media queries further down. */
  --h1-size: 6rem;
  --h2-size: 3.3rem;
  --h3-size: 1.8rem;
  --h4-size: 1.3rem;

  --heading-font: 'Montserrat', sans-serif;
  --heading-weight: 900;
  --heading-tracking: 0em;
  --heading-leading: 1.1em;

  --body-font: 'Source Code Pro', 'Courier New', monospace;
  --body-weight: 600;
  /* Live declares -.04em but resolves it against a 17.667px context and then
     inherits the computed px down to 16px body copy, so the rendered value is
     -0.7067px rather than the -0.64px a local -.04em would give. Pinned in px
     to match; measured on production 2026-08-22. */
  --body-tracking: -0.7067px;
  --body-leading: 1.2em;

  /* Redline #46, Jon 2026-09-21: "I don't like this font. Let's not use this
     font on the site... Replace it anywhere it is used. I think it looks
     unprofessional." Permanent Marker is retired site-wide. Every use ran
     through this one token -- card dates, the section 2 intro, the Featured
     label, and both post bylines -- so this is the whole change.
     Source Code Pro is the site's body face and one of Ivaldi's two standard
     fonts (Montserrat is the other). It keeps meta text visually distinct
     from the Montserrat headings, which is the job the marker face was
     doing, without the handwritten look. Weight 600 because 400 in a
     monospace reads thin at the small sizes meta text uses.
     Side effect, welcome: the marker intro's 4.21:1 contrast item (worklog
     2026-09-09) goes away with the face. */
  --meta-font: 'Source Code Pro', 'Courier New', monospace;

  /* Live's hand-drawn heading underline and its About/Blog/Contact accent copy
     are both #5CA94C, a bright leaf green. It is NOT in the 2026 brand book,
     which has only Deep Forest, Pine, Light Sage and the two siennas and says
     alternative colours should not be introduced. Jon asked for live's green
     specifically (redlines #51, #61, #62, #65 -- "add the green underline from
     the live site", "a different shade of green for the states so they pop"),
     and neither Pine nor Light Sage does that job on a Deep Forest ground.
     Carried as one named token so it is easy to find and easy to retire. */
  --accent-green: #5CA94C;

  /* Seamless (mirrored) tiles; see js/main.js "Topo flow". */
  --topo-dark: url('../images/topo-dark-tile.jpg');
  --topo-mist: url('../images/topo-mist-tile.jpg');
}

/* Section themes are defined further down, on the .theme-* classes the page
   builders already emit. Live composes each section as: background image, then
   the theme's own ground colour painted OVER it at a per-section opacity
   (0.0-0.72, see PARITY_SPEC.md s.5). Sections set their value with an inline
   --overlay. An earlier note in this file claimed production used no overlay at
   all -- that was wrong, and running without one is what made our sections read
   as raw contour maps against production's soft wash. */

/* -------------------------------------------------------------------------
   Reset + base
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--body-font);
  font-weight: var(--body-weight);
  letter-spacing: var(--body-tracking);
  color: var(--text-dark);
  /* Live sets html/body to the dark green via custom.css, so any gap between
     sections shows green rather than cream. */
  background: var(--dark-green);
  line-height: var(--body-leading);
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, h4, h5, h6 {
  font-family: var(--heading-font);
  font-weight: var(--heading-weight);
  text-transform: uppercase;
  line-height: var(--heading-leading);
  margin: 0 0 0.5em;
  letter-spacing: var(--heading-tracking);
}
/* custom.css collapses the space under h3/h4 on live. */
h3, h4 { margin-bottom: 0; padding-bottom: 0; }
p { margin: 0 0 1em; }
/* Every live section is background-width--full-bleed with content-width--wide,
   so the content box is the viewport less the 3vw gutters -- the 1389px
   maxPageWidth never binds at desktop widths. Capping at 1389 here made our
   content box ~50px narrower than production's and forced the hero H1 to wrap
   onto a fourth line. */
/* It DOES bind on very wide screens, though, which the note above never
   measured: at 2520px live's blocks start at x=558, a 1389px content box
   centred (Jon's desktop redlines #83-#87). So the cap is on the content box,
   gutters added back. It only engages above ~1478px (where 94vw > 1389), so
   every width up to that renders exactly as before. */
.container {
  width: 100%;
  max-width: calc(var(--max-width) + 2 * var(--page-padding));
  margin: 0 auto;
  padding: 0 var(--page-padding);
}
/* Kept for any page that genuinely wants the narrower measure. */
.container--capped { max-width: var(--max-width); }
main { display: block; overflow-x: hidden; }

/* topography texture -- the site's signature. Verified against production
   2026-07-20 (Playwright DOM inspection of .section-background): the topo
   JPGs are painted full-bleed, FULL OPACITY, no color overlay/tint -- the
   apparent contrast comes from the image's own baked-in palette (dark green
   on near-black for the dark file, warm gray linework on cream for the mist
   file). Baked directly onto the theme classes below so every section using
   that theme gets it automatically, matching production's per-section
   default (dark topo on dark themes, mist topo on light themes). The
   star/compass/lattice marks visible on production are part of the JPG
   itself, not separate DOM decoration -- nothing extra needed for those. */
/* Background composition, matching how live builds every section:
     ground colour  ->  background image, cropped by a per-section focal point
                    ->  a scrim of --overlay-color at --overlay opacity
                    ->  content
   The scrim must sit ABOVE the image and BELOW the content, so ::before takes
   z-index 0 inside an isolated stacking context and .container takes z-index 1.
   (A negative z-index here would drop the scrim behind the image and paint
   nothing.) Focal points and opacities are per-section and were measured off
   production -- see PARITY_SPEC.md s.5. */
.theme-hero, .theme-dark-green, .theme-sage, .theme-cream,
.theme-rust, .theme-bright, .theme-dark-bold {
  position: relative;
  isolation: isolate;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: var(--bg-pos, center);
}
.theme-hero::before, .theme-dark-green::before, .theme-sage::before,
.theme-cream::before, .theme-rust::before, .theme-bright::before,
.theme-dark-bold::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-color: var(--overlay-color, transparent);
  opacity: var(--overlay, 0);
}
.theme-hero > .container, .theme-dark-green > .container, .theme-sage > .container,
.theme-cream > .container, .theme-rust > .container, .theme-bright > .container,
.theme-dark-bold > .container {
  position: relative;
  z-index: 1;
}

.theme-hero, .theme-dark-green {
  background-color: var(--dark-green);
  background-image: var(--topo-dark);
  color: var(--white);
  --overlay-color: var(--dark-green);
}
/* Live's "white" theme: near-white green-tinted ground under a slightly
   different near-white scrim, dark-green headings, burnt-orange body copy.
   The orange is the theme's paragraph colour, not a local override. */
.theme-sage, .theme-cream {
  background-image: var(--topo-mist);
  background-color: var(--cream);
  color: var(--orange);
  --overlay-color: var(--cream-scrim);
}
.theme-sage h1, .theme-sage h2, .theme-cream h1, .theme-cream h2 { color: var(--mid-green); }
.theme-sage h3, .theme-sage h4, .theme-cream h3, .theme-cream h4 { color: var(--dark-green); }
/* Live's "bright" theme -- the What Sets Us Apart band. Ground is the dark rust
   #492411 with NO background image (the live section carries none), orange
   headings, white body copy. The previous rust-tinted-topo treatment was an
   invention; production has no topo here at all. */
.theme-rust, .theme-bright {
  background-color: var(--rust);
  background-image: none;
  color: var(--white);
  --overlay-color: var(--rust);
}
/* Mist, not the accent. Live ran burnt orange on dark rust, two warms far
   enough apart in lightness to clear 3.4:1. The brand's only two warms --
   Sienna Clay and Sienna Brown -- sit much closer, and mapping this pair
   straight across collapsed the headings to 1.51:1 against their own ground.
   Mist restores the light-on-warm reading at 6.35:1; the ground still carries
   the section's warm statement. */
.theme-rust h1, .theme-rust h2, .theme-rust h3, .theme-rust h4,
.theme-bright h1, .theme-bright h2, .theme-bright h3, .theme-bright h4 {
  color: var(--mist);
}
.theme-rust .lede, .theme-bright .lede { color: var(--bright-cream); }

/* Live's "dark-bold" -- mid-green ground, sage large headings, white sub-heads.
   Its scrim is dark green at 63% alpha (not the mid-green ground), which the
   section opacity then scales: 0.63 x 0.39 = ~0.25 effective. */
.theme-dark-bold {
  background-color: var(--mid-green);
  color: var(--dark-green);
  --overlay-color: rgba(var(--deep-forest-rgb), 0.63);
}
.theme-dark-bold h1, .theme-dark-bold h2 { color: var(--sage); }
.theme-dark-bold h3, .theme-dark-bold h4 { color: var(--white); }
/* Legacy per-section opt-in classes -- the theme classes carry the topo by
   default now, so these are synonyms for any stray usage. They must NOT pin
   background-position: it would win on specificity over the theme rule and
   throw away the section's focal point. */
.has-topo, .has-topo-mist { background-size: cover; }

/* -------------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------------- */
/* Pill buttons -- verified against production (computed styles, 2026-07-20):
   border-radius 25px, solid fill (not outline), Montserrat 800 uppercase,
   1px border in the text color, subtle scale/shadow on hover. */
/* Primary button, exact: Montserrat 800 / 1.25rem / uppercase / 0 tracking /
   1.2em leading / 1.6rem x .8rem padding / 25px radius / .5px stroke. */
.btn {
  display: inline-block;
  font-family: var(--heading-font);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0em;
  /* 19.2px is what live's primary buttons render (210x50 at padding
     12.8/25.6px). The earlier 25.4px was reverse-engineered from the hero
     pill's 332px width, but the hero pill is a sqsrte-scaled-text element with
     its own JS-computed size and zero vertical padding -- it is not the
     general button, and is matched separately below. */
  font-size: 19.2px;
  line-height: 1.2em;
  padding: 0.8rem 1.6rem;
  border: 0.5px solid currentColor;
  border-radius: var(--pill);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
/* Secondary/tertiary variants carry their own padding and stroke weight. */
.btn-secondary { font-size: 1.25rem; padding: 0.6rem 1.1rem; border-width: 2px; }
.btn-tertiary  { font-size: 0.75rem; padding: 0.5rem 0.8rem; border-width: 1px; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(0,0,0,0.18); }
.btn-outline-light { background: var(--sage); color: var(--dark-green); border-color: var(--dark-green); }
.btn-outline-light:hover { background: var(--white); }
.btn-solid-orange { background: var(--orange); color: var(--white); border-color: var(--orange); }
.btn-solid-orange:hover { background: #8F4820; border-color: #8F4820; }
.btn-outline-dark { background: var(--white); color: var(--dark-green); border-color: var(--dark-green); }
.btn-outline-dark:hover { background: var(--sage); }

/* -------------------------------------------------------------------------
   Header / Nav
   ------------------------------------------------------------------------- */
/* tweak-transparent-header is true on live, and custom.css forces the header,
   announcement bar and logo wrapper transparent at >=768px -- so the nav floats
   over the hero image rather than sitting in a solid bar above it. Below 768px
   the mobile menu is solid #003417 (also custom.css). */
.site-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: transparent;
  border-bottom: none;
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-height);
}
.site-logo img { height: var(--logo-height); width: auto; }
/* The footer lockup had no size rule of its own -- harmless while it was a
   1441px raster with intrinsic dimensions, but an SVG would take the
   container's width instead. */
/* Redline #15. The brand SVG carries 2.73% of clear space inside its own
   viewBox, so at 232px the mark's ink starts ~6.3px right of the phone number
   below it and the column looks ragged. Pull the box left by exactly that
   inset so the INK lines up, which is what the eye actually reads. */
.footer-col img { width: 232px; max-width: 100%; height: auto; display: block; margin-left: -6.3px; }
.site-logo { display: flex; align-items: center; }

@media (max-width: 900px) {
  .site-header { background: var(--dark-green); }
  .site-logo img { height: var(--logo-height-mobile); }
  .site-header .container { height: calc(var(--logo-height-mobile) + 2 * var(--header-pad-y-mobile)); }
}

.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  z-index: 1100;
}
.nav-toggle span {
  display: block;
  width: 26px;
  height: 3px;
  background: var(--sage);
  margin: 5px 0;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle.is-open span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.nav-toggle.is-open span:nth-child(2) { opacity: 0; }
.nav-toggle.is-open span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

.main-nav > ul {
  display: flex;
  align-items: center;
  gap: 30px;
}
.main-nav a {
  font-size: 0.95rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--sage);
  padding: 8px 0;
}
.main-nav a:hover { color: var(--orange); }
.main-nav .nav-cta {
  font-family: 'Montserrat', sans-serif;
  font-weight: 800;
  color: var(--dark-green);
  background: var(--sage);
  border: 1px solid var(--sage);
  border-radius: var(--pill);
  padding: 11px 24px;
  transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.main-nav .nav-cta:hover { background: var(--white); transform: translateY(-2px); }

.nav-dropdown { position: relative; }
.nav-dropdown-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--sage);
  padding: 8px 0;
}
.nav-dropdown-toggle:hover { color: var(--orange); }
.nav-dropdown-toggle .caret { font-size: 0.65rem; transition: transform 0.2s ease; }
.nav-dropdown.is-open .caret { transform: rotate(180deg); }
/* Dropdown panel per the agency custom.css: #1d412b ground, 15px radius,
   shifted left 5em, no panel padding, .5em per item, white text; the hover
   state swaps to a #d0e4b5 pill with #002303 text. */
.nav-dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: -5em;
  right: auto;
  text-align: left;
  min-width: 200px;
  background: var(--deep-forest);
  border: none;
  border-radius: 15px;
  padding: 0;
  margin-top: 8px;
  overflow: hidden;
}
.nav-dropdown.is-open .nav-dropdown-menu { display: block; }
.nav-dropdown-menu li { padding: 0.5em; }
.nav-dropdown-menu li a { display: block; color: var(--white); }
.nav-dropdown-menu li:hover { border-radius: 15px; background: var(--light-sage); }
.nav-dropdown-menu li:hover a { color: var(--deep-forest); }

@media (max-width: 960px) {
  .nav-toggle { display: block; }
  .main-nav {
    position: fixed;
    inset: 0;
    top: var(--header-height);
    background: var(--dark-green);
    display: none;
    overflow-y: auto;
  }
  .main-nav.is-open { display: block; }
  .main-nav > ul {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 24px;
  }
  .main-nav ul > li { width: 100%; border-bottom: 1px solid rgba(var(--light-sage-rgb), 0.1); }
  .main-nav a { display: block; padding: 16px 0; }
  .main-nav .nav-cta { display: inline-block; margin: 16px 0; }
  .nav-dropdown-menu { position: static; background: rgba(0,0,0,0.2); border: none; margin: 0; }
}

/* -------------------------------------------------------------------------
   Sections -- themed per the live site's section-theme system
   ------------------------------------------------------------------------- */
/* --section-pad mirrors each class's own top padding so a section that sits
   under a divider can add the strip height back without hard-coding which
   padding class it happens to use. */
.section { padding: 104px 0; --section-pad: 104px; }
.section-sm { padding: 64px 0; --section-pad: 64px; }
.section-tight { padding: 56px 0; --section-pad: 56px; }

.theme-hero {
  color: var(--white);
  padding: calc(var(--header-height) + 40px) 0 88px;
  text-align: center;
}
/* Production's hero is 636px tall at 1440 wide (measured off the rendered
   screenshot, not the DOM -- a live DOM reading gives 820px because the page's
   entry animations leave blocks offscreen until they play). The heading block
   is top-aligned, first line's ink at y=171, and the bottom 6vw is the
   chevron divider. */
.theme-hero.is-tall {
  /* 636px total. Measured at a 1440x900 viewport, where production's chevron
     meets the left margin at y=556. Do NOT re-derive this from a tall
     full-page capture: live's sections are sized in vh, so a 4000px-tall
     window stretches this section to 587 and an 8000px one further still.
     Extra room goes on the bottom so the heading block keeps its y=171 start. */
  padding: 118.562px 0 86.4px;
  min-height: 637px;
  /* Redlines #1 and #3. topo-dark and topo-mist are the SAME terrain artwork in
     two colourways, so if both sections draw them at the same scale and offset,
     the contour lines run straight through the angled divider and only the
     colour changes across it. The hero shows the top of the image; section 2
     picks up exactly where the hero stops. */
  background-size: 100% auto;
  background-position: 50% 0;
  background-repeat: no-repeat;
  display: flex;
  align-items: flex-start;
}
.theme-hero.is-tall > .container { width: 100%; }
.theme-hero h1, .theme-hero h3 { color: var(--white); }
/* Measured off live: 29.3px in a lighter green, not a small tracked label.
   Live's green there is rgb(80,147,66), which the 2026 palette does not carry;
   Pine on Deep Forest is 2.38:1, Brand Sage is 6.36:1. Same substitution as
   the services and about heroes. */
.theme-hero .eyebrow {
  color: var(--brand-sage);
  font-weight: 900;
  letter-spacing: 0;
  font-size: 29.3px;
  line-height: 1.16;
  text-align: center;
  margin: 0 0 14px;
}

/* NOTE: color only here -- background-color/image for these theme classes
   is set once, above, in the topo-texture block. Using the `background`
   shorthand here would silently wipe the background-image declared there. */
.theme-sage { color: var(--text-dark); }
.theme-sage h1, .theme-sage h2, .theme-sage h3, .theme-sage h4 { color: var(--dark-green); }

.theme-rust { color: var(--sage); }
.theme-rust h1, .theme-rust h2, .theme-rust h3, .theme-rust h4 { color: var(--white); }
.theme-rust .accent { color: var(--orange); }

.theme-cream { color: var(--text-dark); }
.theme-cream h1, .theme-cream h2, .theme-cream h3, .theme-cream h4 { color: var(--dark-green); }

.theme-dark-green { color: var(--sage); }
.theme-dark-green h1, .theme-dark-green h2, .theme-dark-green h3, .theme-dark-green h4 { color: var(--white); }

/* -------------------------------------------------------------------------
   Section dividers -- organic curved/scalloped edges between sections.
   Verified against production 2026-07-20: Squarespace's built-in "shape
   divider" feature, an SVG clip-path applied via a data-section-id-scoped
   style block. Approximated here as a positioned SVG strip at the bottom of
   the section, painted in the color of the section that follows it, so it
   reads as the next section's color curving up into this one -- same visual
   effect without needing per-section unique clip-path generation.
   ------------------------------------------------------------------------- */
.section-divider {
  position: relative;
  overflow: visible;
}
/* Divider geometry, taken from the live rendered DOM (PARITY_SPEC.md s.5).
   Squarespace clips the section with a path in objectBoundingBox units spanning
   x = -1..2 in steps of 0.5, which works out to exactly ONE full peak-trough
   cycle across the visible section width -- a single broad chevron, high at both
   edges and dipping to its low point at centre. Our previous tile drew eight
   small teeth per 220px, which is why the hero edge read as a sawtooth against
   production's wide V. Strip height is --divider-height (6vw) for both shapes;
   the wave's amplitude is 0.021 against the zigzag's 0.103, so it occupies
   about a fifth of the strip. */
/* Redline #22: "These triangles need to have the white topo image inside them
   from the section below. I want the topo images to flow into one another, but
   with the color transition along this angled look that we have built."

   The strip used to be a ::after on the section ABOVE, filled with the next
   section's flat colour, so the triangles were plain wedges with no texture.
   Painting the topo into that strip cannot line up: it would be sized to a 6vw
   strip while the section below sizes the same image to its own full height.

   So the shape is cut out of the NEXT section instead. It is pulled up over the
   section above by the strip height and given that height back as padding, then
   clipped to the chevron. The triangles are now literally the next section's own
   background, which makes the topo continuous across the boundary by
   construction rather than by matching two separate paintings of it. The V at
   centre reveals the section above, each side keeping its own ground and its own
   topo, with the colour changing along the angle. */
.section-divider::after { content: none; }

.section-divider + section {
  margin-top: calc(-1 * var(--divider-height));
  padding-top: calc(var(--section-pad, 96px) + var(--divider-height));
  position: relative;
  z-index: 1;
}
/* One chevron cycle: level at both edges, full depth at centre. */
.section-divider--zigzag + section {
  clip-path: polygon(
    0 0, 50% var(--divider-height), 100% 0, 100% 100%, 0 100%);
}
/* The wave is the same cycle at about a fifth of the amplitude, so the straight
   segments of a polygon read as the curve at this scale. */
.section-divider--wave + section {
  clip-path: polygon(
    0    calc(var(--divider-height) * 0.80),
    12.5% calc(var(--divider-height) * 0.84),
    25%   calc(var(--divider-height) * 0.90),
    37.5% calc(var(--divider-height) * 0.96),
    50%   var(--divider-height),
    62.5% calc(var(--divider-height) * 0.96),
    75%   calc(var(--divider-height) * 0.90),
    87.5% calc(var(--divider-height) * 0.84),
    100%  calc(var(--divider-height) * 0.80),
    100% 100%, 0 100%);
}

/* orange hand-drawn underline -- production uses Squarespace's native "Text
   Shape" feature (a per-heading generated SVG stroke, data-shape=
   "underlineCurve", stroke hsla(lightAccent), round linecap/linejoin).
   That path is procedurally unique per heading and can't be pulled as a
   static asset, so this replicates the same visual language: a single
   gently-arced stroke with a small upturned flick at the end, scaled to the
   heading's own width via a wide non-square viewBox + preserveAspectRatio
   none. Verified path shape sampled from production 2026-07-20. */
.underline-accent {
  position: relative;
  display: inline-block;
  padding-bottom: 22px;
}
.underline-accent::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 78%;
  min-width: 90px;
  height: 20px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 509 20' preserveAspectRatio='none'%3E%3Cpath d='M2,15.5 C64,13 127,10.4 254,9.1 C381,7.8 445,9.9 505,10.6 C495,10.8 487,12.9 483,13.8' fill='none' stroke='%23AC5726' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.underline-accent.center::after { left: 50%; transform: translateX(-50%); }

/* Block-level variant of the same stroke, for display headings that are grid
   items or .fit containers. `.underline-accent` is inline-block, which would
   shrink a .fit heading to its text and break the container-query sizing its
   lines depend on -- so this keeps the element's own display and just hangs
   the stroke underneath, centred.

   Redlines #49, #51, #61, #62, #65 (Jon, 2026-09-21). Live puts this under the
   H1 on About Us, Blog, Careers and Contact in green; #49 asks for the orange
   one under the homepage services heading, matching "Forging Relationships".
   Note PARITY_SPEC s.15 removed `underline-accent` as an invention after
   checking only two homepage headings, and flagged that "the accent may still
   be genuine elsewhere". It is: live carries it on all four of these. */
.rule-under { position: relative; padding-bottom: 30px; }
.rule-under::after {
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0;
  width: 72%;
  min-width: 120px;
  height: 20px;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 509 20' preserveAspectRatio='none'%3E%3Cpath d='M2,15.5 C64,13 127,10.4 254,9.1 C381,7.8 445,9.9 505,10.6 C495,10.8 487,12.9 483,13.8' fill='none' stroke='%235CA94C' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* #49 wants the orange stroke, not the green one. */
.rule-under--orange::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 509 20' preserveAspectRatio='none'%3E%3Cpath d='M2,15.5 C64,13 127,10.4 254,9.1 C381,7.8 445,9.9 505,10.6 C495,10.8 487,12.9 483,13.8' fill='none' stroke='%23AC5726' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
@media (max-width: 1023px) {
  .rule-under { padding-bottom: 20px; }
  .rule-under::after { height: 13px; width: 78%; }
}

/* -------------------------------------------------------------------------
   Typographic scale
   ------------------------------------------------------------------------- */
/* The live scale, not an approximation of it: H1 6rem, H2 3.3rem, H3 1.8rem,
   H4 1.3rem (PARITY_SPEC.md s.2). Squarespace serves those flat values at
   >=768px and derives a fluid value below that breakpoint from
       calc((V - 1) * calc(.012 * min(100vh, 900px)) + 1rem)
   where V is the unitless size. Both branches are reproduced verbatim so the
   mobile ramp matches too -- an earlier hand-tuned clamp() scale had H1 20%
   small and H3 nearly 80% large. */
/* MOBILE (2026-09-18): Squarespace's ramp is driven by viewport HEIGHT, so a
   390x844 phone asks for 66.64px no matter how narrow it is, and
   "Delivering exceptional" needs 507px in a 367px box. Capped by width as well.
   12vw is the largest cap at which the longest hero word still fits 390px;
   min() means the ramp is untouched wherever it already fits. */
.h-xxl { font-size: min(calc((6 - 1) * calc(.012 * min(100vh, 900px)) + 1rem), 12vw); }
.h-xl  { font-size: calc((3.3 - 1) * calc(.012 * min(100vh, 900px)) + 1rem); }
.h-lg  { font-size: calc((1.8 - 1) * calc(.012 * min(100vh, 900px)) + 1rem); }
.h-md  { font-size: calc((1.3 - 1) * calc(.012 * min(100vh, 900px)) + 1rem); }

@media screen and (min-width: 768px) {
  .h-xxl { font-size: var(--h1-size); }
  .h-xl  { font-size: var(--h2-size); }
  .h-lg  { font-size: var(--h3-size); }
  .h-md  { font-size: var(--h4-size); }
}

/* Hero lines are sized individually because production renders them as three
   separate headings and shrinks line 1 to fit the content box. These are px,
   not rem, and are matched empirically to production's RENDERED line widths
   (1348 / 1048 / 519 px at a 1440 viewport) rather than derived from the
   declared 6rem. Squarespace and Google both serve Montserrat as a variable
   font, and its wght=900 instance is ~6% narrower than the static cut, so
   working back from the declared rem size lands ~6% short. Re-derive with
   tools/parity_check.py if the font source ever changes. */
/* Block-level, because live renders these as three separate <h1> elements. As
   inline spans separated by <br> they inherit the h1's own 96px strut and each
   line box becomes 96px instead of the span's size -- a 288px block against
   live's 257px. */
.hero-l1, .hero-l2, .hero-l3 { display: block; line-height: 1; }
.hero-l1 { font-size: 82.1px; }
.hero-l2 { font-size: 87.9px; }
.hero-l3 { font-size: 87.1px; }
/* Production's hero baselines sit 96-100px apart; 1.1em of the 96px h1 gives
   105.6px, which compounds into a 15px drift by the third line. */
.theme-hero .h-xxl { line-height: 1; }
/* Live's section padding-top is 118.562px but its first heading's ink starts at
   y=148 -- the fluid-engine grid adds 29.4px above the block. Carried as a
   margin so the section keeps its measured 118.562px padding. */
.theme-hero.is-tall .h-xxl { margin-top: 29.4px; }

/* The hero pill is its own case on live: a scaled-text element at 20.167px with
   zero vertical padding, sized to 332x64 by the wrapper rather than by leading.
   Reproduced with an explicit height so the box matches without inflating the
   font the way the old 25.4px rule did. */
.theme-hero.is-tall .btn {
  font-size: 20.167px;
  padding: 0 25.6px;
  /* Live's pill is a fixed-width flex box (332.438px). Its text ink is only
     223px, so the box does not hug the text -- the container was sized before
     the scaled-text JS shrank the font. Pin the width or the pill lands 55px
     narrow even with the font and padding matched exactly. */
  width: 332.438px;
  height: 64px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-width: 0.571429px;
}
@media (max-width: 767px) {
  .hero-l1, .hero-l2, .hero-l3 { font-size: inherit; }
}

.lede { font-size: 1.1rem; max-width: 680px; }
/* Live renders all meta text (post dates, categories) in Permanent Marker. */
.meta, .card-meta, .post-meta {
  font-family: var(--meta-font);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}
.lede.center { margin-left: auto; margin-right: auto; }
.text-center { text-align: center; }
.eyebrow-label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-size: 0.8rem;
  color: var(--orange);
  margin-bottom: 14px;
  display: block;
}

/* -------------------------------------------------------------------------
   Grids / cards
   ------------------------------------------------------------------------- */
.grid {
  display: grid;
  gap: 32px;
}
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}
@media (min-width: 601px) and (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

/* --------------------------------------------------------------------------
   Homepage sections (see PARITY_SPEC.md s.5 / s.8)
   -------------------------------------------------------------------------- */

/* Section 2. The orange intro is a Permanent Marker block on live, centred
   under the heading and noticeably wider than a normal lede. */
.marker-intro {
  font-family: var(--meta-font);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--orange);
  font-size: 1.05rem;
  line-height: 1.5;
  max-width: 760px;
  margin: 20px auto 0;
}
/* Value cards sit on filled sage panels. Production's fluid-engine places
   Problem-Solving leftmost even though it is last in the markup, so the visual
   order is set here rather than by reordering the DOM. */
.value-cards { align-items: start; }
.value-card {
  background: rgba(var(--light-sage-rgb), 0.85);
  border-radius: 6px;
  padding: 24px;
  text-align: left;
}
.value-card h4 { margin-bottom: 12px; }
.value-card p { margin-bottom: 0; font-size: 0.95rem; color: var(--dark-green); }
/* grid-row is pinned alongside grid-column on purpose: the cards appear in
   reverse DOM order, and with only a column set, auto-placement walks the
   cursor forward and drops each card onto a new row (a 3x taller section). */
@media (min-width: 901px) {
  .value-card--1 { grid-column: 1; grid-row: 1; }
  .value-card--2 { grid-column: 2; grid-row: 1; }
  .value-card--3 { grid-column: 3; grid-row: 1; }
}

/* Section 3. Full-bleed aerial photo; a stroked frame around the heading, two
   translucent dark-green copy panels, then the featured-post row over the same
   image. */
.csd-section { padding-top: 96px; }
.csd-frame {
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 4px;
  padding: 28px 40px;
  text-align: center;
}
.csd-frame h1 { color: var(--white); margin: 0; }
.csd-panels { margin-top: 40px; }
.csd-panel {
  background: rgba(var(--deep-forest-rgb), 0.72);
  border-radius: 6px;
  padding: 22px 26px;
  margin: 0;
  color: var(--white);
  font-size: 0.95rem;
  text-align: center;
}
.featured-label {
  margin-top: 48px;
  font-family: var(--meta-font);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--white);
  font-size: 0.95rem;
}
.featured-posts { margin-top: 12px; }
.featured-posts .card-link {
  background: rgba(var(--light-sage-rgb), 0.82);
  border-radius: 6px;
  overflow: hidden;
}
.featured-posts .card-body { padding: 16px 18px 20px; }
.featured-posts .card-body h4 { font-size: 0.95rem; color: var(--dark-green); margin: 8px 0 10px; }
/* Evergreen, not Pine: these dates sit on a translucent Light Sage card over
   the photograph, where Pine reads 2.94:1 at 13px. Evergreen clears 4.46:1
   and stays subordinate to the Deep Forest title above it. */
.featured-posts .card-meta { color: var(--evergreen); font-size: 0.8rem; }
.featured-posts .read-more { font-family: var(--body-font); font-size: 0.8rem; }

/* Section 7 is a thin decorative topo band -- production measures 54px tall at
   1440x900, not the ~26vw a "custom height 6" section suggests. Section 9 is
   the logo band at 637px. Both measured on production 2026-08-22. */
/* .theme-hero carries the tall hero padding; the band must override it or the
   54px collapses into ~230px of inherited padding. */
/* -------------------------------------------------------------------------
   Section 7 -- Google reviews
   Live runs Elfsight's paid Google Reviews app here. We render the same reviews
   statically from content/reviews.json, matching the section envelope (513px,
   topo background, 0.15 scrim) and live's card metrics so the page rhythm is
   identical without the subscription or the third-party script.

   The old .band-decorative rule pinned this section to 54px. That number was a
   cold-cache artifact: live collapses to 54px until the widget renders, and a
   measurement taken before then was recorded as if it were the real height.
   ------------------------------------------------------------------------- */
.reviews-band {
  min-height: 513px;
  padding: 9px 0;
  display: flex;
  align-items: flex-start;
  color: var(--deep-forest);
}
.reviews-band > .container { width: 100%; }

/* The header is a light panel on the dark topo: #F2ECE9, x=113, w=1200, h=100,
   starting at y=49. The section's ground is the DARK topo (mean luminance 23),
   so without this panel the dark ink below is unreadable -- which is exactly how
   it looked before the panels were measured. */
.reviews-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  width: 1200px;
  max-width: calc(100% - 140px);
  min-height: 100px;
  margin: 40px 0 0 70px;
  background: var(--mist);
  /* left 52px puts the summary ink at live's x=165; right 1px puts the CTA's
     right edge at live's x=1312. */
  /* Redline #8. The button was pinned 1px from the panel edge. */
  padding: 24px 34px 0 52px;
}
.reviews-summary { font-family: var(--heading-font); font-weight: 700; font-size: 20px; line-height: 1.2; }
.reviews-score { display: flex; align-items: center; gap: 8px; margin-top: 11px; }
.reviews-rating { font-weight: 700; font-size: 15px; }
.reviews-count { font-size: 15px; font-weight: 600; }

/* 230x43, 1.71px stroke, 24px radius -- measured off live's button. */
.reviews-cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 230px;
  height: 43px;
  margin-top: 15px;
  border: 1.71429px solid var(--deep-forest);
  border-radius: 24px;
  color: var(--deep-forest);
  font-weight: 700;
  font-size: 16px;
  text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease;
}
.reviews-cta:hover { background: var(--deep-forest); color: var(--sage); }

/* Cards are 262px wide, centre-aligned light panels (#F8F8F8 with a hairline
   #F2ECE9 border) on a 282px pitch. Eight of them overflow a 1440px viewport, so
   the rail scrolls horizontally. */
.reviews-rail {
  display: flex;
  gap: 20px;
  /* 20px below the header panel's bottom edge (y=149) puts the first card at
     live's y=169. */
  margin: 20px 0 0;
  padding: 0 70px 12px;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* Without this the snapport starts at the padding edge, so scroll-snap-align
     immediately scrolls the rail by 70px and cancels the left inset -- the first
     card lands flush against the container instead of at live's x=113. */
  scroll-padding-left: 70px;
  -webkit-overflow-scrolling: touch;
}
.review-card {
  flex: 0 0 262px;
  width: 262px;
  min-height: 270px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 24px 25px 0;
  background: #FFFFFF;
  border: 0.571429px solid var(--mist);
  box-sizing: border-box;
}
.review-stars { display: inline-flex; gap: 0; }
.review-stars .star { width: 20px; height: 20px; fill: #E7A33E; }
/* 3 lines at 23px, exactly as live clamps it. */
.review-text {
  margin: 6px 0 0;
  font-size: 16px;
  font-weight: 600;
  line-height: 23px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Live expands the review in place; ours links out to the Google listing, so
   there is no JS and the full text is always read at its source. */
.review-more {
  margin-top: 0;
  color: var(--sienna-brown);
  font-size: 16px;
  line-height: 23px;
  font-weight: 600;
  text-decoration: none;
}
.review-more:hover { text-decoration: underline; }
.review-who { display: flex; flex-direction: column; align-items: center; margin-top: 16px; }
.review-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; flex: none; }
.review-name { display: block; margin-top: 8px; font-size: 15px; line-height: 18px; font-weight: 600; }
.review-date { display: block; margin-top: 4px; font-size: 12px; font-weight: 600; color: rgba(var(--deep-forest-rgb), 0.5); }

@media (max-width: 767px) {
  .reviews-band { min-height: 0; padding: 40px 0; }
  /* Desktop pins this panel with `margin-left: 70px` and buys the room back
     with `max-width: calc(100% - 140px)`. The two are a pair: at 390px the
     max-width left a 227px box for 246px of content, and dropping it alone
     pushed the panel 58px off the right edge because the 70px margin stayed.
     Reset both. */
  .reviews-head { flex-direction: column; padding: 0 16px;
                  max-width: 100%; margin-left: 0; margin-right: 0; }
  .reviews-cta { margin-top: 4px; }
  .reviews-rail { padding: 0 16px 12px; }
}
.band-logo { padding: 96px 0; min-height: 637px; display: flex; align-items: center; }
.band-logo .container { width: 100%; }
.band-logo-mark { width: 320px; max-width: 60%; margin: 0 auto; }

/* Section 2's value-card grid is inset well inside the page max-width on live:
   the three cards span 969px of the 1425px content box with ~95px gutters. */
.value-cards {
  max-width: 969px;
  margin-left: auto;
  margin-right: auto;
  gap: 95px;
}

.service-card {
  background: rgba(255,255,255,0.5);
  border: 1px solid rgba(var(--deep-forest-rgb), 0.12);
  border-radius: 6px;
  padding: 28px;
}
.theme-sage .service-card { background: rgba(255,255,255,0.55); }
.service-card h4 { font-size: 1.05rem; margin-bottom: 12px; }
.service-card p { margin-bottom: 0; font-size: 0.95rem; }
/* Dark theme sections need a darker card (not a lighter one) to keep light
   text legible -- a translucent white card on a dark background composites
   to a medium tone that fails WCAG AA contrast against white/sage text. */
.theme-rust .service-card,
.theme-dark-green .service-card,
.theme-hero .service-card {
  background: rgba(0, 0, 0, 0.3);
  border-color: rgba(255, 255, 255, 0.16);
}
.theme-rust .service-card p,
.theme-dark-green .service-card p,
.theme-hero .service-card p {
  color: rgba(255, 255, 255, 0.92);
}

.feature-tile { text-align: center; padding: 20px; }
.feature-tile img { width: 72px; height: 72px; object-fit: contain; margin: 0 auto 16px; }
.feature-tile h4 { font-size: 1rem; margin-bottom: 10px; }
.feature-tile p { font-size: 0.92rem; }

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: center;
}
@media (max-width: 900px) { .two-col { grid-template-columns: 1fr; } }
.two-col img { border-radius: 6px; width: 100%; object-fit: cover; }

.stat-list { display: grid; gap: 8px; margin-top: 24px; }
.stat-list li { padding-left: 28px; position: relative; }
.stat-list li::before {
  content: '\2713';
  position: absolute;
  left: 0;
  color: var(--orange);
  font-weight: 700;
}

/* project / blog cards */
.card-link {
  display: block;
  background: var(--white);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 14px rgba(var(--deep-forest-rgb), 0.08);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-link:hover { transform: translateY(-4px); box-shadow: 0 10px 26px rgba(var(--deep-forest-rgb), 0.15); }
.card-link .card-img { aspect-ratio: 4 / 3; overflow: hidden; }
.card-link .card-img img { width: 100%; height: 100%; object-fit: cover; }
.card-link .card-body { padding: 20px; }
.card-link .card-body h3, .card-link .card-body h4 {
  color: var(--dark-green);
  font-size: 1rem;
  margin-bottom: 8px;
}
.card-link .card-meta { font-size: 0.8rem; color: var(--evergreen); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.card-link .card-excerpt { font-size: 0.9rem; color: var(--text-dark); }
.read-more { color: var(--orange); font-weight: 700; font-size: 0.85rem; text-transform: uppercase; }

/* team directory */
.team-card { text-align: center; }
.team-card .headshot {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 16px;
}
.team-card .headshot img { width: 100%; height: 100%; object-fit: cover; }
.team-card h3 { font-size: 1.05rem; margin-bottom: 4px; color: var(--dark-green); }
.team-card .role { font-size: 0.85rem; color: var(--mid-green); font-weight: 700; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 0.03em; }

/* project detail table */
.project-meta-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 24px;
  margin: 24px 0 40px;
  padding: 24px 0;
  border-top: 2px solid rgba(var(--deep-forest-rgb), 0.15);
  border-bottom: 2px solid rgba(var(--deep-forest-rgb), 0.15);
}
.project-meta-grid dt { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--orange); font-weight: 700; margin-bottom: 6px; }
.project-meta-grid dd { margin: 0; font-weight: 700; }
.scope-item { margin-bottom: 20px; }
.scope-item h4 { font-size: 1rem; margin-bottom: 8px; color: var(--dark-green); }

/* -------------------------------------------------------------------------
   Contact form
   ------------------------------------------------------------------------- */
.contact-form { max-width: 730px; margin: 0; }
.form-row { margin-bottom: 22px; }
/* Live's field labels are Montserrat 900 uppercase at 17.6668px with a 4px
   gap to the field -- much larger than the 0.85rem/700 they were built at. */
.form-row label {
  display: block;
  font-family: var(--heading-font);
  font-weight: 900;
  font-size: 17.6668px;
  text-transform: uppercase;
  letter-spacing: 0;
  margin-bottom: 0;
  padding-bottom: 4px;
  color: var(--dark-green);
}
.theme-hero .form-row label,
.theme-rust .form-row label,
.theme-dark-green .form-row label { color: var(--sage); }
.form-row .required { color: var(--orange); }
.form-row input[type="text"],
.form-row input[type="tel"],
.form-row input[type="email"],
.form-row textarea,
.form-row input[type="file"] {
  width: 100%;
  /* 5px/10px padding at 17.6668px gives live's 32px field height. */
  padding: 5px 10px;
  /* Live's fields carry a transparent border AND a transparent box-shadow ring,
     so they are visually undelineated -- you cannot see where to type. Matching
     the geometry but keeping a visible hairline instead: copying an invisible
     input would be a usability regression, not parity worth having. */
  border: 0.571429px solid rgba(var(--deep-forest-rgb), 0.35);
  border-radius: 5px;
  font-family: 'Source Code Pro', monospace;
  font-size: 17.6668px;
  line-height: 1.2;
  background: var(--white);
  color: var(--dark-green);
  box-sizing: border-box;
}
.form-row textarea { min-height: 100px; resize: vertical; }
.form-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 600px) { .form-row-2 { grid-template-columns: 1fr; } }
.checkbox-group { display: grid; gap: 10px; }
.checkbox-group label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
}
.checkbox-group input[type="checkbox"] { margin-top: 3px; width: 18px; height: 18px; flex-shrink: 0; }
.field-help { font-size: 0.8rem; opacity: 0.75; margin-top: 6px; }
.form-status { margin-top: 20px; padding: 14px 18px; border-radius: 4px; font-size: 0.9rem; display: none; }
.form-status.is-visible { display: block; }
.form-status.is-success { background: rgba(var(--pine-rgb), 0.18); border: 1px solid var(--mid-green); }
.form-status.is-error { background: rgba(172, 87, 38, 0.18); border: 1px solid var(--orange); }

/* -------------------------------------------------------------------------
   Map / iframe embeds
   ------------------------------------------------------------------------- */
/* Square, per the agency custom.css .map-responsive rule (padding-bottom:100%),
   not the 16/9 we had. */
.map-embed { width: 100%; aspect-ratio: 1 / 1; border: 0; border-radius: 6px; }
.iframe-embed { width: 100%; border: 0; min-height: 720px; }

/* -------------------------------------------------------------------------
   Footer
   ------------------------------------------------------------------------- */
/* --- Footer -------------------------------------------------------------
   Sienna brown, four columns. Restored at Jon's request 2026-09-10 over the
   measured dark-green reproduction of live's. See common.footer_html(). ---- */
.site-footer { background: var(--rust); color: var(--sage); padding: 64px 0 32px; }
.footer-grid {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr 1fr;
  gap: 40px;
  margin-bottom: 48px;
}
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-grid img { height: 48px; margin-bottom: 18px; }
.footer-phone { font-family: 'Montserrat', sans-serif; font-weight: 900; font-size: 1.6rem; color: var(--white); margin-bottom: 18px; }
.footer-social { display: flex; gap: 14px; }
.footer-social a {
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--sage);
  border-radius: 50%;
  transition: background 0.2s ease, color 0.2s ease;
}
.footer-social a:hover { background: var(--orange); border-color: var(--orange); color: var(--white); }
.footer-col h5 {
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  /* Redline #13. Sienna Clay on the Sienna Brown footer reads 1.5:1 -- the same
     two-warms collapse that hit the What Sets Us Apart headings. Mist clears
     6.3:1. */
  color: var(--mist);
  margin-bottom: 18px;
}
/* Redline #11. Nine service areas in one column made the footer unnecessarily
   tall; two columns roughly halve it. */
.footer-col ul { columns: 2; column-gap: 24px; }
.footer-col ul li { margin-bottom: 10px; font-size: 0.9rem; break-inside: avoid; }
.footer-col ul li a:hover { color: var(--orange); }
.footer-rule { border: none; border-top: 1px dashed rgba(var(--light-sage-rgb), 0.35); margin: 8px 0 20px; }
.footer-tagline { font-size: 0.85rem; opacity: 0.85; }
.footer-tagline strong { display: block; color: var(--white); font-family: 'Montserrat', sans-serif; font-weight: 900; text-transform: uppercase; margin-bottom: 4px; }
.footer-bottom {
  border-top: 1px solid rgba(var(--light-sage-rgb), 0.2);
  padding-top: 24px;
  font-size: 0.78rem;
  opacity: 0.7;
  text-align: center;
}

.mb-0 { margin-bottom: 0; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; background: var(--orange); color: var(--white);
  padding: 12px 20px; z-index: 2000;
}
.skip-link:focus { left: 12px; top: 12px; }

/* 404 page */
.error-page { text-align: center; padding: 160px 0; }
.error-page .h-xxl { color: var(--white); }

/* blog index / post */
.blog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; }
@media (max-width: 900px) { .blog-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .blog-grid { grid-template-columns: 1fr; } }
.post-body h4 { color: var(--dark-green); margin: 1.6em 0 0.4em; font-size: 21px; }
.post-meta { font-size: 0.85rem; color: var(--mid-green); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 24px; }
/* Live sets the post byline in Permanent Marker at 26px, the same face it uses
   for all meta text. The name itself is a link on live, pointing at a
   Squarespace author archive that has no equivalent here, so it renders as
   plain text rather than a dead end. */
.post-author {
  font-family: var(--meta-font);
  font-size: 26.0008px;
  line-height: 1.2;
  color: var(--orange);
  margin: -12px 0 26px;
}
.post-author span { color: var(--rust); }
.back-to-blog { display: inline-block; margin-top: 48px; font-weight: 700; color: var(--orange); text-transform: uppercase; font-size: 0.85rem; }

/* -------------------------------------------------------------------------
   Section 8 -- contact. Measured off live at 1440x900 (section height 1086).

   Two columns, not a stack. Left column carries the four-line headline, the
   copy, "Contact us today!" and the map iframe; the form sits beside it in its
   own 730px column starting at x=629. The headline lines are separate
   scaled-text elements on live, each with line-height equal to its own size --
   the same pattern as the hero.
   ------------------------------------------------------------------------- */
.contact-section { padding: 113px 0 78px; }
.contact-cols {
  display: grid;
  grid-template-columns: 586px 730px;
  align-items: start;
}
/* Wide screens only: the two fixed columns (1316px) sit inside a 1389px box;
   live runs them to both edges. Below 1479px the box is narrower than 1389
   and the layout is exactly as approved. Redline #87. */
@media (min-width: 1479px) { .contact-cols { justify-content: space-between; } }
.contact-left { min-width: 0; }
.contact-right { min-width: 0; }

/* Headline lines sit at x=78 (35px in from the container's 3vw gutter) and are
   369px wide; the copy below is at x=70 and 386px wide. */
/* Redline #9. Live scales each of these four lines independently (43.6 / 43.7
   / 60.3 / 58.2px), which read as four different sizes stacked together. Jon
   asked for one size across the block; 52px is the largest that keeps the
   longest line ("Civil Engineer") on one line in this column. Uppercase and
   centred to match live's H4 treatment. */
.contact-line {
  margin: 0;
  width: 100%;
  max-width: 520px;
  line-height: 1.04;
  text-transform: uppercase;
  text-align: center;
  font-size: 52px;
}
.contact-l1 { margin-top: 2px; }

.contact-copy {
  margin: 105px auto 0;
  width: 386px;
  max-width: 100%;
  font-size: 16px;
  /* 19.2px, measured -- five lines at 386px wide gives live's 96px block. */
  line-height: 19.2px;
  /* Redline #50, Jon 2026-09-21: "Make this centered between Looking for a
     civil engineer near you? Let's talk and contact us." The block itself was
     already centred in the column (100px either side); what read as off was the
     TEXT -- ragged left between a centred headline above and a centred
     "Contact Us Today!" below. */
  text-align: center;
}
.contact-today {
  margin: 16px auto 0;
  width: 386px;
  max-width: 100%;
  font-size: 34.1px;
  line-height: 1;
  text-transform: uppercase;
  text-align: center;
}
/* Redline #10. Live tucks the map 3px under the heading above it, which reads
   as crowded once the heading is centred; 34px gives it room. */
.contact-left .map-embed {
  display: block;
  width: 439px;
  max-width: 100%;
  height: 439px;
  margin: 34px auto 0;
  border: 0;
}

@media (max-width: 1023px) {
  .contact-section { padding: 64px 0; }
  .contact-cols { grid-template-columns: 1fr; gap: 40px; }
  .contact-line, .contact-copy, .contact-today { margin-left: 0; width: auto; }
  .contact-l1, .contact-l2 { font-size: 34px; }
  .contact-l3, .contact-l4 { font-size: 44px; }
  .contact-left .map-embed { width: 100%; height: 320px; }
}

/* Live's contact submit is solid dark green with a white hairline and white
   text (136x51 at 12.8/25.6 padding), not the orange pill this was built with. */
.btn-solid-dark {
  background: var(--dark-green);
  color: #fff;
  border: 0.571429px solid #fff;
  font-size: 20.167px;
  padding: 12.8px 25.6px;
}
.btn-solid-dark:hover { background: #16291D; border-color: #fff; }

/* -------------------------------------------------------------------------
   Section 5 -- "What sets us apart".

   Live lays the five differentiators out 3 + 2, with each icon BESIDE its text
   (icon left, a 214px text column right), not four across with the icon above.
   Headings are two lines at ~20px with line-height equal to the size, and the
   copy is 16px/19.2px. That composition is live's and is kept.

   The VERTICAL RHYTHM IS NOT LIVE'S -- deliberate departure, redline #30.
   Jon, 2026-09-17: "I need it to be more compact than what we are showing.
   There is a lot of buffer on this module." Live runs 846px here, of which
   430px is empty: 93 pad / 104 under the H2 / 171 between the rows / 101
   before the closing line / 61 pad. Measured, our build matched live to within
   a few pixels, so this is a design change rather than a parity fix -- live is
   equally airy. Tightened to 614px. Live's anchors, if they are ever wanted
   back: H2 y=93, row 1 text y=252, row 2 text y=533, closing y=747, h=846.
   measure_live.py will report this section ~232px short on the homepage and
   the three location pages. That is the decision, not a gap.
   ------------------------------------------------------------------------- */
.apart-section { padding: 56px 0 40px; }
.apart-section .apart-title { margin: 0; font-size: 54.9px; line-height: 1; }
.apart-row {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 68px;
  margin-top: 52px;
}
.apart-row-2 { margin-top: 72px; gap: 190px; }
/* Row 2's headings stay in a 214px column but its paragraphs run wider on live
   -- 326px under "Local Knowledge" and 270px under "End-to-End Support" -- so
   both wrap to two lines rather than five. */
.apart-row-2 .apart-tile:nth-child(1) > div { width: 326px; }
.apart-row-2 .apart-tile:nth-child(2) > div { width: 270px; }
.apart-row-2 .apart-tile h4 { width: 214px; }
.apart-tile {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  text-align: left;
}
.apart-tile > img {
  flex: none;
  /* Width is set per icon in the markup, from live's measured sizes -- the same
     numbers location_page.FEATURES_ROW1 uses, so the two copies of this module
     render identically (redline #30). A max-width/max-height cap was used here
     before and inflated the tall Regulatory Expertise clipboard to 123x160
     against the location pages' 103x133. */
  height: auto;
  /* Icons are bottom-heavy and hang above the heading. 40px matches the
     location pages' .loc-feat-txt padding-top; this was -50px and did not. */
  margin-top: -40px;
}
.apart-tile > div { width: 214px; flex: none; }
.apart-tile h4 {
  margin: 0;
  font-size: 20px;
  line-height: 1.06;
  text-transform: uppercase;
}
.apart-tile p {
  margin: 13px 0 0;
  font-size: 16px;
  line-height: 19.2px;
}
/* Live's closing line runs the full 1001px content width, not the 680px the
   shared .lede cap would give it. */
.apart-section > .container > .lede {
  margin-top: 48px;
  max-width: 1001px;
  width: 1001px;
}

@media (max-width: 1023px) {
  .apart-section { padding: 64px 0; }
  .apart-row, .apart-row-2 { flex-wrap: wrap; gap: 32px; margin-top: 40px; }
  .apart-tile { width: 100%; }
  .apart-tile > img { margin-top: 0; max-width: 96px; }
  /* The 214/326/270px text columns are desktop measurements off live; left
     fixed they run past a 375px screen. */
  .apart-tile > div,
  .apart-row-2 .apart-tile:nth-child(1) > div,
  .apart-row-2 .apart-tile:nth-child(2) > div { width: auto; flex: 1 1 auto; min-width: 0; }
  .apart-row-2 .apart-tile h4 { width: auto; }
  .apart-section > .container > .lede { margin-top: 40px; width: auto; max-width: 100%; }
}

/* -------------------------------------------------------------------------
   Section 4 -- services. Measured off live at 1440x900 (830px).

   The heading is two separate scaled-text lines (65.7px then 63.7px, each with
   line-height equal to its own size) and carries no underline accent -- live's
   H1 box height equals its font size exactly. The cards sit in a 4-wide grid of
   242px columns on a 336px pitch, starting 17px in from the container gutter.
   ------------------------------------------------------------------------- */
.services-section { padding: 62px 0 66px; }
.services-title { margin: 0; text-align: center; }
.services-title span { display: block; line-height: 1; }
.services-l1 { font-size: 65.7px; }
.services-l2 { font-size: 63.7px; }

.services-grid {
  display: grid;
  /* Live's blocks are 275px wide on a 336px pitch (so a 61px gutter), each with
     16.49px of padding -- which is what puts the text itself at x=60 and 242px
     wide. Row 1 headings sit at y=300 and row 2 at y=556: a 256px pitch. */
  grid-template-columns: repeat(4, 275px);
  /* Live fills this grid column-major -- "Erosion & Sediment Control Plans"
     sits UNDER "Due Diligence", not beside it -- so the cards pair down each
     column rather than across each row. */
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
  gap: 61px;
  row-gap: 31px;
  margin: 92px 0 0;
}
/* Live has no card panel here at all -- no fill, no border, no radius. The
   panelled card was an invention; only the padding is real. Scoped to this
   section so the city pages keep the treatment they were built with. */
.services-section .service-card {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 16.4911px;
}
.services-section .service-card h4 {
  margin: 0;
  font-size: 20px;
  line-height: 1.03;
  text-transform: uppercase;
}
.services-section .service-card p {
  margin: 15px 0 0;
  font-size: 16px;
  line-height: 19.2px;
}

@media (max-width: 1023px) {
  .services-section { padding: 48px 0; }
  /* Also reset the column-major flow: left set, the cards keep pairing down
     implicit columns and run off the side of a narrow screen. */
  .services-grid {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: auto;
    grid-auto-flow: row;
    gap: 32px;
    margin: 40px 0 0;
  }
  .services-l1 { font-size: 36px; }
  .services-l2 { font-size: 34px; }
}
@media (max-width: 600px) {
  .services-grid { grid-template-columns: 1fr; }
}

/* -------------------------------------------------------------------------
   Section 6 -- project management. Measured off live at 1440x900 (731px).

   The heading is two scaled lines ("project" 58px / "management" 56.2px) at
   x=883, not one 28.8px line; the photo runs 779x601 from y=48; and the copy
   sits in narrower columns than the full grid cell (416px lead, 390px body).
   ------------------------------------------------------------------------- */
.pm-section { padding: 48px 0 82px; }
.pm-cols {
  display: grid;
  grid-template-columns: 779px 1fr;
  align-items: start;
}
.pm-photo { width: 779px; height: 601px; object-fit: cover; display: block; }
.pm-title { margin: 52px 0 0 61px; width: 443px; max-width: 100%; }
/* Redline #7. Live renders these uppercase (Montserrat 900, the global heading
   transform). Forcing lowercase here is what produced the sentence-case
   mismatch Jon flagged. */
.pm-title span { display: block; line-height: 1; text-transform: uppercase; }
.pm-l1 { font-size: 58px; }
.pm-l2 { font-size: 56.2px; }
.pm-lead {
  margin: 22px 0 0 74px;
  width: 416px;
  max-width: 100%;
  font-size: 21.0004px;
  line-height: 1.2;
  /* Live sets this lead in uppercase Montserrat 900, not sentence case. */
  text-transform: uppercase;
  font-family: var(--heading-font);
  font-weight: 900;
}
.pm-copy {
  margin: 74px 0 0 88px;
  width: 390px;
  max-width: 100%;
  font-size: 17.6668px;
  line-height: 1.2;
}

@media (max-width: 1023px) {
  .pm-section { padding: 48px 0; }
  .pm-cols { grid-template-columns: 1fr; gap: 32px; }
  .pm-photo { width: 100%; height: auto; }
  .pm-title { margin: 0; width: auto; }
  .pm-l1 { font-size: 38px; }
  .pm-l2 { font-size: 36px; }
  .pm-lead, .pm-copy { margin-left: 0; width: auto; }
  .pm-copy { margin-top: 20px; }
}

/* -------------------------------------------------------------------------
   Section 3 -- civil site design. Measured off live at 1440x900 (1138px).

   The display heading renders at 127.6px (not the 96px --h1-size), the two
   copy panels are 456px wide at x=175 and x=794, and the three featured posts
   are 328px columns on a 349px pitch starting at x=200 with 218px-tall images.
   ------------------------------------------------------------------------- */
.csd-section { padding: 68px 0 155px; }
.csd-section .h-xxl {
  /* Redline #5. 127.6px gives live's 1253px single-line width at exactly
     1440px wide -- but it is a fixed size in a fluid container, so any narrower
     viewport wrapped it to two lines. Scaled off the container instead, capped
     at live's size, so it stays on one line at every width. */
  font-size: min(127.6px, 9.35vw);
  line-height: 1;
  margin: 0;
  text-align: center;
  white-space: nowrap;
}
/* Redline #16. Same treatment as the blog rail below it: a soft dark scrim
   that fades out at the edges, so the copy reads against the aerial photo
   without boxing it in. */
.csd-section .csd-panels {
  display: grid;
  grid-template-columns: 456px 456px;
  gap: 163px;
  align-items: start;
  margin: 105px 0 0 108px;
  /* Redline #40, 2026-09-17: "Get rid of the dark green transparency behind
     these modules." A dark green 0->0.55->0 vertical gradient used to sit here,
     behind and around the two sage panels. Gone. The panels keep their own sage
     ground (redline #26) so the copy contrast is untouched -- it was never
     reading against this gradient. Padding is kept so the panels do not move. */
  padding: 30px 24px 34px;
}
.csd-section .csd-panel {
  margin: 0;
  width: 456px;
  max-width: 100%;
  font-size: 16px;
  line-height: 19.2px;
}
/* Live's left panel sits 10px lower than the right one. */
.csd-section .csd-panel:first-child { margin-top: 10px; }

.featured-section .featured-posts {
  display: grid;
  grid-template-columns: repeat(3, 328px);
  gap: 21px;
  margin: 39px 0 0 157px;
  /* Redline #41, 2026-09-17: "Get rid of this transparency hatch behind the
     blog posts." This was redline #6's dark green scrim rail, added when the
     cards sat straight on the aerial photograph. #41 supersedes #6 -- do not
     put it back. The cards carry their own Mist 0.94 ground, so nothing here
     was load-bearing for contrast. Padding kept so the cards do not move. */
  position: relative;
  padding: 26px 22px 30px;
  margin-left: 135px;
}
.featured-section .featured-posts .card-link {
  background: rgba(var(--mist-rgb), 0.94);
  border-radius: 6px;
  overflow: hidden;
  display: block;
}
.featured-section .featured-posts .card-img img {
  width: 328px;
  height: 218px;
  object-fit: cover;
  display: block;
}
/* Redlines #43, #44, #45, 2026-09-17: "Give the text more buffer at the edge of
   these modules." This was live's inset, 5px rather than live's 6px so the
   longest title kept three lines instead of breaking to four. At 18px the
   titles still measure three lines (292px column), so the line count that the
   old value was protecting survives the change. Cards grow 367 -> 399px and the
   section 1366 -> 1398px; that is the cost of the buffer and it is deliberate.

   NOTE: this rule is what actually applies, not the `.featured-posts .card-body`
   rule further down the file -- three classes beats two. Change it here. */
.featured-section .featured-posts .card-body { padding: 14px 18px 18px; }
/* Live sets these titles in the BODY face -- Source Code Pro 600, 20px on 24px,
   no uppercase, -0.7067px tracking. As uppercase Montserrat they ran to five
   lines instead of three. */
.featured-section .featured-posts h4 {
  margin: 0;
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 20px;
  line-height: 24px;
  text-transform: none;
  letter-spacing: -0.706672px;
}
.featured-section .featured-posts .read-more {
  font-family: 'Source Code Pro', monospace;
  font-size: 14px;
  text-transform: none;
  color: var(--deep-forest);
}

@media (max-width: 1023px) {
  .csd-section { padding: 64px 0; }
  /* nowrap is a desktop rule (redline #5 keeps live's single line). On a phone
     it turns a 550px string in a 367px box into clipped text. Let it wrap and
     cap the size: "Design" is the longest word and clears 390px easily. */
  .csd-section .h-xxl { font-size: min(56px, 12vw); width: auto; margin-left: 0;
                        white-space: normal; }
  .csd-section .csd-panels { grid-template-columns: 1fr; gap: 24px; margin: 32px 0 0; }
  .csd-section .csd-panel { width: auto; }
  .csd-section .csd-panel:first-child { margin-top: 0; }
  .featured-section .featured-posts { grid-template-columns: 1fr; gap: 32px; margin: 40px 0 0; }
  .featured-section .featured-posts .card-img img { width: 100%; height: auto; }
}

/* Live has no outlined frame around the section 3 heading and no filled panels
   behind its two copy blocks -- both were inventions. Live's wrapper carries
   39.99px of padding and nothing else; the copy is 456px wide, centred, 16px on
   19.2px, in the dark rust accent. */
.csd-section .csd-frame {
  border: 0;
  border-radius: 0;
  padding: 39.99px 0;
  text-align: center;
}
/* This copy used to sit directly on the photograph with no panel, in white,
   because the image composites to #78765E behind it and Deep Forest read
   2.65:1 there. Redline #26 asks for a sage panel instead, which solves the
   same problem properly: on sage at 0.86 the Deep Forest copy reads 10.2:1,
   and the section stops depending on where the photograph happens to be dark. */
.csd-section .csd-panel {
  background: rgba(var(--light-sage-rgb), 0.86);
  border-radius: 20px;
  padding: 24px 30px;
  text-align: center;
  color: var(--dark-green);
}

/* -------------------------------------------------------------------------
   Section 2 -- core values. Measured off live at 1440x900 (727px).

   The heading is two display lines (67.6px then 96px) left-aligned at x=211,
   not one centred 28.8px line. The three value columns run Exercise / Do the
   right thing / Provide Valuable Service left-to-right -- the middle and right
   were swapped -- at x=228 (249px), x=566 (293px) and x=954 (249px).
   ------------------------------------------------------------------------- */
/* 81px, re-tuned once the webfont had actually swapped in: with fallback
   metrics a value-card paragraph wrapped to an 8th line and the section
   measured 19px taller than it really is. */
/* The hero is 637px tall, so section 2 offsets the shared topo by that much and
   the two halves line up across the divider: same scale, same horizontal
   anchor, different colourway. */
/* Redline #47, Jon 2026-09-21: "There is a little too much whitespace in this
   module. Let's decrease the whitespace to match the live site." Live runs this
   section at 712px.

   Note the padding-top declared here does NOT win: `.section-divider + section`
   is more specific and computes it as `--section-pad + --divider-height`, so the
   section can slide up under the hero's chevron. Reducing the top gap therefore
   means reducing --section-pad, not padding. It was 104 (+86.4 of clipped
   divider) = 190.4px above the heading. */
.values-section {
  --section-pad: 40px;
  padding: 76px 0 48px;
  background-size: 100% auto;
  background-position: 50% -637px;
  background-repeat: no-repeat;
}
/* Mobile redline #67. At phone width `100% auto` makes the 2500x2861 artwork
   ~490px tall, so it ran out 150px above the bottom of the 637px hero and never
   reached the chevron, and section 2's -637px offset landed past its end. Size
   by height instead: 2300px covers the hero plus section 2 down to 360px wide,
   and lands the contours at roughly the desktop scale (0.92 vs 0.58). Both
   sections share the size, so the lines still run through the divider. */
@media (max-width: 767px) {
  .theme-hero.is-tall,
  .values-section { background-size: auto 2300px; }
}
/* Redline #2. Live sets this heading in sqsrte-text-color--accent (#492411 ->
   our --rust) and centres it, and wraps "building success" in a text-highlight
   -- the orange underline. It was left-aligned off a fixed 168px margin, which
   only centred by accident at exactly 1440px wide. */
/* Scoped to the section so it outranks `.theme-cream h3`, which would
   otherwise repaint it Deep Forest. */
.values-section .forging-title { margin: 0; text-align: center; color: var(--rust); }
.values-section .forging-title span { color: inherit; }
.forging-title span { display: block; line-height: 1; }
.forging-title .underline-accent { display: block; padding-bottom: 26px; }
.forging-title .underline-accent::after { left: 50%; transform: translateX(-50%); width: 58%; }
.forging-l1 { font-size: 67.6px; }
.forging-l2 { font-size: 96px; }
/* Live centres this and sets it in the light accent (#C96229 -> --orange). */
.values-section .marker-intro {
  margin: 20px auto 0;
  width: 1010px;
  max-width: 100%;
  font-size: 21.0004px;
  line-height: 1.2;
  text-align: center;
  color: var(--orange);
}
.values-section .value-cards {
  display: flex;
  align-items: stretch;
  /* Redline #17. Live's three columns are 249/293/249 on uneven margins, which
     left each heading a different number of lines and started every
     description at a different height. Equal widths on a uniform gap, wider
     than live, so the headings wrap consistently. */
  gap: 28px;
  /* 103px was the biggest single gap in the section (redline #47). */
  margin: 40px auto 0;
  max-width: 1120px;
}
/* Redline #4 asked for these on a light panel so the copy reads against the
   topo; white at 86% with a hairline did that. Redline #24 supersedes it:
   "The live site has these as rounded corners and the sage color instead of
   this white. Make this match the live site." Live is sage at 0.76 on a 25px
   radius, measured. */
.values-section .value-card {
  flex: none;
  padding: 26px 28px;
  background: var(--panel-sage-strong);
  border: 0;
  border-radius: var(--panel-radius);
  text-align: center;
}
.values-section .value-card h4 { text-align: center; }
.values-section .value-card p { text-align: center; }
.values-section .value-card--1 { order: 1; }
.values-section .value-card--3 { order: 2; }
.values-section .value-card--2 { order: 3; }
.values-section .value-card { flex: 1 1 0; min-width: 0; }
/* The heading gets a fixed slot so every description starts on the same line,
   whatever the heading's length. Centred inside it so short headings do not
   float at the top of the reserved space. */
.values-section .value-card h4 {
  min-height: 3.3em;
  display: flex;
  align-items: center;
  justify-content: center;
}
.values-section .value-card h4 {
  margin: 0;
  font-size: 21.0004px;
  line-height: 1.1;
}
.values-section .value-card p {
  margin: 16px 0 0;
  font-size: 16px;
  line-height: 19.2px;
}

@media (max-width: 1023px) {
  .values-section { padding: 56px 0; }
  .forging-title { margin-left: 0; }
  .forging-l1 { font-size: 34px; }
  .forging-l2 { font-size: 46px; }
  .values-section .marker-intro { margin: 20px 0 0; width: auto; }
  .values-section .value-cards { flex-direction: column; gap: 28px; margin: 32px 0 0; }
  .values-section .value-card,
  .values-section .value-card--1,
  .values-section .value-card--2,
  .values-section .value-card--3 { width: auto; margin-right: 0; }
}

/* -------------------------------------------------------------------------
   Services -- the four-line call-out live places between the Civil Site Design
   grid and its closing copy. Live sets it centred in #6AB759; on the 2026
   palette that role is Pine, which is the nearest sanctioned green.
   ------------------------------------------------------------------------- */
.svc-callout {
  margin: 26px 0;
  text-align: center;
  color: var(--pine);
  line-height: 1.02;
}
.svc-callout span { display: block; font-size: clamp(28px, 4.4vw, 62px); }
.svc-rule { border: 0; border-top: 1px solid rgba(var(--deep-forest-rgb), 0.18); margin: 30px 0; }
@media (max-width: 767px) {
  .svc-callout span { font-size: clamp(22px, 7vw, 34px); }
}

/* -------------------------------------------------------------------------
   File upload -- live shows an "Add a File" button rather than the browser's
   default "No file chosen". The real <input type=file> stays in the DOM and
   keeps its name, so the form contract is unchanged; the label drives it.
   ------------------------------------------------------------------------- */
.form-row .file-drop {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  margin-bottom: 0;
  padding-bottom: 0;
  font-family: var(--heading-font);
  font-weight: 900;
  font-size: 17.6668px;
  text-transform: uppercase;
}
.file-drop-cta {
  display: inline-flex;
  align-items: center;
  padding: 8px 18px;
  border: 0.571429px solid currentColor;
  border-radius: var(--pill);
  background: var(--mist);
  color: var(--deep-forest);
  transition: background 0.2s ease, color 0.2s ease;
}
.form-row .file-drop:hover .file-drop-cta { background: var(--light-sage); }
.file-drop-name {
  font-family: var(--body-font);
  font-weight: 600;
  font-size: 15px;
  text-transform: none;
  color: var(--evergreen);
}
.file-drop-name:empty::before { content: attr(data-empty); opacity: 0.7; }
/* The input still has to exist and keep its name; it just is not the visible
   control. Kept focusable so keyboard users reach it. */
.form-row input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.form-row input[type="file"]:focus-visible + .file-drop .file-drop-cta,
.form-row .file-drop:focus-within .file-drop-cta { outline: 2px solid var(--sienna-clay); outline-offset: 2px; }

/* Each line of a display heading is its own block, matching live, where every
   line is a separately scaled heading rather than one string broken by <br>. */
.theme-hero .hero-line { display: block; line-height: 1; }

/* Headings broken with <br> render as separate lines but read as one combined
   string to anything walking the DOM -- "We Pride Ourselves on theExperience
   and Quality". Live splits these into separately scaled headings. Making the
   <br> a block-level break gives the same rendering while letting each line be
   read on its own, without the multi-H1 that live's approach would introduce
   (CUTOVER.md already flags multi-H1 heroes as an SEO problem). */
h1 .hl, h2 .hl, h3 .hl, h4 .hl { display: block; }

/* -------------------------------------------------------------------------
   About -- the service-area block live carries above the chamber line.
   ------------------------------------------------------------------------- */
.serving-title { margin: 56px 0 0; text-align: center; }
.serving-areas { margin-top: 18px; text-align: center; }
.serving-areas h4 {
  margin: 0 0 6px;
  font-size: 25.8px;
  line-height: 1.25;
  color: var(--evergreen);
}
@media (max-width: 767px) { .serving-areas h4 { font-size: 19px; } }

/* A hero subhead rendered as a heading rather than body copy (live does this on
   /contact/). Sits in the hero's light ink so it reads on the dark ground. */
.theme-hero .hero-subhead {
  color: var(--sage);
  line-height: 1.05;
  text-align: center;
}
.theme-hero .hero-subhead .hl { display: block; }

/* -------------------------------------------------------------------------
   Location pages -- the SEO pages. Live scales these display headings
   considerably larger than our flat 28.8px: "YOUR Trusted" at 53.2px and the
   RECENT / PROJECT / HIGHLIGHTS stack at 103.7 / 89.8 / 67.4px. Sized to read
   at live's weight while still fitting our container at any width.
   ------------------------------------------------------------------------- */
.location-intro-heading .hl:nth-child(1) { font-size: clamp(30px, 3.7vw, 53.2px); }
.location-intro-heading .hl:nth-child(2) { font-size: clamp(22px, 2.3vw, 32.8px); }
.location-intro-heading .hl:nth-child(3) { font-size: clamp(24px, 2.8vw, 39.7px); }
.location-intro-heading .hl { line-height: 1.04; }

.location-highlights-heading .hl { line-height: 0.98; }
.location-highlights-heading .hl:nth-child(1) { font-size: clamp(42px, 7.2vw, 103.7px); }
.location-highlights-heading .hl:nth-child(2) { font-size: clamp(38px, 6.2vw, 89.8px); }
.location-highlights-heading .hl:nth-child(3) { font-size: clamp(30px, 4.7vw, 67.4px); }

/* The search-intent line on a location page: a heading on live, at 21px, sitting
   between the display heading and the body copy. */
.location-intro-lead {
  margin: 18px 0 0;
  font-size: 21.0004px;
  line-height: 1.25;
  text-transform: none;
}

/* ==========================================================================
   LOCATION PAGES -- direct reproduction of live's Squarespace layout
   ==========================================================================
   Jon, 2026-09-09: "the location pages need to be a direct copy of the live
   page." Everything below was measured off production over CDP rather than
   eyeballed; the raw dumps live in PARITY_SPEC.md s.18.

   THE GRID. Live's fluid engine is 24 columns with a 6px gutter inside a
   content box of viewport - 2 * 3vw. At 1440 that is 1354px, columns of
   50.67px, and block edges at x = 43 + n * 56.67. Every measured block width
   on the three location pages falls out of that formula to within a pixel
   (span 20 -> 1127, span 22 -> 1240, span 24 -> 1354), so the grid below is
   not an approximation of live's layout -- it is live's layout.

   THE HEADINGS. Squarespace's sqsrte-scaled-text sizes each LINE of a display
   heading independently, to fill its block. That is why live reads "RECENT /
   PROJECT / HIGHLIGHTS" at 103.7 / 89.8 / 67.4px: three lines, one width.
   Reproduced statically with container query units -- .fit is the query
   container, each line carries --k (measured px / block px * 100) and resolves
   to cqw. Same rendering as live at 1440, and it keeps scaling with the block
   at every other width instead of snapping at a breakpoint. No JS, no layout
   shift, and no dependence on the tab being visible (PARITY_SPEC s.13).
   ------------------------------------------------------------------------- */
.sq-sec { position: relative; isolation: isolate; overflow: hidden; }
/* The grid is not exclusive to .sq-sec: the footer uses it too, and scoping
   this to a parent selector left the footer stacking at full width. */
.sq-grid {
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  column-gap: 6px;
  padding: 0 var(--page-padding);
  /* Same wide-screen cap as .container: live's fluid grid centres a 1389px
     box above ~1478px (x=558 at 2520 on /services/). Inert below that. */
  max-width: calc(var(--max-width) + 2 * var(--page-padding));
  margin-left: auto; margin-right: auto;
  position: relative;
  z-index: 1;
}
.sq-grid > .fe { grid-column: calc(var(--c) + 1) / span var(--n); min-width: 0; }
.sq-sec p:last-child { margin-bottom: 0; }

/* Per-line scaled headings. */
.fit { container-type: inline-size; margin: 0; }
.fit > .hl { display: block; font-size: calc(var(--k) * 1cqw); line-height: 1.03;
             /* Each --k is the size at which live fits that exact string to the
                block, so the line is by definition one line. Without nowrap a
                sub-pixel metrics difference wraps the last word onto its own
                line and the whole section grows -- the hero went 659 -> 734. */
             white-space: nowrap; }

/* SCRIMS. Live builds every section as ground -> full-bleed image ->
   .section-background-overlay -> content, and the overlay's own alpha and its
   opacity MULTIPLY. The hero's rgba(0,52,23,0.63) at opacity 0.64 is an
   effective 0.40, not 0.63; reading either number alone gets it wrong, and
   reading the alpha alone is what made our hero visibly darker than live's.
   Measured effective values, all three cities, in tools/build_site.py SCRIMS.

   The light sections matter just as much: live washes the mist topo under a
   near-white scrim at 0.51 to 0.78, so the contour lines sit well back. We had
   no scrim at all, which is why our topo read as a texture competing with the
   copy instead of a watermark behind it. */
.sq-photo { background-color: var(--ground, var(--mid-green));
             background-image: var(--img);
             background-size: cover; background-position: center; }
.sq-light { background-color: var(--ground, var(--cream));
             background-image: var(--topo, var(--topo-mist));
             background-size: cover; background-position: center; }
.sq-photo::before, .sq-light::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: var(--scrim-color); opacity: var(--scrim); }
.sq-photo { --scrim-color: var(--dark-green); --scrim: 0.40; }
.sq-light { --scrim-color: var(--cream-scrim); --scrim: 0.51; }

/* --- S0 hero (measured 659px; content block c2 span 20 at y=237) ---------- */
/* Section heights and row tracks come in per city as --sh / --rows / --mh:
   live's own template drifts between the three (Knoxville's hero is 713px and
   Huntsville's is 659, its highlights cards are 122px tall against 149). The
   defaults here are Huntsville's, so a city that matches it can stay silent. */
.loc-hero { min-height: var(--sh, 659px); }
.loc-hero > .sq-grid { padding-top: var(--padt, 237px);
                        padding-bottom: var(--padb, 164px); }
.loc-hero-h { color: var(--white); text-align: center; }

/* --- S1 intro (690px) ----------------------------------------------------
   Left column is heading / lead / dark box with 171px of deliberate air above
   the box -- the low-density stack Jon flagged as missing. Right column is a
   single 842x595 photo spanning the whole thing. */
.loc-intro > .sq-grid {
  min-height: var(--sh, 690px);
  /* One track per section is 1fr: the slack live leaves between blocks. It
     absorbs the few pixels our type metrics differ by, so a paragraph that
     takes five lines here and four on live shortens the gap instead of
     growing the section past live's measured height. */
  grid-template-rows: var(--rows, 48px minmax(149px, auto) 11px
                      minmax(114px, auto) 1fr minmax(149px, auto) 47px);
}
.loc-intro-h    { grid-row: 2; color: var(--rust); }
.loc-intro-lead { grid-row: 4; color: var(--orange); font-size: 21px;
                  line-height: 1.22; font-weight: 700; }
.loc-intro-box  { grid-row: 6; background: var(--dark-green); border-radius: 20px;
                  padding: 26px 27px; color: var(--sage); font-size: 16px;
                  line-height: 1.2; }
.loc-intro-media { grid-row: 2 / 7; align-self: start; }
.loc-intro-media img { width: 100%; height: var(--mh, 595px); object-fit: cover; }

/* --- S2 "Services You Can Trust" (1052px) --------------------------------
   Full-bleed city photograph, heading centred near the top, and a single
   translucent white card dropped near the bottom. The whitespace between them
   is the section: on live you are looking at the photograph, not at copy. */
.loc-trust > .sq-grid {
  min-height: var(--sh, 1052px);
  grid-template-rows: var(--rows, 48px minmax(189px, auto) 1fr
                      minmax(149px, auto) 116px);
}
.loc-trust-h   { grid-row: 2; color: var(--white); text-align: center; }
.loc-trust-box { grid-row: 4; background: rgba(255, 255, 255, 0.53);
                 border-radius: 20px; padding: 34px 36px; color: var(--dark-green);
                 font-size: 16px; line-height: 1.2; }

/* --- S3 Our Services (942px) --------------------------------------------
   Eight cards on a mid-green 55% fill, two rows of four, last column wider
   (span 6 against span 5). Card copy is vertically centred, which is why
   live's headings sit at different heights across a row. */
.loc-svc > .sq-grid {
  min-height: 942px;
  grid-template-rows: 168px minmax(268px, auto) 39px minmax(268px, auto)
                      73px minmax(63px, auto) 54px;
}
.loc-svc-h { grid-row: 1; align-self: start; margin-top: 48px; color: var(--rust); }
.sq-rule  { grid-row: 1; align-self: start; margin-top: 110px; height: 1px;
             background: var(--rust); opacity: 0.45; }
.sq-rule-wide { grid-row: 5; align-self: end; height: 1px;
                 background: var(--rust); opacity: 0.35; }
.loc-apart .sq-rule-wide { background: var(--cream); opacity: 0.3; }
.loc-card  { background: rgba(var(--pine-rgb), 0.55); border-radius: 25px;
             padding: 20px 14px; display: flex; flex-direction: column;
             justify-content: center; }
.loc-card .fit > .hl { color: var(--white); }
.loc-card p { margin: 18px 0 0; color: var(--dark-green); font-size: 17.7px;
              line-height: 1.2; }
.loc-svc-note { grid-row: 6; background: rgba(255, 255, 255, 0.07);
                border-radius: 25px; display: flex; align-items: center;
                justify-content: center; color: var(--rust); font-size: 21px;
                text-align: center; text-transform: uppercase;
                font-family: var(--heading-font); font-weight: 900; padding: 0 20px; }

/* --- S4 What Sets Us Apart (614px, was live's 846px) ---------------------
   Dark rust ground. Three features across, then two, icon left of the label.

   Same deliberate departure as the homepage copy of this module -- see the
   Section 5 header comment for the reasoning and live's original anchors.
   Jon, 2026-09-17, chose the tight rhythm and chose to apply it everywhere the
   module appears, so the tracks below mirror the homepage: 56px to the H2,
   52px under it, ~32px between the rows, 48px before the closing note.
   measure_live.py will report this section 232px short on all three location
   pages. That is the decision, not a gap. */
.loc-apart { background: var(--rust); }
.loc-apart > .sq-grid {
  min-height: 636px;
  /* Row 5 is a fixed 48px rather than the 1fr the other location sections use.
     1fr collapsed to 21px once the section came down from 846, which put the
     divider rule hard against both the Local Knowledge copy above it and the
     closing note below. 48px with the rule centred gives 24px either side and
     reproduces the homepage's 48px from the last feature to the closing line. */
  grid-template-rows: 163px minmax(165px, auto) 32px minmax(150px, auto)
                      48px minmax(40px, auto) 32px;
}
/* .sq-rule-wide is shared with S3, which still wants it at the bottom of a
   tall 1fr row -- scope the centring to this section only. */
.loc-apart .sq-rule-wide { align-self: center; }
.loc-apart-h { grid-row: 1; align-self: start; margin-top: 56px;
               color: var(--white); text-align: center; }
.loc-apart .sq-rule { background: var(--cream); opacity: 0.4; }
.loc-feat { display: flex; gap: 11px; align-items: flex-start; }
.loc-feat-icon { position: relative; flex: 0 0 auto; }
.loc-feat-icon img { width: 100%; height: auto; }
/* Redline #30: "Make sure anywhere on the site this module is used, they all
   match one another." They did not. The location pages carried a 0.5 dark
   green tint over these icons and the homepage did not, so the same five icons
   read as clean orange line-art on one page and muddy dark squares on another.
   The tint came from a fluidImageOverlay on live -- read off the DOM on images
   that never actually loaded in the capture browser, so it was never checked
   against a rendered page. Removed; the homepage treatment is the right one. */
.loc-feat-txt { flex: 1 1 auto; min-width: 0; padding-top: 40px; }
.loc-feat-txt .fit > .hl { color: var(--white); }
.loc-feat-txt p { margin: 14px 0 0; color: var(--cream); font-size: 16px;
                  line-height: 1.2; }
/* Live sets this line in burnt orange on the dark rust ground, which is 3.1:1
   there and 1.51:1 in the 2026 palette, where Sienna Brown is a much lighter
   ground than live's #49250F. It disappeared. Light Sage is 5.25:1 and still
   reads as a tint rather than another white line. Same call as the contact
   headline; both come back the moment Sienna Brown is darkened. */
.loc-apart-note { grid-row: 6; color: var(--sage); font-size: 16px;
                  line-height: 1.2; text-align: center; }

/* --- S5 Industries We Serve (1045px) ------------------------------------
   Pale sage list panel on the left, tinted photograph on the right with the
   closing statement set over its lower half. */
.loc-ind { background-color: var(--white); background-image: var(--topo-mist);
           background-size: cover; background-position: center; }
.loc-ind > .sq-grid {
  min-height: var(--sh, 1045px);
  grid-template-rows: var(--rows, 80px minmax(69px, auto) 29px
                      minmax(804px, auto) 1fr);
}
.loc-ind-h { grid-row: 2; color: var(--dark-green); font-size: 44.7px; line-height: 1.03; }
.loc-ind-panel { grid-row: 4; background: rgba(var(--light-sage-rgb), 0.37);
                 border-radius: 25px; padding: 34px; }
.loc-ind-panel .loc-ind-intro { color: var(--mid-green); font-size: 21px;
                                line-height: 1.22; margin: 0 0 40px;
                                font-family: var(--heading-font); font-weight: 900;
                                text-transform: uppercase; }
.loc-ind-panel h4 { color: var(--dark-green); font-size: 21px; line-height: 1.2; }
.loc-ind-panel p  { color: var(--mid-green); font-size: 17.7px; line-height: 1.2;
                    margin: 8px 0 34px; }
.loc-ind-panel p:last-child { margin-bottom: 0; }
.loc-ind-media { grid-row: 2 / 5; align-self: start; position: relative;
                 height: var(--mh, 884px); border-radius: 25px; overflow: hidden;
                 display: flex; align-items: flex-end; }
.loc-ind-media img { position: absolute; inset: 0; width: 100%; height: 100%;
                     object-fit: cover; }
/* Live's tint is 0.5 over a bright stock photograph. Our replacement photos
   are darker to start with, so 0.5 crushed them to near-black and buried the
   statement set over them; --tint lets a page dial it back. */
.loc-ind-media::after { content: ""; position: absolute; inset: 0;
                        background: var(--dark-green); opacity: var(--tint, 0.5); }
.loc-ind-media h3 { position: relative; z-index: 1; margin: 0 51px 84px 57px;
                    color: var(--white); font-size: 29.3px; line-height: 1.16; }

/* --- S6 Recent Project Highlights (751px) -------------------------------- */
.loc-high { background: var(--dark-green); }
.loc-high > .sq-grid {
  min-height: var(--sh, 751px);
  grid-template-rows: var(--rows, 80px minmax(270px, auto) 1fr
                      minmax(270px, auto) 62px);
}
.loc-high-h { grid-row: 2; color: var(--white); }
.loc-high-box { grid-row: 4; background: var(--mid-green); border-radius: 25px;
                padding: 26px 27px; color: var(--white); font-size: 17.7px;
                line-height: 1.2; }
.loc-high-col { grid-row: 2 / 5; align-self: start; padding-top: var(--cardt, 98px);
                display: grid; grid-template-rows: var(--cards, 109px 149px 149px);
                row-gap: 51px; }
.loc-high-card { background: rgba(var(--pine-rgb), 0.25); border-radius: 25px;
                 padding: 20px 23px; display: flex; align-items: center; }
.loc-high-card h4 { color: var(--sage); font-size: 21px; line-height: 1.2; }

/* --- Contact band, boxed variant ----------------------------------------
   Live wraps the contact block's three parts in cards: dark rust behind the
   headline, sage behind the copy, sage behind the form. Redline #9 stands --
   the four headline lines stay one size rather than live's 44/44/61/59 -- so
   this adds the cards without undoing what Jon already signed off. */
.contact-boxed { padding: 66px 0 47px; }
.contact-boxed .contact-cols {
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  column-gap: 6px;
}
.contact-boxed .contact-left  { grid-column: 1 / span 8; }
.contact-boxed .contact-right { grid-column: 11 / span 14; }
.contact-boxed .contact-head { background: var(--rust); border-radius: 25px;
                               padding: 52px 36px;
                               /* Query container for .contact-line below, so the
                                  headline can size itself to the box it is in
                                  rather than to a breakpoint. */
                               container-type: inline-size; }
/* Redline #9 asked for one size across these four lines instead of live's
   44 / 44 / 61 / 59, sized so "Civil Engineer" stays on one line. That was 52px
   against the old 586px column; live's column is 444px, so the same rule now
   gives 40px. nowrap enforces the rule rather than trusting the measurement.
   Colour keeps live's two-tone -- lead lines quieter, payoff line brightest --
   but not live's exact pair. Live sets #D7601E on #49250F, which is 3.6:1;
   the same relationship in the 2026 palette is Sienna Clay on Sienna Brown,
   and Sienna Brown is much the lighter ground, so that lands at 1.51:1 and is
   genuinely unreadable. Light Sage gives 5.25:1 and keeps the hierarchy. Flagged
   for Jon: the alternative is darkening Sienna Brown. */
/* MOBILE (2026-09-18): 40px is a desktop measurement and `nowrap` turns any
   shortfall into clipped text rather than a wrap. At 390px the head's inner box
   is 295px and "Civil Engineer" needs 344px at 40px -- the longest line ran 50px
   past its own box on all six pages carrying this band, and dragged
   .contact-head, .contact-left, .contact-cols and the section into the clipped
   report with it (30 of the 70 groups in the mobile scan, from this one rule).

   Sized off the container instead of a breakpoint. "Civil Engineer" measures
   8.6px of width per 1px of font, so it fits any box at font <= box/8.6, i.e.
   11.6cqw; 11cqw leaves a margin for metric drift. min() keeps the desktop
   value exactly 40px, so redline #9's "one size across all four lines" still
   holds and nothing above 1024 moves. */
.contact-boxed .contact-line { font-size: min(40px, 11cqw); white-space: nowrap;
                               color: var(--sage); }
.contact-boxed .contact-l4 { color: var(--white); }
.contact-boxed .contact-copy-box { background: rgba(var(--light-sage-rgb), 0.55);
                                   border-radius: 25px; padding: 22px 27px;
                                   margin-top: 50px; }
/* The boxed variant sets this copy ragged-left inside its sage panel, which
   is live's layout on /contact/ and is not what #50 was about. */
.contact-boxed .contact-copy { margin: 0; width: auto; color: var(--dark-green);
                               text-align: left; }
.contact-boxed .contact-today { margin: 24px 0 0; width: auto; text-align: left;
                                color: var(--dark-green); }
.contact-boxed .contact-right { background: rgba(var(--light-sage-rgb), 0.57);
                                border-radius: 25px; padding: 28px 23px; }
.contact-boxed .contact-left .map-embed { width: 100%; margin: 34px 0 0; height: 470px; }

/* --- Location pages, below desktop --------------------------------------
   The 24-column grid and the fixed row tracks only describe live's desktop
   composition. Below 1024 every section collapses to a single flow column,
   which is what live's own mobile breakpoint does. */
@media (max-width: 1023px) {
  .sq-sec > .sq-grid { display: block; min-height: 0; padding-top: 56px;
                         padding-bottom: 56px; }
  .sq-grid > .fe { margin-bottom: 28px; }
  .sq-grid > .fe:last-child { margin-bottom: 0; }
  .sq-rule { display: none; }
  /* .fit sizes each line so its string lands exactly at the container width --
     --k is live's measured fit. That is a knife edge: our Montserrat is a
     fraction wider than live's at the same size, and once .sq-grid collapses to
     one column the line has no slack left, so names like "Abby Guillemette"
     clip by 1-5% instead of wrapping (nowrap is what makes .fit work). 0.95
     buys 5% of headroom, which clears the worst case measured (4.6%), and
     leaves desktop -- where the parity numbers live -- untouched. */
  .fit > .hl { font-size: calc(var(--k) * 0.95cqw); }
  /* The per-icon widths are inline on .loc-feat-icon, from live's desktop
     measurements, so they survive the collapse to one column and eat 160 of a
     390px screen. The homepage copy of this module already caps at 96px here;
     cap this one to the same number so the two still match on a phone
     (redline #30, "they all match one another"). */
  .loc-feat-icon { max-width: 96px; }
  .loc-hero { min-height: 0; }
  .loc-hero > .sq-grid { padding-top: 130px; padding-bottom: 70px; }
  .loc-intro-media img { height: auto; }
  .loc-ind-media { display: block; height: auto; }
  .loc-ind-media img { position: static; height: auto; }
  .loc-ind-media h3 { margin: -150px 24px 24px; }
  .loc-svc-row, .loc-high-col { display: grid; grid-template-columns: 1fr;
                                grid-template-rows: none; row-gap: 20px;
                                padding-top: 0; }
  .loc-high-card { min-height: 100px; }
  .loc-feat-txt { padding-top: 0; }
  .contact-boxed { padding: 56px 0; }
  /* 36px of side padding is 72px of a 367px column. */
  .contact-boxed .contact-head { padding: 40px 22px; }
  .contact-boxed .contact-cols { display: block; }
  .contact-boxed .contact-right { margin-top: 40px; }
  .contact-boxed .contact-left .map-embed { height: 320px; }
}


/* --- Location sections that differ by city -------------------------------
   Live's three location pages are not one template. Knoxville runs Recent
   Project Highlights light and Industries We Serve dark, the exact inverse of
   Huntsville; Chattanooga keeps Huntsville's polarity but drops a Lookout
   Mountain photograph behind Highlights at only a 0.15 scrim, so the mountain
   is plainly visible where the other two are flat green. Measured, then copied,
   because Jon asked for a direct copy. --------------------------------------- */

/* Knoxville: Recent Project Highlights on the light ground. */
.loc-high--light { background-color: var(--cream); background-image: var(--topo-mist);
                   background-size: cover; background-position: center;
                   --scrim-color: var(--cream-scrim); --scrim: 0.78; }
.loc-high--light::before { content: ""; position: absolute; inset: 0; z-index: 0;
                           pointer-events: none;
                           background: var(--scrim-color); opacity: var(--scrim); }
.loc-high--light .loc-high-h { color: var(--dark-green); }
.loc-high--light .loc-high-box { background: var(--sage); color: var(--dark-green); }
.loc-high--light .loc-high-card { background: rgba(var(--pine-rgb), 0.81); }
.loc-high--light .loc-high-card h4 { color: var(--white); }

/* Chattanooga: Recent Project Highlights over the Lookout Mountain photograph. */
.loc-high--photo { background-color: var(--dark-green); background-image: var(--img);
                   background-size: cover; background-position: center;
                   --scrim-color: var(--dark-green); --scrim: 0.15; }
.loc-high--photo::before { content: ""; position: absolute; inset: 0; z-index: 0;
                           pointer-events: none;
                           background: var(--scrim-color); opacity: var(--scrim); }
.loc-high--photo .loc-high-box { background: rgba(var(--sienna-brown-rgb), 0.47); }
.loc-high--photo .loc-high-card { background: rgba(var(--deep-forest-rgb), 0.53); }

/* Knoxville: Industries We Serve on the dark ground, under the dark topo. */
.loc-ind--dark { background-color: var(--dark-green); background-image: var(--topo-dark);
                 --scrim-color: var(--dark-green); --scrim: 0.33; }
.loc-ind--dark::before { content: ""; position: absolute; inset: 0; z-index: 0;
                         pointer-events: none;
                         background: var(--scrim-color); opacity: var(--scrim); }
.loc-ind--dark .loc-ind-h { color: var(--white); }
/* Live's panel here is sage at 0.39 over the dark ground, with dark green
   headings and sage body. That composites to 2.88:1 for the headings and
   2.68:1 for the body -- the one place live's own contrast is unreadable
   rather than merely low, and this is a page the firm buys traffic for.
   Deepening the panel to 0.22 and setting the copy white keeps the translucent
   sage panel live has and takes the body to 7.1:1. */
.loc-ind--dark .loc-ind-panel { background: rgba(var(--light-sage-rgb), 0.22); }
.loc-ind--dark .loc-ind-panel .loc-ind-intro { color: var(--sage); }
.loc-ind--dark .loc-ind-panel h4 { color: var(--white); }
.loc-ind--dark .loc-ind-panel p { color: var(--white); }
.loc-ind--dark .loc-ind-media h3 { color: var(--white); }

/* Chattanooga sets the industries closing statement in a card. */
.loc-ind-close-card { background: rgba(var(--sienna-brown-rgb), 0.69); border-radius: 25px;
                      padding: 20px 27px; }
.loc-ind-media .loc-ind-close-card { position: relative; z-index: 1;
                                     margin: 0 51px 84px 57px; }
.loc-ind-media .loc-ind-close-card h3 { margin: 0; }
/* 51 + 57px of side margin is a desktop inset. On a 390px screen it leaves the
   card 259px wide, and the 29.3px heading inside needs 258px of a 205px inner
   box. This override has to live HERE, not up in the location mobile block --
   same specificity as the rule above, so only source order decides, and the
   block further up the file loses. (ARCHITECTURE s.12.) */
@media (max-width: 1023px) {
  .loc-ind-media .loc-ind-close-card { margin: 0 0 40px; }
}

/* ==========================================================================
   SERVICES -- direct reproduction of live's layout
   ==========================================================================
   Measured off production, same method as the location pages (PARITY_SPEC
   s.18.2). Live's /services/ is nine content sections; the rebuild had eight,
   and 618px of accumulated drift. What a content diff could not see:

     * live's card grids are STAGGERED, not uniform. The Civil Site Design band
       runs 6/8/8 columns across the top row and 6/6/5/4 across the second; Real
       Estate Due Diligence runs 5/6/7/6 then four even 6s. Ours were flat
       four-column grids.
     * three flat colour bands (near-white, sage, mid-green) carry the
       Additional Services list at 152px each. Ours was one card grid.
     * the closing CTA is its own dark green band, not a button.

   Cards are .sq-card with --fill / --p set per group, so the fills below are
   live's own alpha values against the 2026 tokens.
   ------------------------------------------------------------------------- */
.sq-card { background: var(--fill, transparent); border-radius: var(--r, 25px);
           padding: var(--p, 20px 19px); display: flex; flex-direction: column;
           justify-content: center; min-width: 0; }
.sq-card .fit > .hl,
.sq-card .sq-card-h { color: var(--card-h, var(--white)); }
.sq-card .sq-card-h { font-size: 21px; line-height: 1.16; }
.sq-card p { margin: 14px 0 0; color: var(--card-p, var(--sage)); font-size: 16px;
             line-height: 1.2; }
.sq-flat { background: var(--ground); }

/* --- S0 hero (711) -------------------------------------------------------
   Live runs the topo at full strength here: the overlay is present but at
   opacity 0, so the contour lines sit right on the dark green. */
.svc-hero { background-color: var(--dark-green); background-image: var(--topo-dark);
            background-size: cover; background-position: center; }
.svc-hero > .sq-grid { min-height: 711px;
  grid-template-rows: 177px minmax(264px, auto) 38px minmax(34px, auto) 1fr; }
.svc-hero-h { grid-row: 2; color: var(--white); text-align: center; }
.svc-hero-h > .hl { line-height: 1.0; }
.svc-hero-sub { grid-row: 4; text-align: center; font-size: 29.3px; line-height: 1.16;
                /* Live sets this in a vivid green (92,169,76) that the 2026
                   palette does not carry; Pine on Deep Forest is 2.38:1.
                   Brand Sage is 6.36:1 and holds the same lighter-green role. */
                color: var(--brand-sage); }

/* --- S1 experience and quality (440) ------------------------------------ */
.svc-quality > .sq-grid { min-height: 440px;
  grid-template-rows: 48px minmax(150px, auto) 1fr minmax(134px, auto) 48px; }
.svc-quality-media { grid-row: 2 / 5; align-self: start; }
.svc-quality-media img { width: 100%; height: 345px; object-fit: cover; }
.svc-quality-h { grid-row: 2; color: var(--rust); }
.svc-quality-card { grid-row: 4; background: var(--rust); border-radius: 25px;
                    padding: 20px 44px; display: flex; align-items: center; }
/* Live sets this line burnt orange on the dark rust card: 3.1:1 there, 1.51:1
   in the 2026 palette. Light Sage, 5.25:1 -- the third instance of this same
   pairing, all recorded in PARITY_SPEC s.18.7. */
.svc-quality-card h4 { margin: 0; color: var(--sage); font-size: 21px;
                       font-weight: 700; line-height: 1.22; }

/* --- S2 civil site design (1230) ---------------------------------------- */
/* Redline #53, Jon 2026-09-21: "I'd like less white space between the service
   items." The gaps here are fixed grid rows, and the 141px between the two card
   rows was the worst of them. 83/141/70 -> 48/56/48, top pad 35 -> 24.

   NOT touched, on purpose: the background photograph. Jon, same redline: "I
   think this background image makes this module look too busy... Then, I'll
   take a look and decide on the background image." That decision is his and is
   still open. */
.svc-design > .sq-grid { min-height: 1040px;
  grid-template-rows: 24px minmax(115px, auto) 48px minmax(200px, auto)
                      56px minmax(268px, auto) 48px minmax(160px, auto) 1fr; }
.svc-design-h { grid-row: 2; color: var(--white); }
.svc-design-foot { grid-row: 8; color: var(--sage); font-size: 17.7px; line-height: 1.2; }
.svc-design-callout { grid-row: 8; text-align: center; color: var(--brand-sage); }
/* Live draws this as a pill with a hairline outline, not a text link. */
.svc-design-cta { grid-row: 9; align-self: start; justify-self: center;
                  color: var(--sage); font-size: 20.2px; font-weight: 800;
                  font-family: var(--body-font); text-transform: uppercase;
                  border: 1px solid var(--sage); border-radius: var(--pill);
                  padding: 12px 46px; text-align: center; }

/* --- S3 project management (921) ---------------------------------------- */
.svc-pm > .sq-grid { min-height: 921px;
  grid-template-rows: 107px minmax(115px, auto) 49px minmax(169px, auto)
                      94px minmax(310px, auto) 1fr; }
.svc-pm-h { grid-row: 2; color: var(--rust); }
.svc-pm-note { grid-row: 4; background: rgba(255, 255, 255, 0.07); border-radius: 25px;
               padding: 24px 37px; display: flex; align-items: center; }
/* 4.21:1 against live's own 3.80:1 for the same pairing. Below AA for body
   text either way; kept because dropping the accent loses the only warm note
   in this band, and it is an improvement on live rather than a regression. */
.svc-pm-note p { margin: 0; color: var(--orange); font-size: 17.7px;
                 line-height: 1.2; text-align: right; }
.svc-pm-media { grid-row: 1 / 5; align-self: start; margin-top: 95px; }
.svc-pm-media img { width: 100%; height: 380px; object-fit: cover; }

/* --- S4 real estate due diligence (1151) -------------------------------- */
.svc-due > .sq-grid { min-height: 1151px;
  grid-template-rows: 83px minmax(88px, auto) 56px minmax(60px, auto)
                      73px minmax(275px, auto) 41px minmax(257px, auto) 1fr; }
.svc-due-h { grid-row: 2; color: var(--white); }
.svc-due-lead { grid-row: 4; color: var(--white); font-size: 21px; line-height: 1.22;
                text-align: center; }
.svc-due-foot { grid-row: 9; align-self: start; color: var(--white);
                font-size: 17.7px; line-height: 1.2; text-align: center; }

/* --- S5 / S6 / S7 the three Additional Services bands (152 each) --------- */
.svc-band > .sq-grid { min-height: 152px; align-items: center; }
.svc-band-title { font-size: 72.2px; line-height: 1.03; color: var(--dark-green);
                  text-align: center; }
.svc-band h3 { font-size: 29.3px; line-height: 1.16; text-align: center;
               color: var(--band-ink, var(--dark-green)); }

/* --- S8 closing CTA (239) ------------------------------------------------ */
.svc-cta { background: var(--dark-green); }
.svc-cta > .sq-grid { min-height: 239px; grid-template-rows: 60px minmax(110px, auto) 1fr; }
.svc-cta-h { grid-row: 2; text-align: center; color: var(--brand-sage); }

@media (max-width: 1023px) {
  .svc-hero > .sq-grid, .svc-quality > .sq-grid, .svc-design > .sq-grid,
  .svc-pm > .sq-grid, .svc-due > .sq-grid, .svc-band > .sq-grid,
  .svc-cta > .sq-grid { min-height: 0; }
  .svc-quality-media img, .svc-pm-media img { height: auto; }
  .svc-pm-media { margin-top: 0; }
  .svc-band-title { font-size: 40px; }
  .svc-pm-note p { text-align: left; }
}
/* Mobile redline #73. .fit sizes every line to its own container width, so on
   one column the seven tiles ran 18.5px to 42.7px. On a phone they read as a
   set, so one size, and long titles wrap instead of shrinking. */
@media (max-width: 767px) {
  .svc-design .sq-card .fit > .hl { font-size: 24px; line-height: 1.1;
                                    white-space: normal; }
}

/* --- /contact/ hero, measured (PARITY_SPEC s.20) ------------------------- */
.contact-hero > .sq-grid { min-height: 535px;
  grid-template-rows: 160px minmax(118px, auto) 42px minmax(82px, auto) 133px; }
.contact-hero-h { grid-row: 2; color: var(--white); }
.contact-hero-sub { grid-row: 4; text-align: center; color: var(--brand-sage);
                    font-weight: 700; }
@media (max-width: 1023px) {
  .contact-hero > .sq-grid { min-height: 0; grid-template-rows: none;
                             padding-top: 130px; padding-bottom: 60px; }
}

/* ==========================================================================
   ABOUT US -- direct reproduction of live's layout (PARITY_SPEC s.20)
   ==========================================================================
   Live's team bios are two stacked photographs, not a headshot beside text: a
   backdrop at x=43 under a dark-green tint that varies per person, with the
   headshot at x=214 on a sage ground overlapping it. Ours was a plain two
   column block, which is most of the 432px the page was over.
   ------------------------------------------------------------------------- */
/* Last track is 1fr for the same reason every other measured section has one:
   it absorbs the few pixels our type metrics differ from live's instead of
   pushing the section past live's height. */
.ab-hero > .sq-grid { min-height: 781px;
  grid-template-rows: 137px minmax(258px, auto) 58px minmax(220px, auto) 1fr; }
/* Redline #51: "center the text". The lines are .fit blocks with nowrap, so
   centring the parent centres each line independently, the way live does. */
.ab-hero-h { grid-row: 2; color: var(--white); text-align: center; }
/* Four scaled lines in one block, not a heading plus a separate list: live runs
   "Proudly Serving the Southeast" at 40.4 and the three state lines at 42.6 /
   41.9 / 42.3, spaced 58 / 56 / 54 apart. */
.ab-hero-sub { grid-row: 4; text-align: center; color: var(--white); }
.ab-hero-sub > .hl { line-height: 1.35; }

.ab-bio { background: var(--cream); }
.ab-bio > .sq-grid { min-height: var(--sh); padding-top: 42px; padding-bottom: 42px;
                     align-items: start; }
.ab-media { position: relative; height: var(--bh); }
.ab-back { position: absolute; left: 0; top: 0; width: var(--bw); height: var(--bh);
           object-fit: cover; max-width: none; }
/* Live tints the backdrop and leaves the headshot untouched, so the tint is a
   layer between them rather than a filter on either. */
.ab-media::after { content: ""; position: absolute; left: 0; top: 0;
                   width: var(--bw); height: var(--bh);
                   background: var(--dark-green); opacity: var(--tint); }
.ab-shot { position: absolute; left: 171px; top: 80px; z-index: 1;
           width: 387px; height: var(--hh); object-fit: cover; max-width: none;
           background: var(--sage); }
.ab-text { margin-top: var(--ty); }
.ab-name .hl { color: var(--dark-green); }
.ab-role { margin-top: 24px; }
.ab-role .hl { color: var(--rust); font-weight: 700; }
.ab-text p { margin: 24px 0 0; color: var(--mid-green); font-size: 17.7px;
             line-height: 1.2; }

.ab-faq-sec { background-color: var(--dark-green); background-image: var(--topo-dark);
              background-size: cover; background-position: center;
              --scrim-color: var(--dark-green); }
.ab-faq-sec::before { content: ""; position: absolute; inset: 0; z-index: 0;
                      pointer-events: none;
                      background: var(--scrim-color); opacity: var(--scrim); }
.ab-faq-sec > .sq-grid { min-height: 926px;
  grid-template-rows: 41px minmax(56px, auto) 98px minmax(255px, auto)
                      53px minmax(54px, auto) 24px minmax(204px, auto) 1fr; }
.ab-faq-h { grid-row: 2; text-align: center; color: var(--white); font-size: 54.3px;
            line-height: 1.03; }
.ab-faq-list { grid-row: 4; }
.ab-faq { border-bottom: 1px solid rgba(var(--light-sage-rgb), 0.35); padding: 14px 0; }
.ab-faq summary { cursor: pointer; list-style: none; color: var(--white);
                  font-family: var(--heading-font); font-weight: 900;
                  text-transform: uppercase; font-size: 21px; line-height: 1.16; }
.ab-faq summary::-webkit-details-marker { display: none; }
.ab-faq summary::after { content: "+"; float: right; margin-left: 16px; }
.ab-faq[open] summary::after { content: "\2013"; }
.ab-faq-body { color: var(--sage); font-size: 16px; line-height: 1.3; padding-top: 12px; }
.ab-faq-body .stat-list li { margin: 6px 0 0 18px; list-style: disc; }
.ab-member { grid-row: 6; }
.ab-member .hl { color: var(--white); font-weight: 700; }
.ab-badges { grid-row: 8; display: flex; align-items: flex-start; gap: 11px; }
.ab-badges img { width: 160px; height: auto; }
.ab-chamber { flex: 0 0 273px; }
/* BBB seal (Jon, 2026-10-01) sits under the chamber name, in the ~119px the
   name leaves empty beside the two 190-204px badges, so the badge row and the
   section keep their measured heights. BBB's own hosted seal and link, as
   their seal programme requires. */
/* The seal's own lettering is dark on a transparent PNG, so on the green FAQ
   ground it disappeared (Jon, 2026-10-01): it sits on a Mist card. Gap and
   padding are trimmed so card + name still fit inside the 204px badge row. */
.ab-chamber-col { flex: 0 0 273px; display: flex; flex-direction: column; gap: 4px; }
.ab-chamber-col .ab-chamber { flex: none; }
.ab-badges .ab-bbb { display: block; width: max-content; line-height: 0;
                     padding: 4px 8px; border-radius: 10px; background: var(--mist); }
.ab-badges .ab-bbb img { width: 150px; height: auto; border: 0; }
.ab-chamber .hl { color: var(--white); }
.ab-faq-media { grid-row: 2 / 10; align-self: start; margin-top: 47px;
                position: relative; }
.ab-faq-media img { width: 100%; height: 791px; object-fit: cover; }
.ab-faq-media::after { content: ""; position: absolute; inset: 0;
                       background: var(--dark-green); opacity: 0.5; }

@media (max-width: 1023px) {
  .ab-hero > .sq-grid, .ab-bio > .sq-grid, .ab-faq-sec > .sq-grid {
    min-height: 0; grid-template-rows: none; }
  .ab-hero > .sq-grid { padding-top: 130px; padding-bottom: 60px; }
  .ab-media { height: auto; }
  .ab-back, .ab-media::after { position: static; width: 100%; height: auto; }
  .ab-media::after { display: none; }
  .ab-shot { position: static; width: 100%; height: auto; margin-top: -60px; }
  .ab-text { margin-top: 28px; }
  .ab-badges { flex-wrap: wrap; }
  .ab-faq-media img { height: auto; }
}
/* Mobile redline #71: "Ditch this image and reduce section on mobile." */
@media (max-width: 767px) {
  .ab-faq-media { display: none; }
  .ab-faq-sec > .sq-grid { padding-bottom: 48px; }
}

/* --- /blog/ index and project meta headings, measured ---------------------
   Live's blog index runs "Engaging Topics" at 101.5px with "Civil Engineering
   Expertise" at 53.9 under it; ours said "Blog". Project pages label their
   meta block "Project / Details" as two scaled lines at 48.7 / 53.0. */
.blog-index-sub { color: var(--brand-sage); font-size: 53.9px; line-height: 1.1;
                  text-align: center; margin-top: 34px; }
/* Live's blog hero is 535px: the h1 at 101.5 starting at y=171 and the
   subhead at 53.9 starting at y=309. */
.blog-hero { min-height: 535px; padding: 0; }
.blog-hero > .container { padding-top: 145px; padding-bottom: 60px; }
.blog-hero h1 { font-size: 101.5px; line-height: 1.05; }
.project-meta-title { color: var(--orange); margin-bottom: 20px; }
.project-meta-title .hl { display: block; line-height: 1.04; }
.project-meta-title .hl:nth-child(1) { font-size: 48.7px; }
.project-meta-title .hl:nth-child(2) { font-size: 53px; }
@media (max-width: 1023px) {
  .blog-index-sub { font-size: 30px; }
  /* 101.5px is live's desktop measurement and had no mobile counterpart:
     "Engaging Topics" ran 215px past its box, the worst single clip on the
     site. */
  .blog-hero h1 { font-size: min(101.5px, 13vw); }
  /* 535px is live's desktop hero. Once the heading is a third of the size it
     leaves half a phone screen of empty ground under the subhead. */
  .blog-hero { min-height: 0; }
  .blog-hero > .container { padding-top: 96px; padding-bottom: 48px; }
  .theme-hero .eyebrow { font-size: 20px; }
  .project-meta-title .hl:nth-child(1),
  .project-meta-title .hl:nth-child(2) { font-size: 32px; }
}

/* --- Blog post, measured off live (PARITY_SPEC s.21) ---------------------
   Live is ONE near-white section 2995px tall: title at 54.3px centred in a
   1015px column (c3 span 18), a marker-font byline at 26px, then the body.
   The rebuild had a dark hero band over a content band and a thumbnail at the
   top of every post, neither of which live has, and ran 4142px.

   One divergence: live sets the BODY COPY in burnt orange, which is 3.80:1 on
   its near-white ground. Verified off the rendered pixels, not just the DOM.
   These are the longest-form pages on the site; body text stays Deep Forest at
   about 11:1. Flagged for Jon rather than reproduced. ---------------------- */
.post-sec { background: var(--cream); }
.post-sec > .sq-grid { padding-top: 161px; padding-bottom: 90px; }
.post-title { font-size: 54.3px; line-height: 1.1; color: var(--dark-green);
              text-align: center; margin: 0; }
.post-byline { margin-top: 30px; text-align: center; font-family: var(--meta-font);
               font-size: 26px; line-height: 1.2; color: rgba(var(--pine-rgb), 0.74); }
.post-byline a { color: inherit; }
.post-byline-dot { margin: 0 10px; }
/* One post pastes a bare URL ("Link to our services page: https://...") as a
   single unbreakable token. At 390px that one word ran the paragraph 49px past
   its column and took .sqs-html-content, .fe, .sq-grid and the section with it
   -- 5 of the 7 remaining clipped groups, from one URL. Body copy should break
   long tokens rather than clip them. */
.post-body { overflow-wrap: anywhere; margin-top: 36px; font-size: 17.7px; line-height: 1.38;
             color: var(--text-dark); }
/* Live separates paragraphs by line-height alone: its first paragraph runs
   381 to 503, which is exactly five lines at 24.4px and no margin. An 18px
   margin here put the post 431px over across the whole page. Its list text is
   979px wide in a 1015px column, so the indent is ~22px, not the browser
   default 40 plus a margin. */
.post-body p { margin: 0; font-size: 17.7px; }
.post-body strong { font-weight: 700; }
.post-body ol, .post-body ul { margin: 0; padding-left: 22px; }
.post-body ol { list-style: decimal; }
.post-body ul { list-style: disc; }
.post-body li { margin-bottom: 12px; }
.post-body li p { margin: 0; }
.post-body a { text-decoration: underline; text-underline-offset: 3px; }
.post-signoff { margin-top: 48px; text-align: center; font-family: var(--heading-font);
                font-weight: 900; text-transform: uppercase; font-size: 16px;
                line-height: 1.6; color: var(--dark-green); }
.post-signoff a { display: block; }
.post-signoff .back-to-blog { margin-top: 28px; font-size: 14px; color: var(--mid-green); }

@media (max-width: 1023px) {
  .post-sec > .sq-grid { padding-top: 120px; padding-bottom: 60px; }
  .post-title { font-size: 34px; }
  .post-byline { font-size: 19px; }
}

/* --- /projects/ and /blog/ index grids, measured (PARITY_SPEC s.21) -------
   Live's project cards are 401px squares in a 3-up grid at x=69/520/970, with
   the title set OVER the photograph at the top left rather than captioned
   beneath it. Ours stacked photo-then-caption and ran 361px tall per row.
   Blog cards are 414x277 images at x=69/513/957 with a numeric date, a 29.3px
   title and a Read More line.

   Live's project title sits directly on the photograph. Whether it carries a
   scrim there could not be read -- live's project images do not load in the
   capture browser. A soft top scrim is added here so the title stays legible
   over any photo; flagged for Jon. ---------------------------------------- */
.proj-index { background: var(--cream); }
.proj-index > .container { padding-top: 95px; padding-bottom: 95px; }
/* Mobile redlines #75-#77. The green hero gets more room under its title (and
   the green underline live carries, via .rule-under in build_site.py), the gap
   above the first card comes down from 121px toward live's 72px, and the
   titles get a denser mist band so they read over busy photographs. */
@media (max-width: 767px) {
  .svc-hero.projects-hero > .sq-grid { padding-bottom: 84px; }
  .proj-index > .container { padding-top: 40px; padding-bottom: 56px; }
  .proj-card::after { height: 42%; background: linear-gradient(
                        var(--cream-scrim) 0%, rgba(var(--cream-scrim-rgb), 0.94) 60%,
                        rgba(var(--cream-scrim-rgb), 0) 100%); }
}
.proj-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px;
             max-width: 1303px; margin: 0 auto; }
.proj-card { position: relative; display: block; aspect-ratio: 1 / 1;
             overflow: hidden; background: var(--white); }
.proj-card img { position: absolute; inset: 0; width: 100%; height: 100%;
                 object-fit: cover; }
.proj-card::after { content: ""; position: absolute; left: 0; right: 0; top: 0;
                    height: 45%; background: linear-gradient(
                      var(--cream-scrim) 0%, rgba(var(--cream-scrim-rgb), 0.82) 55%,
                      rgba(var(--cream-scrim-rgb), 0) 100%); }
.proj-card h3 { position: absolute; z-index: 1; left: 28px; right: 28px; top: 26px;
                margin: 0; font-size: 29.3px; line-height: 1.16;
                color: var(--dark-green); }

.blog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px 30px;
             max-width: 1302px; margin: 0 auto; }
.blog-grid .card-link { display: block; }
.blog-grid .card-img img { width: 100%; height: 277px; object-fit: cover; display: block; }
.blog-grid .card-meta { margin-top: 46px; font-size: 21px; line-height: 1.16;
                        color: var(--mid-green); font-family: var(--heading-font);
                        font-weight: 900; letter-spacing: 0; text-transform: none; }
.blog-grid h3 { margin: 18px 0 0; font-size: 29.3px; line-height: 1.16;
                color: var(--dark-green); }
.blog-grid .read-more { display: inline-block; margin-top: 22px; font-size: 16px;
                        color: var(--mid-green); text-transform: uppercase;
                        font-family: var(--heading-font); font-weight: 900; }

.projects-hero > .sq-grid { min-height: 426px;
  grid-template-rows: 180px minmax(96px, auto) 1fr; }
.projects-hero-h { grid-row: 2; color: var(--white); font-size: 83.1px;
                   line-height: 1.03; }

.post-submark { width: 50px; height: 64px; margin: 0 auto 18px; display: block; }

@media (max-width: 1023px) {
  .proj-grid, .blog-grid { grid-template-columns: 1fr; gap: 28px; }
  .proj-card h3, .blog-grid h3 { font-size: 22px; }
  .blog-grid .card-meta { margin-top: 18px; font-size: 16px; }
  .projects-hero > .sq-grid { min-height: 0; grid-template-rows: none;
                              padding-top: 130px; padding-bottom: 60px; }
  .projects-hero-h { font-size: 38px; }
}

/* --- /careers/, /directory-contacts-download/ and the project pages -------
   All measured, PARITY_SPEC s.21. ----------------------------------------- */
.careers-hero { min-height: 605px; padding: 0; }
.careers-hero > .container { padding-top: 150px; padding-bottom: 70px; }
.careers-hero h1 { font-size: 80.1px; line-height: 1.05; }
.careers-hero .hero-subhead, .careers-hero .lede { font-size: 26px; line-height: 1.2;
                                                   margin-top: 60px; }
.directory-hero { min-height: 535px; padding: 0; }
.directory-hero > .container { padding-top: 165px; padding-bottom: 90px; }

/* Live's case study: hero 481, gallery 420, one 757px band for details and
   scope. The rebuild stacked all three in a single 1194px block. */
.case-hero > .sq-grid { min-height: 481px;
  grid-template-rows: 166px minmax(184px, auto) 1fr; }
.case-hero-h { grid-row: 2; color: var(--white); text-align: center;
               font-size: 85.8px; line-height: 1.04; }
.case-gallery { background: var(--cream); }
.case-gallery > .container { padding-top: 43px; padding-bottom: 43px; }
.case-gallery-grid { display: grid; grid-template-columns: 2fr 3fr; gap: 24px; }
.case-gallery-grid img { width: 100%; height: 334px; object-fit: cover; }
.case-body { background: var(--cream); }
/* Live's right column is the summary card (0 to 149) then the scope from 194;
   the rust card runs 0 to 394 alongside both. An earlier pass had the summary
   card occupying a 394px track, which pushed the scope down and the section
   219px over. */
/* Redlines #55/#56/#59/#60, Jon 2026-09-21: "Make white space dynamic to reduce
   to the amount of text that is in each of these project pages. This has lots
   of white space below the Project Details and project scope of services...
   Don't ditch any of the copy. I like the copy and the flow; there is just so
   much wasted space."

   min-height was live's 757px, which on a short page left ~100px of empty cream
   under the last scope item and forced the rust card to stretch past its
   content. The tracks now size to what is in them, and the final 1fr is gone so
   there is no slack row to fill. Copy is untouched. */
.case-body > .sq-grid { padding-top: 0; padding-bottom: 48px;
  grid-template-rows: minmax(149px, auto) 36px minmax(200px, auto); }
/* Redline #39: "Make this project details rectangle the full height of the
   text to the right. I want it to be much longer so we can add some more
   information." Live pins it at 394px; stretching it across the tracks it
   already spans lets it grow with whatever goes in it, and it now ends level
   with the scope column beside it. This is a deliberate step away from live's
   geometry on these four pages. */
/* Redline #39 asked for this card to run the full height of the text beside it,
   "so we can add some more information". The information was never added, so on
   a page with three meta fields it rendered as a 717px rust box with ~350px of
   nothing under the last line -- which is what #59/#60 then flagged. It now
   sizes to its content and still grows with whatever is put in it, which is
   what #39 was actually after. The newer redline wins where they conflict. */
.case-meta { grid-row: 1 / 4; align-self: start;
             background: var(--rust); border-radius: 20px;
             padding: 28px 22px; color: var(--white); }
.case-meta-title .hl { display: block; color: var(--sage); line-height: 1.04; }
.case-meta-title .hl:nth-child(1) { font-size: 48.7px; }
.case-meta-title .hl:nth-child(2) { font-size: 53px; }
.case-meta dl { margin: 34px 0 0; }
.case-meta dt { font-family: var(--heading-font); font-weight: 900;
                text-transform: uppercase; font-size: 21px; line-height: 1.16; }
.case-meta dd { margin: 6px 0 24px; font-size: 16px; line-height: 1.2; }
.case-summary { grid-row: 1; align-self: start; background: rgba(var(--light-sage-rgb), 0.73);
                border-radius: 25px; padding: 27px 39px; }
.case-summary h3 { font-size: 29.3px; line-height: 1.16; color: var(--dark-green); }
.case-summary p { margin: 16px 0 0; font-size: 16px; line-height: 1.2; color: var(--mid-green); }
.case-scope { grid-row: 3; align-self: start; }
/* Same redlines: "I also think there are like four different text colors on
   this page that really bother me. Let's simplify."
   The cream column ran three -- Sienna Brown on this H2, Deep Forest on the
   item headings, Pine on the body. This H2 goes Deep Forest, so the column is
   two: Deep Forest for every heading, Pine for every paragraph. Rust stays as
   the Project Details card's GROUND, where it still does work. */
.case-scope h2 { font-size: 54.3px; line-height: 1.03; color: var(--dark-green);
                 margin-bottom: 26px; }
.case-scope .scope-item { margin-bottom: 22px; }
.case-scope .scope-item h4 { font-size: 21px; line-height: 1.16; color: var(--dark-green); }
.case-scope .scope-item p { margin: 10px 0 0; font-size: 16px; line-height: 1.2;
                            color: var(--mid-green); }

@media (max-width: 1023px) {
  .careers-hero, .directory-hero, .case-hero > .sq-grid, .case-body > .sq-grid {
    min-height: 0; grid-template-rows: none; }
  .careers-hero > .container, .directory-hero > .container {
    padding-top: 130px; padding-bottom: 60px; }
  .careers-hero h1 { font-size: 40px; }
  .case-hero-h { font-size: 36px; }
  .case-gallery-grid { grid-template-columns: 1fr; }
  .case-gallery-grid img { height: auto; }
  .case-meta-title .hl:nth-child(1), .case-meta-title .hl:nth-child(2) { font-size: 32px; }
  .case-scope h2 { font-size: 32px; }
}
/* Mobile redlines #72, #79-#81. Collapsing the measured grid dropped the
   166-180px spacer row that clears the fixed header, so the first title line
   started at y=56 under a 78px header. Same clearance the projects and careers
   heroes already use. */
@media (max-width: 1023px) {
  .svc-hero > .sq-grid, .case-hero > .sq-grid {
    grid-template-rows: none; padding-top: 130px; padding-bottom: 60px; }
}

/* Live closes the case study with a 32px spacer band before the footer. */
.case-tail { background: var(--cream); height: 32px; }

/* --- Project prev/next (redline #58) -------------------------------------
   Jon: "Add a Next Project button so customers can click through our past
   projects more easily, instead of going to the top menu bar, then the
   Projects page, and selecting a new project."

   Live has a band in this position, but it is two bare chevrons at the far
   edges of a sage strip -- no label, no project name, easy to miss, which is
   most of why it does not get used. Same position and ground; each side names
   itself and its destination. Order and wrap-around come from PROJECT_ORDER in
   build_site.py, so adding a project rewires the neighbours on its own. */
.case-nav { background: var(--sage); }
.case-nav-row { display: flex; align-items: center; justify-content: space-between;
                gap: 24px; padding: 30px 0; }
.case-nav-link { display: flex; align-items: center; gap: 14px;
                 min-height: 44px; color: var(--dark-green);
                 text-decoration: none; max-width: 46%; }
.case-nav-next { text-align: right; }
.case-nav-chev { font-size: 40px; line-height: 1; flex: none; }
.case-nav-text { display: block; min-width: 0; }
.case-nav-label { display: block; font-family: var(--heading-font); font-weight: 900;
                  text-transform: uppercase; font-size: 14px; letter-spacing: 0.04em;
                  color: var(--mid-green); }
.case-nav-title { display: block; font-family: var(--heading-font); font-weight: 800;
                  font-size: 19px; line-height: 1.2; margin-top: 3px; }
.case-nav-link:hover .case-nav-title { text-decoration: underline;
                                       text-underline-offset: 3px; }
@media (max-width: 1023px) {
  /* Two stacked rows rather than two cramped columns on a phone. */
  .case-nav-row { flex-direction: column; align-items: stretch; gap: 18px;
                  padding: 26px 0; }
  .case-nav-link { max-width: none; }
  .case-nav-next { flex-direction: row-reverse; justify-content: flex-end;
                   text-align: left; }
  .case-nav-chev { font-size: 32px; }
}
/* Live's careers form band is 806px. The generic .iframe-embed rule sets
   min-height: 720px, which a plain height cannot shrink past. */
.careers-form { padding: 0; }
.careers-form > .container { padding: 60px var(--page-padding); }
.careers-form .iframe-embed { height: 686px; min-height: 0; }

/* --- Header on light-topped pages ----------------------------------------
   Jon, redlines #18 and #19 (2026-09-10): "Using the white logo on the blog
   pages with a light background makes it impossible to see" and "Using the
   Sage color on this page makes these very hard to read."

   The header is transparent and absolutely positioned over the first section,
   which is a dark hero everywhere except the 18 blog posts. Sage on Mist is
   1.7:1; Deep Forest is 11.3:1. The Contact Us pill inverts to a solid Deep
   Forest button so it still reads as the primary action, at 11.3:1 rather
   than the sage-on-mist 1.7:1 it had. -------------------------------------- */
.site-header.is-dark-ink .main-nav a { color: var(--dark-green); }
.site-header.is-dark-ink .main-nav a:hover { color: var(--sienna-clay); }
.site-header.is-dark-ink .main-nav a.is-active { color: var(--sienna-clay); }
.site-header.is-dark-ink .main-nav .nav-cta {
  color: var(--mist);
  background: var(--dark-green);
  border-color: var(--dark-green);
}
.site-header.is-dark-ink .main-nav .nav-cta:hover {
  color: var(--white);
  background: var(--evergreen);
  border-color: var(--evergreen);
}
/* The Locations trigger is a <button>, not one of the .main-nav anchors, so it
   needs its own rule or it stays sage. */
.site-header.is-dark-ink .nav-dropdown-toggle { color: var(--dark-green); }
.site-header.is-dark-ink .nav-dropdown-toggle:hover { color: var(--sienna-clay); }
/* Redline #20. The rule above is a descendant selector, so it also caught the
   links INSIDE the Locations panel -- and that panel paints its own Deep Forest
   ground, so they went dark green on dark green and vanished. Jon: "These look
   blank when I select locations and try to select a page." The panel keeps its
   own ink regardless of the page behind it. */
.site-header.is-dark-ink .nav-dropdown-menu li a { color: var(--white); }
.site-header.is-dark-ink .nav-dropdown-menu li:hover a { color: var(--deep-forest); }
.site-header.is-dark-ink .nav-toggle span { background: var(--dark-green); }
/* Below 900 the header paints itself dark green, which would bury the Deep
   Forest logo. On a light page it stays transparent instead; the dark ink then
   reads against the page itself, exactly as it does at desktop. */
@media (max-width: 900px) {
  .site-header.is-dark-ink { background: transparent; }
}
@media (max-width: 1023px) {
  /* The mobile drawer paints its own dark ground, so the links go back to sage
     once it is open regardless of the page behind it. */
  .site-header.is-dark-ink .main-nav.is-open a { color: var(--sage); }
  .site-header.is-dark-ink .main-nav.is-open .nav-cta { color: var(--dark-green);
                                                        background: var(--sage);
                                                        border-color: var(--sage); }
}

/* --- Blog subheadings (Jon, 2026-09-10) ----------------------------------
   "This blog post has sub headings in bold for each of the subsections... I
   want to make all the blog posts match that... make bold and add an extra
   line too."

   Two shapes, both promoted by build_blog.promote_subheadings(): a bold lead-in
   that used to run inline with its paragraph, and a standalone all-caps
   paragraph. Both now sit on their own line with a blank line's worth of space
   above -- 26px against a 24.4px body line. --------------------------------- */
.post-body .post-sub {
  display: block;
  /* padding, not margin: this is the paragraph's first child, so a top margin
     collapses out through the <p> and the gap never appears. */
  padding-top: 26px;
  font-weight: 700;
  color: var(--dark-green);
}
/* No first-child exception. The post body is a single .sqs-html-content
   wrapper, so `.post-body > *:first-child` matches that wrapper and the
   descendant selector then zeroed EVERY subheading on the page rather than
   the first one. Every post opens on body copy anyway. */
.post-body .post-sub-line {
  margin-top: 26px;
  font-weight: 700;
  color: var(--dark-green);
}

/* Live's project gallery is two images at a per-page column split and height;
   a third image, where a page has one, gets its own full-width band. Only the
   NASA page has one. Measured, PARITY_SPEC s.21. */
.case-gallery-grid { grid-template-columns: var(--gc, 2fr 3fr); }
.case-gallery-grid img { height: var(--gh, 334px); }
.case-feature { background: var(--cream); }
.case-feature > .container { padding-top: 43px; padding-bottom: 43px; }
.case-feature img { width: 100%; height: var(--fh, 901px); object-fit: cover; }
@media (max-width: 1023px) {
  .case-gallery-grid img, .case-feature img { height: auto; }
}

/* Redline #33: the state list sits under "Proudly Serving the Southeast" and
   reads smaller than it, not the same size as live sets it. */
/* Redline #51: "use a different shade of green for the states so they pop."
   They were Light Sage, the same family as everything else on this ground.
   Live sets them in #5CA94C, the same green as the underline. */
.ab-hero-states { grid-row: 4; text-align: center; color: var(--accent-green);
                  align-self: end; margin-top: 14px; }
.ab-hero-states > .hl { line-height: 1.34; }

/* ==========================================================================
   SAGE PANELS -- the site-wide standard (Jon, redlines #24 #26 #27 #28 #29 #35)
   ==========================================================================
   "Make all contact forms have this sage-colored transparency behind them.
   This should be the standard for the whole site."

   Live already works this way and the rebuild had drifted off it: live's value
   cards are sage 0.76 at a 25px radius, its service cards sage ~0.53 at 25px,
   and its featured block is ONE sage 0.62 panel 1126px wide holding the label
   and all three posts rather than three separate cards. Ours were near-white
   at a 6px radius, which is why they read as white boxes with square corners.

   One deliberate departure from live: live's Civil Site Design panels are dark
   green at 0.75. Jon asked for sage there too, so the whole section reads as
   one family. Copy goes Deep Forest on them, 10.2:1.
   ------------------------------------------------------------------------- */
:root{
  --panel-sage-strong: rgba(var(--light-sage-rgb), 0.76);  /* value cards */
  --panel-sage-mid:    rgba(var(--light-sage-rgb), 0.62);  /* unused since redline #42 */
  --panel-sage-soft:   rgba(var(--light-sage-rgb), 0.53);  /* service cards */
  --panel-radius: 25px;
}

/* Redline #26: the Civil Site Design copy panels are sage, not live's dark
   green, so the copy reads dark on light like every other panel on the page.
   Those panels are still here -- #40 removed only the gradient BEHIND them.
   They run a higher alpha (0.86) than the featured panel used to, because they
   sit over the brightest part of the photograph where sage at 0.62 composites
   to within a few percent of the image and stops reading as a panel at all. */

/* Redline #42, 2026-09-17: "Get rid of this transparency hatch behind the blog
   posts." This was the sage 0.62 panel added by redlines #27 and #28; #42
   supersedes both -- do not put it back. Padding is kept so the label and the
   card rail hold their positions.

   The label HAD to change treatment with it. On the sage panel it was Deep
   Forest; with the panel gone it sits straight on the photograph, and that
   ground is not one colour -- it runs #394D49 to #A7AAA9 across the width of
   the word. No flat colour survives it: white measures 7.32:1 against the
   median but 2.34:1 against the lightest tenth of the pixels it crosses, and
   Deep Forest is the exact mirror (5.23:1 light, 1.36:1 dark). That variance is
   why the panel was there.

   So: white, plus a dark halo, which gives the glyphs their own local ground
   without putting a box back on the photograph. Checked against the rendered
   page, not just the numbers -- the word crosses a yellow road line and white
   alone washes out on it. If a measurable ground is ever preferred over a
   shadow, a small chip behind the word is the alternative. */
.featured-wrap { margin-top: 44px; padding: 26px 30px 32px; }
.featured-wrap .featured-label { margin-top: 0; color: var(--white);
  text-shadow: 0 1px 2px rgba(var(--deep-forest-rgb), 0.95), 0 0 10px rgba(var(--deep-forest-rgb), 0.7); }
.featured-posts { margin-top: 14px; gap: 22px; }
.featured-posts .card-link { background: transparent; border-radius: 14px; overflow: hidden; }
/* Card body padding lives on `.featured-section .featured-posts .card-body`
   (redlines #43-#45), which outranks anything set here. Do not add a padding
   declaration to this selector -- it will not apply and it will mislead the
   next person. */
/* Redline #21: the longest of the three titles used to sit hard against the
   card edge. The body column is the constraint, not the type size. */
.featured-posts .card-body h4 { font-size: 0.95rem; line-height: 1.35; }

/* Redline #29: the homepage service cards are sage on live, not white. */
.services-section .service-card {
  background: var(--panel-sage-soft);
  border: 0;
  border-radius: var(--panel-radius);
  padding: 26px 28px;
}

/* Redline #35: every contact form gets the sage panel, not just the pages that
   happened to use the boxed variant. */
.contact-section .contact-right {
  background: rgba(var(--light-sage-rgb), 0.57);
  border-radius: var(--panel-radius);
  padding: 28px 23px;
}

/* Redline #37: the blog index cards had their text hard against the edge. */
.blog-grid .card-link { padding: 0 0 4px; }
.blog-grid .card-meta,
.blog-grid h3,
.blog-grid .read-more { padding-right: 18px; }

@media (max-width: 1023px) {
  .featured-wrap { padding: 20px 18px 24px; }
  .featured-posts .card-body { padding: 14px 4px 6px 0; }
}

/* --- Header legibility over photo heroes (redline #38) --------------------
   Jon: "The page buttons are very hard to read on this page. Let's make these
   be a dark green so they can be read."

   Measured behind the nav on the three location heroes: Knoxville composites
   to rgb(188,208,193), where Light Sage is 1.12:1 -- effectively invisible,
   exactly as reported. But per-page ink cannot be the answer: Deep Forest is
   7.54:1 on Knoxville and 1.73:1 on Chattanooga, and Huntsville's sky fails
   both ways at 2.33 and 3.61. The photograph decides, and photographs change.

   A scrim under the header fixes all three at once and keeps one header
   treatment across the site: Knoxville 5.0:1, Huntsville 7.0:1, Chattanooga
   9.0:1, all with the sage that is already there. The blog posts keep their
   dark ink instead -- they open on a light ground with no photograph. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(11, 26, 17, 0.86) 0%, rgba(11, 26, 17, 0.78) 62%, rgba(11, 26, 17, 0) 100%);
}
.site-header.is-dark-ink::before { display: none; }
@media (max-width: 900px) {
  /* The header already paints solid dark green at this width. */
  .site-header:not(.is-dark-ink)::before { display: none; }
}

/* Redline #23: the hero call to action picks up the copper the nav links use
   on hover, rather than only lightening its fill. */
.btn-outline-light:hover { background: var(--white); color: var(--sienna-clay); }

/* --- Footer: tagline and locations (redlines #31, #32) -------------------
   #31 moves Veteran-Owned under the social links. #32 gives each market its
   own small map with the location page linked beneath it, and widens that
   column to hold them. */
.footer-grid { grid-template-columns: 1.15fr 0.85fr 2fr; }
@media (max-width: 1023px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-tagline { margin-top: 22px; }
.footer-maps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.footer-map iframe {
  display: block; width: 100%; height: 132px; border: 0; border-radius: 6px;
  filter: saturate(0.85);
}
.footer-map a {
  display: block; margin-top: 9px; font-size: 0.85rem; font-weight: 700;
  color: var(--sage); text-decoration: underline; text-underline-offset: 3px;
}
.footer-map a:hover { color: var(--white); }
@media (max-width: 1023px) {
  .footer-col--locations { grid-column: 1 / -1; }
  .footer-maps { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 640px) { .footer-maps { grid-template-columns: 1fr; } }

/* -------------------------------------------------------------------------
   MOBILE TAP TARGETS (2026-09-18)

   The mobile scan flags interactive elements under 40px in either dimension.
   These are the standalone controls it found -- each is a link you have to aim
   at rather than a word inside a sentence. Given a 40px minimum by growing the
   hit area, not the type, so nothing above 1024 moves and no text resizes.

   Deliberately NOT included: links inside body prose
   (.sqs-html-content p a, 178x22). Those are inline links in a running
   sentence, which the WCAG target-size rule exempts, and padding them would
   break the line box of the paragraph they sit in.
   ------------------------------------------------------------------------- */
@media (max-width: 1023px) {
  /* 38x38 circles, 2px under the threshold. */
  .footer-social a { width: 40px; height: 40px; }
  /* Standalone links: keep the type size, grow the box. */
  .footer-map a,
  .footer-phone a,
  .back-to-blog,
  .review-more {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }
  /* Full-width standalone links in the post/grid column. */
  .sq-grid > .fe > a { display: flex; align-items: center; min-height: 40px; }
}

/* =========================================================================
   Phone palette -- live's own colours below 768px. Jon, 2026-09-28: on a phone
   the brand palette read heavy ("the live mobile site has a much lighter
   feel", the greens "do not match at all"), while desktop was approved as it
   stands. So desktop keeps the brand palette and phones take live's values.
   Overriding the brand tokens re-resolves every role token built on them
   (--dark-green, --sage, --cream ...), and the -rgb channels carry the change
   into the translucent rgba() uses. Sienna Brown is deliberately NOT
   overridden: the footer stays the orange-brown Jon chose on 2026-09-10.
   Deep Forest is NOT overridden either: Jon, later the same day, found live's
   #003417 too heavy on a phone and chose desktop's #1F3B29.
   ========================================================================= */
@media (max-width: 767px) {
  :root {
    --pine:        #437638;   --pine-rgb: 67, 118, 56;            /* live mid green  */
    --light-sage:  #DFECCD;   --light-sage-rgb: 223, 236, 205;    /* live sage       */
    --mist:        #F9FEF5;   --mist-rgb: 249, 254, 245;          /* live light ground */
    --cream-scrim: #FBFDF8;   --cream-scrim-rgb: 251, 253, 248;   /* live scrim      */
    /* --rust is no longer overridden here (#134 / #135, Jon 2026-10-02):
       live's #492411 read as brown against the Sienna Brown footer. Phones
       now use the desktop rust, so every orange block matches the footer. */
  }
  .site-footer { background: var(--sienna-brown); }
}

/* Phone-only overrides a page can opt into through inline custom properties
   (inline --scrim beats any stylesheet rule, so the phone value needs its own
   name). Used by the Knoxville hero, redline #82. */
@media (max-width: 767px) {
  .sq-photo::before { background: var(--scrim-color-m, var(--scrim-color));
                      opacity: var(--scrim-m, var(--scrim)); }
}
/* Redline #76's green underline under "Projects Portfolio" is phone-only;
   desktop stays as approved. */
@media (min-width: 768px) {
  .projects-hero-h.rule-under { padding-bottom: 0; }
  .projects-hero-h.rule-under::after { content: none; }
}

/* =========================================================================
   Phone layout, homepage -- Jon, 2026-09-28: "use more of the white background
   on mobile ... the live mobile site uses a white background and the modules
   work around it." Measured against live at 430px (tools/mobile_compare.py).
   Every rule is phone-only; desktop renders identically to before.
   ========================================================================= */
@media (max-width: 767px) {
  /* Hero: live runs ~320px with a three-line title, ours was 637px/five lines.
     Section 2 continues the same topo artwork, so its offset tracks the hero's
     new fixed height (was -637px against a 637px hero). */
  .theme-hero.is-tall { min-height: 330px; padding: 104px 0 52px; }
  .theme-hero.is-tall h1 { font-size: min(27px, 6.2vw); line-height: 1.12; }
  .theme-hero.is-tall .btn { font-size: 17px; min-height: 52px; }
  .values-section { background-position: 50% -330px; }

  /* Section headings down to live's phone scale. */
  .forging-title .forging-l1 { font-size: 23px; }
  .forging-title .forging-l2 { font-size: 32px; padding-bottom: 20px; }
  .values-section .marker-intro { font-size: 16px; line-height: 1.4; }
  .csd-section .csd-frame .h-xxl { font-size: 36px; }
  .services-section .services-title .services-l1,
  .services-section .services-title .services-l2 { font-size: 21px; line-height: 1.1; }
  .apart-section .apart-title { font-size: 30px; }

  /* Value cards: live's order, left-aligned, mid-green copy. */
  .values-section .value-card { text-align: left; padding: 24px 22px; }
  .values-section .value-card h4 { font-size: 19px; min-height: 0; display: block;
                                   text-align: left; }
  .values-section .value-card p { text-align: left; color: var(--mid-green); margin-top: 12px; }
  .values-section .value-card--3 { order: 1; }
  .values-section .value-card--2 { order: 2; }
  .values-section .value-card--1 { order: 3; }
  .services-section .service-card p { color: var(--mid-green); }

  /* Civil Site Design panels: live's dark-green glass with white copy. */
  .csd-section .csd-panel { background: rgba(var(--deep-forest-rgb), 0.82); color: var(--white); }

  /* Featured posts: three stacked 376px cards made this photo band 2250px.
     A swipeable row, like live's carousel, brings it down to one card's height. */
  .featured-section .featured-posts { display: grid; grid-auto-flow: column;
    grid-template-columns: none; grid-auto-columns: 78%; gap: 14px;
    overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 18px;
    -webkit-overflow-scrolling: touch; }
  .featured-section .featured-posts > * { scroll-snap-align: start; }

  /* Contact: live's phone layout has no map between the copy and the form. */
  .contact-section .contact-left .map-embed { display: none; }
  .contact-section .contact-copy { margin-top: 28px; }
}

/* Phone layout, About -- live gives each person a short full-bleed map strip
   with a rounded headshot overlapping it (~340px). Ours stacked the full map
   and a full-width photo, ~810px of imagery per person. */
@media (max-width: 767px) {
  .ab-bio .ab-media { height: 340px; width: 100vw; margin-left: calc(50% - 50vw); }
  .ab-bio .ab-back, .ab-bio .ab-media::after {
    position: absolute; left: 0; top: 0; width: 100%; height: 270px; }
  .ab-bio .ab-media::after { display: block; }
  .ab-bio .ab-shot { position: absolute; left: 50%; top: 70px; margin: 0;
    width: min(66%, 290px); height: 270px; transform: translateX(-50%);
    border-radius: 20px; }
}
/* Phone layout, Projects -- live centres "Projects Portfolio" on one line. */
@media (max-width: 767px) {
  .projects-hero .projects-hero-h { font-size: min(31px, 7.2vw); text-align: center; }
}

/* Phone header -- Jon, 2026-09-28: the solid dark-green bar "clashes" with the
   green topo under it. Live's phone header has no ground at all: logo and menu
   sit on the hero and scroll away with it (ours is already position:absolute,
   so it scrolls). Below 768px: no bar, and no dark gradient either, except on
   the location pages, which open on a photograph and need that gradient for
   the sage links (5.0-9.0:1, see the ::before note above). Blog posts keep
   their dark-ink header, which was already transparent. The open menu drawer
   paints its own ground, so it is unaffected. */
@media (max-width: 767px) {
  .site-header { background: transparent; }
  .site-header::before { display: none; }
  body:has(#main > .sq-photo:first-child) .site-header:not(.is-dark-ink)::before { display: block; }
}

/* -------------------------------------------------------------------------
   STAFF REVIEW 2026-09-30, GROUP B -- legibility and alignment.
   Redline #93-#100. Each rule below overrides an earlier measured value on
   purpose; the earlier rule and its comment stay in place as the record of
   what live does.
   ------------------------------------------------------------------------- */

/* #93 (Abby). The value cards were sage at 0.76 over the mist topo, and the
   contour lines ran through the copy. Same colour, fully opaque. */
.values-section .value-card { background: rgb(198, 219, 206); }

/* #94 (Abby). White copy straight on the due-diligence aerial, under a 0.32
   scrim. It now sits on a Deep Forest card, as the cards above it do.
   Background photo itself is still Jon's open call on #53. */
.svc-due-foot { background: rgba(0, 52, 23, 0.84); border-radius: var(--panel-radius);
                padding: 24px 40px; }

/* #95 (EM). Homepage contact band: the headline block was max-width 520px and
   pinned left in a 586px column, so it centred at x=303 while the copy, the
   "Contact us today" line and the map centred at x=336. Centre the block. */
.contact-line { margin-left: auto; margin-right: auto; }
/* Boxed variant (the six other form pages): the headline is centred in its
   rust card but the copy and "Contact us today" were left-aligned in theirs. */
.contact-boxed .contact-copy, .contact-boxed .contact-today { text-align: center; }

/* #96 (EM). Footer: logo, phone, social row and tagline each sat on the left
   edge at different widths, so the 232px logo centred 21px right of the
   number. Shrink the column to its widest item and centre everything on it. */
.footer-grid > .footer-col:first-child { display: flex; flex-direction: column;
  align-items: center; text-align: center; justify-self: start; width: max-content;
  max-width: 100%; }
.footer-grid > .footer-col:first-child img { margin-left: 0; }

/* #97 (EM). Homepage Project Management: title, lead and copy started at three
   different x (883 / 896 / 910) and the block began 52px below the photo top.
   One left edge, one width, and the text block centred on the photo. */
@media (min-width: 1024px) {
  .pm-text { align-self: center; }
  .pm-title, .pm-lead, .pm-copy { margin-left: 61px; width: 416px; }
  .pm-title { margin-top: 0; }
  .pm-copy { margin-top: 40px; }
}

/* #99 (EM). Services: "Our Civil Site Design services encompass a wide range
   of specialties" read as off-axis left-aligned over a staggered grid. */
.svc-design-h { text-align: center; }

/* #100 (EM). Services Project Management photo: a 2500x1656 frame shown at
   837x380 crops from the middle, which cut the two engineers off at the
   hard hats and kept a band of bare dirt. Bias the crop upward. */
.svc-pm-media img { object-position: 50% 14%; }

/* -------------------------------------------------------------------------
   HOMEPAGE REORDER (Jon, 2026-10-02), redline #101-#102.
   Selectors that read `.csd-section .featured-posts` were renamed to
   `.featured-section .featured-posts`, because the rail left that section.
   ------------------------------------------------------------------------- */
/* #101 The services grid now sits on the Civil Site Design photograph. */
.csd-section .csd-services { padding: 96px 0 0; }
.csd-section .csd-services .services-title { color: var(--white); }
/* On the photo the cards need the same near-opaque sage the copy panels above
   them use (0.86), not the 0.53 they ran on the cream topo. */
.csd-section .csd-services .service-card { background: rgba(var(--light-sage-rgb), 0.92); }
.csd-section .csd-services .service-card h4,
.csd-section .csd-services .service-card p { color: var(--dark-green); }

/* #102 The Featured rail on its own cream topo band, centred. Off the
   photograph the label no longer needs its white-plus-halo treatment. */
.featured-section { padding: 72px 0; }
.featured-section .featured-wrap { margin: 0 auto; padding: 0; width: max-content; max-width: 100%; }
.featured-section .featured-wrap .featured-label { color: var(--dark-green); text-shadow: none;
  font-size: 1.1rem; }
.featured-section .featured-posts { margin: 14px 0 0; padding: 0; }
@media (max-width: 1023px) {
  .featured-section { padding: 48px 0; }
  .featured-section .featured-wrap { width: auto; }
}

/* #101 follow-up: the grid's four 275px columns are narrower than the box, so
   it sat ~50px left of centre once it moved here. */
.csd-section .csd-services .services-grid { justify-content: center; }
/* #104 (EB): a route from the homepage grid to the full services page. */
.csd-services-cta { margin-top: 48px; text-align: center; }

/* #103 About FAQ, image removed and the block centred (columns set in
   about_page.py). The section no longer has to stand as tall as the photo. */
.ab-faq-sec > .sq-grid { min-height: 0; padding-bottom: 72px; }
.ab-faq-h { text-align: center; }
.ab-badges { justify-content: center; }
.ab-member { text-align: center; }

/* #105 Footer membership badges, under the tagline on the centred brand
   column. The BBB seal is dark lettering on a transparent PNG, so it gets the
   same Mist chip it carries on the About page. */
.footer-badges { display: flex; align-items: center; justify-content: center;
                 gap: 14px; margin-top: 22px; }
.footer-badges img { width: auto; height: 64px; margin: 0; }
.footer-badges .footer-bbb { display: block; line-height: 0; padding: 4px 6px;
                             border-radius: 8px; background: var(--mist); }
.footer-badges .footer-bbb img { height: 46px; }

/* -------------------------------------------------------------------------
   JON'S HOMEPAGE AND ABOUT REDLINES, 2026-10-02 (#106-#114)
   ------------------------------------------------------------------------- */
/* #106-#109 About: photo-to-photo gaps ran 163 / 128 / 273px because each bio
   carried live's measured min-height (--sh). Height now comes from content
   plus one 56px padding top and bottom, so every module sits 112px from the
   next. */
.ab-bio > .sq-grid { min-height: 0; padding-top: 56px; padding-bottom: 56px; }

/* #112 Footer: States We Serve centred between the brand column and Our
   Locations (it sat 64px right of that midpoint). The middle track takes the
   free space and the column centres in it; Our Locations keeps its width. */
@media (min-width: 1024px) {
  .footer-grid { grid-template-columns: max-content minmax(0, 1fr) minmax(0, 47%); }
  /* min(): between 1024 and ~1180 the free track is narrower than 266px. */
  .footer-grid > .footer-col:nth-child(2) { justify-self: center; width: min(266px, 100%); }
}

/* #113 / #114 A light Deep Forest panel behind both display headings on the
   Civil Site Design photograph, matched to each other. */
.csd-section .csd-frame .h-xxl,
.csd-section .csd-services .services-title {
  display: block; width: fit-content; max-width: 100%; margin-left: auto; margin-right: auto;
  /* #120 (Jon, 2026-10-02): no tint -- live's frosted blur instead, measured
     on live's "Civil Site Design" block: backdrop-filter blur(3px), no
     background, 25px radius. */
  background: none; -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  border-radius: 25px; }
.csd-section .csd-frame .h-xxl { padding: 14px 40px; }
.csd-section .csd-services .services-title { padding: 22px 44px 46px; }
.csd-section .csd-services .services-title.rule-under::after { bottom: 18px; }
@media (max-width: 1023px) {
  .csd-section .csd-frame .h-xxl { padding: 10px 20px; }
  .csd-section .csd-services .services-title { padding: 16px 20px 34px; }
  .csd-section .csd-services .services-title.rule-under::after { bottom: 12px; }
}

/* #115 One topo painting under Featured, Reviews and Contact. The sections
   drop their own image and ground and keep only their scrim (::before), so
   the lines are continuous across both boundaries. Same artwork the cream
   sections already used; the reviews band turns it green with a 0.88 Deep
   Forest overlay (set inline from build_site.py) instead of the dark file. */
.topo-run { background: var(--cream) var(--topo-mist) center top / cover no-repeat; }
.topo-run > section { background-image: none; background-color: transparent; }

/* #116 Homepage contact copy on a sage panel matching the form (0.57), and
   centred between the headline and the map: 48px above and below. */
.contact-copy-panel { width: 439px; max-width: 100%; margin: 48px auto 0;
  padding: 24px 28px; border-radius: var(--panel-radius);
  background: rgba(var(--light-sage-rgb), 0.57); }
.contact-copy-panel .contact-copy { margin: 0 auto; width: auto; }
.contact-copy-panel + .map-embed, .contact-left .contact-copy-panel ~ .map-embed { margin-top: 48px; }
@media (max-width: 767px) {
  .contact-copy-panel { margin-top: 28px; padding: 20px 18px; }
  .contact-section .contact-copy-panel .contact-copy { margin-top: 0; }
}

/* #117 (Jon, 2026-10-02): blog index cards. The text ran flush against the
   card's left edge (0px inset, 18px on the right from #37). The grid now fills
   the container instead of stopping at 1302px, and the text gets 24px on both
   sides and below. */
.blog-grid { max-width: none; }
.blog-grid .card-link { padding: 0 0 24px; }
.blog-grid .card-meta,
.blog-grid h3,
.blog-grid .read-more { padding-left: 24px; padding-right: 24px; }
@media (max-width: 767px) {
  .blog-grid .card-meta, .blog-grid h3, .blog-grid .read-more { padding-left: 18px; padding-right: 18px; }
  .blog-grid .card-link { padding-bottom: 18px; }
}

/* #121-#125 (Jon, 2026-10-02): the About page's membership badges, moved to
   the homepage reviews band, centred above the reviews. Same sizes as on
   About; the grid placement there becomes a centred column here. */
.reviews-members { display: flex; flex-direction: column; align-items: center;
                   gap: 24px; margin: 56px auto; color: var(--white); }
.reviews-members .ab-member { width: 330px; max-width: 100%; text-align: center; }
.reviews-members .ab-badges { justify-content: center; }
@media (max-width: 767px) {
  .reviews-members { margin: 28px auto 36px; padding: 0 16px; }
  .reviews-members { gap: 16px; }
  .reviews-members .ab-member { width: 260px; }
  .reviews-members .ab-badges { flex-wrap: wrap; align-items: center; gap: 12px 16px; }
  .reviews-members .ab-badges > img { width: 104px; }
  .reviews-members .ab-bbb img { width: 120px; }
}

/* #128 (Jon, 2026-10-02): the badge row is now four flat items -- Working
   Parents, the Chamber's member widget, BBB, the 2026 Small Business Awards
   contender -- with no chamber lettering. The widget prints the member name
   and "Proud Member of" above its logo; the heading already says that, so
   both lines are hidden and only the linked logo shows. Its logo is a JPG on
   white, so it sits on a white card the way the BBB seal sits on Mist. */
.reviews-members .ab-badges { align-items: center; gap: 20px; }
.hmcc-member { line-height: 0; }
.hmcc-member .mn-widget-member-name,
.hmcc-member .mn-widget-member-text { display: none; }
.hmcc-member:has(.mn-widget-member) .hmcc-fallback { display: none; }
.hmcc-member .mn-widget-member-link { display: block; padding: 8px; border-radius: 10px;
                                      background: var(--white); }
.hmcc-member .mn-widget-member-logo { display: block; width: 150px; height: auto; }
.hmcc-fallback img { width: 160px; height: auto; }
.ab-badges .sba-badge { width: 166px; height: auto; border-radius: 10px; }
@media (max-width: 767px) {
  .hmcc-member .mn-widget-member-logo { width: 96px; }
  .hmcc-fallback img { width: 104px; }
  .ab-badges .sba-badge { width: 120px; }
}

/* #127 (Jon, 2026-10-02): the band opened 65px above the badges (9px padding
   + 56px margin) but closed 9px under the review cards. Match the bottom. */
.reviews-members ~ .reviews-rail { margin-bottom: 56px; }
@media (max-width: 767px) { .reviews-members ~ .reviews-rail { margin-bottom: 28px; } }

/* =========================================================================
   /services/ v2 (Jon, 2026-10-02, redline #53). Claude Design concept C,
   restyled to the site and approved on the canvas: photo hero, three
   alternating photo bands (Site Design on mist topo, Project Management on
   dark topo with Sienna Brown cards, Due Diligence on sage), Additional
   Services as pills on the dark topo, then the boxed contact band.
   The old measured layout's .svc-* rules above are left in place, unused.
   ========================================================================= */
.svc2-wrap { max-width: var(--max-width); margin: 0 auto; padding: 0 var(--page-padding); box-sizing: border-box; }
.svc2-hero { background-color: var(--deep-forest);
  background-image: linear-gradient(180deg, rgba(var(--deep-forest-rgb), 0.78) 0%, rgba(var(--deep-forest-rgb), 0.35) 38%, rgba(var(--deep-forest-rgb), 0.92) 100%), url('../images/civil-site-design-services-hero.jpeg');
  background-size: cover; background-position: center 45%; }
.svc2-hero .svc2-wrap { padding-top: 230px; padding-bottom: 72px; }
.svc2-hero h1 { margin: 0; font-family: var(--heading-font); font-weight: 900;
  font-size: clamp(48px, 7vw, 104px); line-height: 1; text-transform: uppercase; color: var(--white); }
.svc2-hero-row { margin-top: 32px; display: flex; justify-content: space-between; align-items: flex-end; gap: 32px; flex-wrap: wrap; }
.svc2-hero-row p { margin: 0; max-width: 56ch; font-size: 17px; line-height: 1.35; color: var(--sage); }
.svc2-jump { display: flex; gap: 12px; flex-wrap: wrap; }
.svc2-pill { display: inline-block; padding: 12px 26px; border: 1px solid var(--sage); border-radius: var(--pill);
  font-family: var(--heading-font); font-weight: 800; font-size: 15px; text-transform: uppercase;
  color: var(--sage); text-decoration: none; }
.svc2-pill:hover { background: var(--sage); color: var(--dark-green); }

.svc2-mist { background-color: var(--cream);
  background-image: linear-gradient(rgba(var(--cream-scrim-rgb), 0.62), rgba(var(--cream-scrim-rgb), 0.62)), var(--topo-mist);
  background-size: cover; background-position: center; }
.svc2-dark { background-color: var(--deep-forest);
  background-image: linear-gradient(rgba(var(--deep-forest-rgb), 0.55), rgba(var(--deep-forest-rgb), 0.55)), var(--topo-dark);
  background-size: cover; background-position: center; }
.svc2-sage { background-color: var(--sage);
  background-image: linear-gradient(rgba(var(--light-sage-rgb), 0.86), rgba(var(--light-sage-rgb), 0.86)), var(--topo-mist);
  background-size: cover; background-position: center; }

.svc2-band { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-height: 660px; scroll-margin-top: 90px; }
.svc2-text { padding: 96px 64px; display: flex; flex-direction: column; justify-content: center; }
.svc2-text--l { padding-left: max(var(--page-padding), calc((100% - var(--max-width)) / 2 + var(--page-padding))); }
.svc2-text--r { padding-right: max(var(--page-padding), calc((100% - var(--max-width)) / 2 + var(--page-padding))); }
.svc2-media { position: relative; min-height: 420px; }
.svc2-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.svc2-h { margin: 0; font-family: var(--heading-font); font-weight: 900; font-size: 50px; line-height: 1.02;
  text-transform: uppercase; color: var(--dark-green); }
.svc2-h.rule-under::after { left: 0; transform: none; width: 62%; }
.svc2-lead { margin: 22px 0 28px; max-width: 54ch; font-size: 17px; line-height: 1.3; color: var(--orange); }
.svc2-dark .svc2-h { color: var(--white); }
.svc2-dark .svc2-lead { color: var(--sage); }
.svc2-sage .svc2-lead { color: var(--dark-green); }

.svc2-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.svc2-card { padding: 20px 22px; background: rgba(var(--light-sage-rgb), 0.76); border-radius: var(--panel-radius); }
.svc2-card h3 { margin: 0; font-family: var(--heading-font); font-weight: 900; font-size: 16px; line-height: 1.1;
  text-transform: uppercase; color: var(--dark-green); }
.svc2-card p { margin: 8px 0 0; font-size: 14px; line-height: 1.3; color: var(--dark-green); }
.svc2-cards--rust .svc2-card { background: var(--rust); }
.svc2-cards--rust .svc2-card h3 { color: var(--sage); }
.svc2-cards--rust .svc2-card p { color: var(--white); }
.svc2-cards--mist .svc2-card { background: rgba(var(--mist-rgb), 0.9); }

.svc2-extra .svc2-wrap { padding-top: 64px; padding-bottom: 64px; text-align: center; }
.svc2-extra h2 { margin: 0; font-family: var(--heading-font); font-weight: 900; font-size: 44px; line-height: 1.05;
  text-transform: uppercase; color: var(--white); }
.svc2-chips { margin-top: 28px; display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.svc2-chip { padding: 12px 24px; border: 1px solid var(--sage); border-radius: var(--pill);
  font-family: var(--heading-font); font-weight: 800; font-size: 15px; text-transform: uppercase; color: var(--sage); }

@media (max-width: 900px) {
  .svc2-band { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .svc2-band .svc2-media { min-height: 280px; order: -1; }
  .svc2-text, .svc2-text--l, .svc2-text--r { padding: 56px 6vw; }
  .svc2-cards { grid-template-columns: minmax(0, 1fr); }
  .svc2-h { font-size: 36px; }
  .svc2-extra h2 { font-size: 32px; }
}
@media (max-width: 767px) {
  .svc2-hero .svc2-wrap { padding-top: 130px; padding-bottom: 48px; }
  .svc2-hero h1 { font-size: min(44px, 11vw); }
  .svc2-pill, .svc2-chip { font-size: 13px; padding: 10px 18px; min-height: 40px; box-sizing: border-box; }
}

/* #132 (Jon, 2026-10-02): What Sets Us Apart showed 104px of orange above the
   title (the generic divider rule, 104px + the 86px wave overlap, outranked
   .apart-section's own 56px) and 58px below the tiles. Both are now 56px of
   visible orange: the wave overlap plus 56 on top, 38 + the tiles' own 18px
   of trailing space at the bottom. */
.section-divider + .apart-section { padding-top: calc(56px + var(--divider-height)); }
.apart-section { padding-bottom: 38px; }
@media (max-width: 1023px) {
  .section-divider + .apart-section { padding-top: calc(48px + var(--divider-height)); }
  .apart-section { padding-bottom: 30px; }
}

/* #132 follow-up (Jon, 2026-10-02: "match the location pages too"): the
   location-page copy closed 32px under its note against 56px above the H2.
   Last track 32 -> 56 so top and bottom match the homepage. */
.loc-apart > .sq-grid {
  grid-template-rows: 163px minmax(165px, auto) 32px minmax(150px, auto)
                      48px minmax(40px, auto) 56px; }

/* #133 (Jon, 2026-10-02): on phones the brand column (logo, phone, social,
   tagline, badges) sat at the left edge. Centred on the page; desktop is
   unchanged. */
@media (max-width: 767px) {
  .footer-grid > .footer-col:first-child { justify-self: center; }
  .footer-social { justify-content: center; }
}
@media (max-width: 767px) {
  /* #132 follow-up, phones: 56px padding + the H2's 56px margin put 112px
     above the heading. 48 / 48, the same as the homepage on a phone. */
  .loc-apart > .sq-grid { padding-top: 48px; padding-bottom: 48px; }
  .loc-apart-h { margin-top: 0; }
}

/* -------------------------------------------------------------------------
   Jon, 2026-10-02: "make all of the headers on all pages except the Location
   pages match the home page with the green topo with the angle."
   Same mechanism as the homepage (.section-divider--zigzag, redline #22): the
   section after the hero is pulled up under it by the strip height, given
   that height back as padding, and clipped to the chevron, so the triangles
   are the next section's own ground and topo. The hero gets the same height
   added at the bottom so nothing in it moves or gets covered.
   Those next sections carry their own top padding on an inner grid (0 on the
   section itself) except the boxed contact band (66px), hence --pt0.
   Careers and Meet the Team already used the homepage divider.
   ------------------------------------------------------------------------- */
.hero-angle + section {
  --pt0: 0px;
  position: relative; z-index: 1;
  margin-top: calc(-1 * var(--divider-height));
  padding-top: calc(var(--pt0) + var(--divider-height));
  clip-path: polygon(0 0, 50% var(--divider-height), 100% 0, 100% 100%, 0 100%);
}
.hero-angle + .contact-boxed { --pt0: 66px; }
@media (max-width: 1023px) { .hero-angle + .contact-boxed { --pt0: 56px; } }

/* -------------------------------------------------------------------------
   Jon, 2026-10-02: "The services page still [has] the image instead of the
   topo at the header. Also, I want all of the pages to match the same height
   of the header as the home page."
   Every non-location header (.hero-match) takes the homepage hero's box:
   637px at desktop, 118.562px above (clears the nav), 86.4px below (the
   chevron), content centred in what is left. Each layout's own measured
   min-heights and grid tracks are released so the section sets the height.
   Phones: homepage is 394px at 390 (104 / 52 padding). Careers and Services
   carry a paragraph and buttons that cannot fit 238px without clipping, so
   394 is a floor on phones, not a cap.
   ------------------------------------------------------------------------- */
section.hero-match.hero-match {
  box-sizing: border-box; min-height: 637px; height: auto;
  padding: 118.562px 0 86.4px;
  display: flex; flex-direction: column; justify-content: center;
  background-color: var(--dark-green); background-image: var(--topo-dark);
  background-size: cover; background-position: center;
}
section.hero-match.hero-match > .sq-grid,
section.hero-match.hero-match > .container,
section.hero-match.hero-match > .svc2-wrap {
  width: 100%; min-height: 0; padding-top: 0; padding-bottom: 0;
  grid-template-rows: none; row-gap: 24px; }
section.hero-match.hero-match > .sq-grid > * { grid-row: auto; }
/* About carried 561px of heading in a 781px hero. Same lines, same
   proportions, at 76% of the width so the block fits the 432px the box
   leaves. */
.hero-match.ab-hero .ab-hero-h { width: 76%; justify-self: center; }
.hero-match.ab-hero .ab-hero-sub,
.hero-match.ab-hero .ab-hero-states { width: 76%; justify-self: center; }
@media (max-width: 767px) {
  section.hero-match.hero-match { min-height: 394px; padding: 104px 0 52px; }
  section.hero-match.hero-match > .sq-grid,
  section.hero-match.hero-match > .container { row-gap: 16px; }
  .hero-match.ab-hero .ab-hero-h,
  .hero-match.ab-hero .ab-hero-sub,
  .hero-match.ab-hero .ab-hero-states { width: 100%; }
}
/* Scaled-text rounding left the About subhead 4px wider than its box. */
.hero-match.ab-hero .ab-hero-sub > .hl { font-size: calc(var(--k) * 0.99cqw); }
/* 404 on phones: 278px of copy in a 238px slot. Smaller heading and tighter
   gaps so it holds the homepage's 394. */
@media (max-width: 767px) {
  .hero-match.error-page .eyebrow { margin: 0; }
  .hero-match.error-page .h-xxl { font-size: 34px; margin: 4px 0 0; }
  .hero-match.error-page .lede { margin-top: 12px !important; font-size: 15px; }
  .hero-match.error-page .container > div { margin-top: 16px !important; }
}
