/* ============================================================
   SOAR v2 — REDESIGN ("Field Operations")
   Load order: elevated.css (brand tokens) -> fs.css
   ------------------------------------------------------------
   ONE design system, applied uniformly:
   - One typeface: Helvetica Neue / Helvetica / Arial (everything,
     incl. eyebrows + stats). Hierarchy by size + weight + case.
   - One spacing scale (--sp*), one radius scale, one card style
     (.card), one button system (.btn / .btn--primary / .btn--ghost),
     one eyebrow label (.eyebrow), one section rhythm (.sec + .band).
   - Brand: navy anchors, dawn gold = ONE key figure / CTA per band.
   - Cinematic dark hero (video) -> light paper editorial bands ->
     navy color-block proof band -> dark project band -> light
     founders -> dark CTA. Alternation gives clear section rhythm
     so the page no longer reads "all one flat blue".
   ============================================================ */

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--c-bg);color:var(--c-ink);
  font-family:var(--font-body);font-size:17px;line-height:var(--leading-body);
  font-variation-settings:"wdth" var(--wd-body);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}

/* ── the width axis, applied ────────────────────────────────────────────────
   ONE family, three widths. Display runs expanded so large type has presence;
   micro-labels run condensed, uppercase and tracked so they read as a
   different REGISTER without being a different typeface. This is how the
   one-family rule survives while the page stops looking generic. */
h1,h2,h3,.h2,.hero h1,.rcard__t,.mtile__n,.ins__t,.growth__big,.step h3,.vrow h4,.faq__q{
  font-variation-settings:"wdth" var(--wd-display)}
.rcard__k,.ins__meta,.mlabel,.cap-lab,.foot__badge{
  font-variation-settings:"wdth" var(--wd-label)}
/* the chip styling belongs to the footer badge ALONE — last time this was a
   substring match into the grouped selector above, which bordered every
   category label on the page */
.foot__badge{font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:#C9D3E0;border:1px solid var(--ink-line);border-radius:var(--radius);
  padding:7px 12px}
img,video,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
::selection{background:rgba(232,84,42,.28)}

:root{
  /* dark cinematic field tokens */
  --ink:        var(--c-primary-strong);
  --ink-2:      #101D30;
  --ink-line:   rgba(255,255,255,.14);
  --ink-line-2: rgba(255,255,255,.08);
  --on-ink:     #EAF0F7;
  --on-ink-dim: #9DB0C6;

  --gold:       var(--c-accent);
  --gold-soft:  var(--c-accent);
  --green:      var(--c-accent);  /* green is out of the UI */

  /* layout */
  --maxw: 1280px;
  --pad:  clamp(20px,5vw,64px);

  /* ONE spacing scale */
  --sp-1: 8px; --sp-2: 16px; --sp-3: 24px; --sp-4: 32px;
  --sp-5: 48px; --sp-6: 64px; --sp-7: 96px;
  --sec-y:  clamp(64px,6.4vw,96px);
  --sec-yt: clamp(44px,4.4vw,64px);

  /* ONE radius scale */
  /* radius comes from the theme token now — see elevated.css */
  --r-1: var(--radius-sm); --r-2: var(--radius); --r-3: var(--radius-lg); --r-pill: 999px;

  /* ONE type scale (size/weight only — single family) */
  --t-eyebrow: 12px;  --track-eyebrow: .12em;
  --t-display: clamp(46px,7vw,104px);
  --t-h2:      clamp(30px,3.6vw,52px);
  --t-h3:      clamp(21px,1.7vw,26px);
  --t-lead:    clamp(17px,1.3vw,21px);
  --track-display: -0.012em;

  --shadow-card: 0 24px 60px -34px rgba(10,27,49,.45);
  --shadow-glass: 0 30px 70px -30px rgba(0,0,0,.55);

  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============================================================
   PRIMITIVES — used identically everywhere
   ============================================================ */
.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--pad);position:relative;z-index:2}
.sec{padding-block:var(--sec-y);position:relative}
.sec--tight{padding-block:var(--sec-yt)}
/* rhythm: consecutive light sections share a ground, so their stacked paddings
   read as one void. The follower gives up half — every gap lands ~140px. */
.band-paper + .band-paper{padding-top:calc(var(--sec-y)/2)}
.sec:has(> .oplap) + .band-paper{padding-top:var(--sec-y)}

/* section colour fields (this is the rhythm engine) */
.band-dark{background:var(--ink);color:var(--on-ink);position:relative;isolation:isolate}

.band-navy{background:var(--c-primary);color:#fff;position:relative;isolation:isolate}
/* seamless wavy contour lines (tiles infinitely -> never cut off), softly
   faded from the top-right with a radial mask so no hard edges show */
.band-navy::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.12;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='150' viewBox='0 0 300 150'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-width='1'%3E%3Cpath d='M0 25 C75 11 225 39 300 25'/%3E%3Cpath d='M0 75 C75 61 225 89 300 75'/%3E%3Cpath d='M0 125 C75 111 225 139 300 125'/%3E%3C/g%3E%3C/svg%3E");
  background-size:300px 150px;
  -webkit-mask-image:radial-gradient(125% 135% at 100% -12%,#000 0,rgba(0,0,0,.35) 46%,transparent 72%);
  mask-image:radial-gradient(125% 135% at 100% -12%,#000 0,rgba(0,0,0,.35) 46%,transparent 72%)}

/* ── dark surfaces: ONE recipe ──────────────────────────────────────────────
   Every dark band is flat brand colour + the same contour texture at the same
   opacity. Only the base VALUE changes between them (navy -> dark -> ink), so
   the steps read as depth instead of as three different treatments. The hero
   scrim is deliberately excluded: it is a legibility gradient over moving
   video, which is a different job from a section surface. */
.band-navy,.band-dark,.band-ink{position:relative;isolation:isolate}
.band-navy{background:var(--c-primary);color:#fff}
.band-dark{background:var(--ink);color:var(--on-ink)}
/* the dark chapter is ONE value. band-dark and band-ink previously differed by
   two points of luminance — invisible, so it was a near-miss rather than a
   decision. The join between process and work is carried by structure (a
   timeline giving way to a horizontal rail), never by colour. */
.band-ink{background:var(--ink);color:var(--on-ink)}
/* dark bands are FLAT. Topo lives only on the statement section. The FAQ is
   sectioned by VALUE instead: one step off-white, no texture. */
.home .sec:has(.faq){background:var(--c-surface-2)}


/* ── topo: light ground only, two deliberate placements ─────────────────────
   Statement (the opening light section) and FAQ (the closing one) — bookends.
   Nowhere else. Zoomed and thick so it reads as contour linework. */
.home .sec:has(.stmt){isolation:isolate}
.home .sec:has(.stmt)::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  /* the field runs PAST the section's bottom edge so its tail dissolves inside
     the next section rather than stopping on the boundary. Light sections are
     transparent, not painted, so the overhang shows through. */
  inset:0 0 -180px 0;
  /* Dave: "way too zoomed in so it looks like tiger stripes instead of
     topography, similar to embers.org". Their contours are fine and widely
     spaced; ours were 240%, which cropped the tile down to a few fat bands.
     100% shows the whole contour field, so the rings read as terrain. Opacity
     nudged back up because thinner lines at the same .020 disappeared. */
  opacity:.026;background-image:url("../assets/topo-tile.svg");
  background-repeat:repeat;background-size:100% auto;background-position:30% 40%;
  /* hold full strength through the body of the section, then ramp out across
     the last third and the overhang. Without this the texture ended on a hard
     horizontal line exactly where the next band began. */
  -webkit-mask-image:linear-gradient(to bottom,#000 0%,#000 58%,rgba(0,0,0,.45) 82%,transparent 100%);
          mask-image:linear-gradient(to bottom,#000 0%,#000 58%,rgba(0,0,0,.45) 82%,transparent 100%)}

/* ── topo on the service pages ──────────────────────────────────────────────
   The contour ground existed only under .home, so every paper section on an
   internal page sat flat. It is the same tile and the same restraint, but
   parameterised: each service page places it on ONE light section and anchors
   it differently, so the texture reads as a property of the ground rather
   than a stamp dropped in the same corner of every page.
   Opacity stays in the .020-.028 band — visible as terrain when you look for
   it, invisible as a pattern when you are reading. */
.svcpage .topo-ground{position:relative;isolation:isolate}
.svcpage .topo-ground::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  /* run past the bottom edge so the tail dissolves into the next band
     instead of ending on the boundary */
  inset:var(--topo-inset, 0 0 -150px 0);
  opacity:var(--topo-o, .023);
  background-image:url("../assets/topo-tile.svg");
  background-repeat:repeat;
  background-size:var(--topo-size, 100% auto);
  background-position:var(--topo-pos, 30% 40%);
  -webkit-mask-image:var(--topo-mask, linear-gradient(to bottom,#000 0%,#000 56%,rgba(0,0,0,.42) 82%,transparent 100%));
          mask-image:var(--topo-mask, linear-gradient(to bottom,#000 0%,#000 56%,rgba(0,0,0,.42) 82%,transparent 100%))}
/* content sits above the ground — the ::before is positioned, so without this
   it would paint over the copy rather than under it */
.svcpage .topo-ground > .wrap{position:relative;z-index:1}

/* ── the topographic field ──────────────────────────────────────────────────
   Round-two note (Dave): "when you scroll down, it's just the basic sections
   that look like a WordPress site from 10 years ago, there's no movement …
   instead of white and scroll down, dark gray, scroll down, dark blue … that's
   so 10 years ago."

   The fix is structural, not decorative. ONE fixed contour field sits behind
   every light section, so a section boundary is no longer a background-colour
   change — the ground is continuous and only the content changes. Light
   sections are therefore transparent, not painted.

   Contours are real marching-squares isolines off a smooth height field, so
   they read as survey terrain rather than as squiggles. SVG, not a raster:
   each band recolours via currentColor and drifts independently, which a
   flat image cannot do. ~26 KB inline, ~12 KB over the wire. */
/* The tile is generated periodic in Y (its height field is built from
   sin/cos terms in 2*pi*n*y/H), so row 0 and row 1000 are identical and it
   repeats down the document with no visible seam. It scrolls with the page —
   an earlier `position:fixed` version was wrong twice over: it did not move
   with the content, and fixed layers do not composite into a full-page
   screenshot, so the texture was invisible in the very captures we send for
   review. ~11 KB, ~5 KB over the wire, cached once, drawn by the compositor. */
/* Light sections are CLEAN. The topo texture is an event, not wallpaper:
   it appears only on chosen dark surfaces, zoomed and bold enough to read as
   contour linework instead of stray hairs. */
body{background-color:var(--c-bg)}
/* Light sections are transparent so the field reads THROUGH them. Painting a
   colour here per section is exactly what produced the band rhythm. */
.band-paper{background:var(--c-bg);color:var(--c-ink)}
.home .band-paper{background:transparent}
/* A second, DEEPER dark. `process` and `work` sit next to each other and our own
   playbook says two neighbours in the same colour read as one dead void — but the
   fix there is not to repaint one of them a different hue, it is to make the
   boundary mean something. `work` drops to near-black and opens with a full-bleed
   rail, so the eye reads "deeper, and the axis just changed" rather than "another
   blue block". A single hairline marks the join. */
.band-ink{background:var(--c-primary-strong);color:var(--on-ink);position:relative;isolation:isolate}
.band-dark + .band-ink{border-top:1px solid rgba(255,255,255,.07)}
/* content sits above the ground */
.sec,.hero,.cta,.site-header,.site-footer,footer{position:relative;z-index:1}

/* REMOVED in v3.01 — the chevron notch divider.
   Dave, reviewing v2.02: "Oh, look, there's. It's not quite straight."
   At a 62% shallow angle it read as a misalignment rather than a decision, and
   angled section dividers are themselves a mid-2010s WordPress idiom, which fed
   the main complaint that the page "looks like a WordPress site from 10 years
   ago." Section separation is now carried by the topographic field, not by a
   cut edge. `.seam` classes left inert in the markup are harmless no-ops. */

/* ── opening statement ─────────────────────────────────────────────────────
   Was a heading, one paragraph, and a link floating in half a screen of empty
   ground. The statement now runs against a supporting column, and the section
   CLOSES on a hairline fact row — the same ruled-data idiom used in Insights and
   in the Saildrone reference, so the page has a repeating structural device
   instead of one-off blocks. It also puts the credentials a government reviewer
   is scanning for above the fold of this section rather than nowhere. */
.stmt{display:grid;grid-template-columns:1.25fr .85fr;gap:clamp(32px,4.5vw,80px);align-items:end}
.stmt__side{display:flex;flex-direction:column;gap:clamp(18px,2vw,26px);align-items:flex-start}
@media(max-width:900px){
  .stmt{grid-template-columns:1fr;align-items:start}
    .fact:nth-child(2){border-right:0}
  .fact:nth-child(-n+2){border-bottom:1px solid var(--c-border);padding-bottom:20px}
}

.home .sec:has(> .wrap > .facts:last-child){padding-bottom:clamp(48px,4.6vw,68px)}


/* ── the growth panel: a contained dark data chart ──────────────────────────
   v3.02. Was `.record` — one oversized figure and three stat rows. Jon asked
   for "a double line graph illustrating state expansion and project
   completions… in the navy box… and replace that data", so the payload is now
   a chart and the panel shell is untouched.

   Still straight from the lookbook: Path Robotics' welder-shortage block —
   chart left, one oversized figure against a vertical accent rule right,
   secondary stats stacked in small caps beneath. The panel keeps giving the
   long light run its mid-point punch without opening another dark band.

   TWO SERIES, ONE CHROMA VOICE. The accent carries the loud line; the second
   series is a NEUTRAL light, not a second hue. Charting the two series in two
   saturated colours is exactly the three-mid-tones mistake the theme note
   describes, and it would put a second voice back on the page. */
.growth{clip-path:polygon(0 0,calc(100% - 44px) 0,100% 44px,100% 100%,0 100%);
  display:grid;grid-template-columns:1.35fr .8fr;gap:clamp(32px,4.4vw,72px);
  align-items:center;background:var(--ink);color:#fff;
  border-radius:var(--radius);position:relative;isolation:isolate;overflow:hidden;
  margin-top:clamp(40px,4.6vw,68px);
  padding:clamp(34px,3.8vw,60px) clamp(30px,3.4vw,56px)}
.growth::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  opacity:.5;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 900 620'%3E%3Cg fill='none' stroke='%231D3A63' stroke-width='1.3'%3E%3Ccircle cx='860' cy='40' r='150'/%3E%3Ccircle cx='860' cy='40' r='260'/%3E%3Ccircle cx='860' cy='40' r='380'/%3E%3Ccircle cx='860' cy='40' r='510'/%3E%3C/g%3E%3C/svg%3E");
  background-size:cover;background-position:top right}
.growth>*{position:relative;z-index:1}
.growth__chart{margin:0}
.growth__head{display:flex;flex-direction:column;gap:10px;margin-bottom:clamp(18px,2vw,28px)}
.growth__eyebrow{font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  font-variation-settings:"wdth" var(--wd-label);color:var(--c-accent)}
.growth__title{font-family:var(--font-display);font-weight:600;
  font-variation-settings:"wdth" var(--wd-display);
  font-size:clamp(21px,2vw,28px);line-height:1.2;letter-spacing:-.01em;color:#fff;margin:0}

/* ── the chart ─────────────────────────────────────────────────────────────
   Plain inline SVG on a fixed 640x348 viewBox, scaled by width. No library:
   seven points per series is not worth 60KB of Chart.js, and a hand-authored
   polyline is the only version whose stroke weights and label sizes obey the
   type scale instead of a plugin's defaults.

   DUAL AXIS ON PURPOSE. Projects run to the high hundreds and counties to 44;
   on one axis the counties line flattens to a dead horizontal and the "double
   line" reads as one line plus a rule. Gridlines are shared, so left 0/200/400/600
   and right 0/20/40/60 land on the same four rules and the panel stays quiet. */
.chart{position:relative}
.chart__svg{display:block;width:100%;height:auto;overflow:visible}
.chart__grid line{stroke:#1D3A63;stroke-width:1}
.chart__tick text{font-family:var(--font-body);font-size:11.5px;font-weight:600;
  letter-spacing:.05em;font-variation-settings:"wdth" var(--wd-label);fill:#7E93AD}
.chart__tick--l text{text-anchor:end}
.chart__tick--r text{text-anchor:start}
.chart__tick--x text{text-anchor:middle}
.chart__tick--x text:first-child{text-anchor:start}
.chart__tick--x text:last-child{text-anchor:end}
.chart__line{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.chart__line--a{stroke:var(--c-accent)}
.chart__line--b{stroke:#8FA6C0;stroke-width:2}
.chart__end circle:first-child{opacity:.22}
.chart__end--a circle{fill:var(--c-accent)}
.chart__end--b circle{fill:#8FA6C0}

.chart__key{list-style:none;margin:clamp(16px,1.8vw,24px) 0 0;padding:0;
  display:flex;flex-wrap:wrap;gap:10px clamp(20px,2.4vw,34px)}
.chart__keyitem{display:flex;align-items:baseline;gap:8px;
  font-size:12.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  font-variation-settings:"wdth" var(--wd-label);color:#C3D0E0;line-height:1.5}
.chart__keyitem::before{content:"";width:14px;height:2.5px;flex:none;
  transform:translateY(-3px);background:currentColor}
.chart__keyitem--a::before{background:var(--c-accent)}
.chart__keyitem--b::before{background:#8FA6C0}
.chart__keyitem span{text-transform:none;letter-spacing:normal;font-weight:400;color:#7E93AD}

/* ── the side: the lead figure, unchanged in kind from .record ──────────── */
.growth__side{display:flex;flex-direction:column;gap:clamp(26px,3vw,40px)}
.growth__lead{border-left:2px solid var(--c-accent);padding-left:clamp(18px,1.8vw,26px)}
.growth__big{font-family:var(--font-display);font-weight:600;
  font-variation-settings:"wdth" var(--wd-display);
  font-size:clamp(64px,6.6vw,104px);line-height:.9;letter-spacing:-.02em;color:#fff}
.growth__unit{display:block;font-size:.16em;font-weight:600;margin-top:.5em;
  letter-spacing:.05em;text-transform:uppercase;
  font-variation-settings:"wdth" var(--wd-label);color:var(--c-accent)}
.growth__lab{margin:16px 0 0;color:#9DB0C6;font-size:15.5px;line-height:1.55;max-width:32ch}
.growth__rows{display:flex;flex-direction:column;gap:clamp(18px,2vw,26px)}
.growth__row{display:grid;grid-template-columns:minmax(84px,auto) 1fr;
  gap:clamp(16px,2vw,26px);align-items:baseline}
.growth__row b{font-family:var(--font-display);font-weight:600;
  font-variation-settings:"wdth" var(--wd-display);
  font-size:clamp(24px,2vw,34px);letter-spacing:-.01em;color:#fff;white-space:nowrap}
.growth__rowunit{font-size:.5em;font-weight:600;margin-left:.12em;color:var(--c-accent)}
.growth__row span{font-size:12.5px;font-weight:600;letter-spacing:.05em;
  text-transform:uppercase;color:#9DB0C6;
  font-variation-settings:"wdth" var(--wd-label);line-height:1.5}

@media(max-width:900px){
  .growth{grid-template-columns:1fr;gap:34px}
  .growth__side{flex-direction:row;flex-wrap:wrap;gap:28px 40px;align-items:flex-start}
  .growth__lead{flex:1 1 240px}
  .growth__rows{flex:1 1 220px}
}
@media(max-width:560px){
  /* the 900 rule's `flex:1 1 240px` is a basis on the MAIN axis — once the
     side stacks, that main axis is vertical, so the basis became a 240px
     HEIGHT and the lead's accent rule ran on well past its own text. */
  .growth__side{flex-direction:column;align-items:stretch}
  .growth__lead,.growth__rows{flex:0 0 auto}
  .chart__tick--x text:nth-child(even){display:none}
}


/* ── services mosaic ────────────────────────────────────────────────────────
   Replaces the six-item icon list + single feature card. That list was
   structurally the same thing Dudek and Rincon do — a column of service links —
   and Dudek/Rincon are the competitors this redesign is supposed to beat.

   The references that read as modern (Anduril's product grid, ESA's project
   mosaic, Saildrone's capability pages) all do the same thing instead: real
   imagery, full-bleed, at DIFFERENT tile sizes so no two rows scan alike. The
   asymmetry is the point — a 3x2 grid of equal cards is the tell. */
.mosaic{
  display:grid;
  grid-template-columns:repeat(12,1fr);
  gap:clamp(12px,1.4vw,20px);  /* identical to the projects rail */
  /* the mosaic is the section's full-width event — wide, inset from the
     viewport edge. Its closing rule (.secfoot) matches THIS width so the pair
     reads as one object; everything else sits on the wrap spine. */
  max-width:min(calc(var(--maxw) + clamp(48px,6vw,120px)), calc(100% - 2*clamp(16px,2.4vw,40px)));
  margin-inline:auto;
  padding:clamp(20px,3vw,38px) 0 0;
}

/* ── section foot: the transition device ────────────────────────────────────
   The mosaic used to stop dead and the next band began after two stacked
   paddings — a void, then a hard edge, with nothing carrying the reader over.
   This strip closes the section with context instead of silence: a hairline,
   one sentence, one exit. It also removes the double gap, because the section
   now ends ON this rule rather than on empty space. */
.secfoot{
  display:grid;grid-template-columns:1fr auto;gap:clamp(20px,3vw,56px);
  align-items:baseline;
  /* text returns to the wrap spine — the heading above the mosaic sits there,
     so the closing line matches it. Only the mosaic itself is the wide event. */
  max-width:var(--maxw);
  margin-inline:auto;
  padding:clamp(26px,2.6vw,38px) var(--pad) 0;
  border-top:1px solid var(--c-border);margin-top:clamp(26px,2.6vw,38px)
}
/* full-bleed, because the mosaic it closes is full-bleed. An inset rule under an
   edge-to-edge grid is the two disagreeing at the exact point they connect. */
.secfoot__t{max-width:64ch}
.secfoot__t{margin:0;color:var(--c-muted);font-size:16px;line-height:1.6;max-width:68ch}
.secfoot__a{font-weight:600;color:var(--c-primary);white-space:nowrap;display:inline-flex;gap:9px;align-items:center}
.secfoot__a .ar{transition:transform .28s var(--ease-out,cubic-bezier(.22,.61,.36,1))}
.secfoot__a:hover .ar{transform:translateX(5px)}
@media(max-width:760px){.secfoot{grid-template-columns:1fr;gap:16px}}
.mtile{
  /* clip, not hidden: hidden = scroll container = it CAPTURES the img's
     view() timeline and the drift freezes. hidden kept as old-browser fallback. */
  position:relative;overflow:hidden;overflow:clip;border-radius:0;
  grid-column:span 4;aspect-ratio:4/3;display:block;text-decoration:none;isolation:isolate;
  background:var(--ink);
}
/* the two that carry the section: the differentiator, then the volume service */
.mtile--hero{grid-column:span 7;grid-row:span 2;aspect-ratio:auto;min-height:100%}
.mtile--wide{grid-column:span 5;aspect-ratio:16/10}
/* sits under --wide in the 5 columns the 7-col hero leaves free. It MUST carry
   its own ratio: with aspect-ratio:auto the row had no intrinsic height and
   collapsed to 91px, because the hero spanning two rows cannot size them. */
.mtile--fill{grid-column:span 5;aspect-ratio:16/10}
.mtile img{
  position:absolute;inset:-1px;width:calc(100% + 2px);height:calc(100% + 2px);width:100%;height:100%;object-fit:cover;z-index:0;
  transform:scale(1.02);
  transition:transform .7s var(--ease-out,cubic-bezier(.22,.61,.36,1)),filter .5s ease;
  filter:saturate(.92)
}
.mtile::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(to top,rgba(6,16,30,.86) 0%,rgba(6,16,30,.42) 30%,rgba(6,16,30,.04) 62%,transparent 100%)
}
.mtile__in{position:relative;z-index:2;display:flex;flex-direction:column;justify-content:flex-end;
  height:100%;padding:clamp(16px,1.5vw,24px);color:#fff;gap:7px}
.mtile__n{font-family:var(--font-display);font-weight:600;letter-spacing:-.005em;line-height:1.18;
  font-size:clamp(16px,1.12vw,19px);max-width:22ch}
.mtile--hero .mtile__n{font-weight:520;font-size:clamp(28px,2.7vw,44px);max-width:15ch;letter-spacing:-.015em}
/* the descriptor is the reward for hovering, not permanent furniture */
.mtile__d{color:#C6D3E4;font-size:14.5px;line-height:1.5;max-width:44ch;
  opacity:0;transform:translateY(8px);
  transition:opacity .32s ease,transform .32s var(--ease-out,cubic-bezier(.22,.61,.36,1))}
.mtile--hero .mtile__d{opacity:1;transform:none}
.mtile:hover .mtile__d,.mtile:focus-visible .mtile__d{opacity:1;transform:none}
.mtile:hover img,.mtile:focus-visible img{transform:scale(1.06);filter:saturate(1)}
.mtile:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

@media(max-width:1000px){
  .mtile{grid-column:span 6}
  .mtile--hero{grid-column:span 12;grid-row:auto;aspect-ratio:16/10}
  .mtile--fill{grid-column:span 6;aspect-ratio:4/3}
  .mtile--wide{grid-column:span 6;aspect-ratio:4/3}
  .mtile__d{opacity:1;transform:none}   /* no hover on touch */
}
@media(max-width:640px){
  .mtile,.mtile--hero,.mtile--wide,.mtile--fill{grid-column:span 12;aspect-ratio:16/11}
}
@media(prefers-reduced-motion:reduce){
  .mtile img{transition:none}.mtile:hover img{transform:scale(1.02)}
}

/* ── page rhythm: two earned dark zones ────────────────────────────────────
   The complaint was that the page alternated blue / white / blue with no room
   to breathe, and it was right: the top half ran dark, dark, light, DARK,
   light, DARK, DARK while the bottom ran four light sections back to back.

   The page now has exactly two dark zones and they are both earned:
     1. hero + trust    — the opening, read as one unit
     2. process + work  — a single dark CHAPTER mid-page; the boundary inside it
                          is structural (a timeline, then a horizontal rail),
                          never another colour change
   Everything else is one continuous light run, and the about section brings the
   ground back up over the dark chapter to close it. Darks are now events.

   The proof stats moved onto the light ground. They stay high in the page,
   which is what the client asked for, but they carry on hairline rules instead
   of a colour field — they never needed one. */


/* ── projects section ───────────────────────────────────────────────────────
   This section used to PIN: arriving at it held the page still and mapped the
   reader's vertical scroll onto the rail's horizontal travel. Removed at client
   request - the page now scrolls past it like any other section, and the rail
   is driven only by the reader (drag, wheel, trackpad swipe, keyboard).
   This section carries no padding of its own outside that block, which is why
   the About panel used to clip the last card by 22px on mobile, where the pin
   never applied. It now gets the normal section rhythm on both edges - the
   extra overlap allowance is gone with the overlap itself. */
#work.work-jack{padding-block:var(--sec-y)}

/* ── projects rail ──────────────────────────────────────────────────────────
   Was a 3-up card grid — the same primitive as every other section. Our own
   playbook's fix for two regions that fight each other is "change the axis",
   and that applies here: the page scrolls down, so the work scrolls SIDEWAYS.
   Full-bleed, snapping, and it runs off the right edge on purpose so it reads
   as a set that continues rather than three cards that stop. */
.rail{
  display:grid;grid-auto-flow:column;
  grid-auto-columns:clamp(280px,30vw,440px);
  gap:clamp(12px,1.4vw,20px);
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  padding:clamp(22px,3vw,40px) var(--pad) clamp(10px,1.2vw,16px);
  /* snap aligns a card's start to the SNAPPORT start, which ignores the rail's
     own padding - so on load it scrolled 64px and pinned the first card flush
     to the viewport edge. Matching the scroll-padding keeps the inset the
     pinned version had. */
  scroll-padding-left:var(--pad);
  scrollbar-width:none;-ms-overflow-style:none
}
.rail::-webkit-scrollbar{display:none;width:0;height:0}
/* the affordance is the cut card at the viewport edge + this fade */
.railwrap{position:relative}
.railwrap::after{content:"";position:absolute;top:0;bottom:0;right:0;width:clamp(48px,7vw,120px);
  pointer-events:none;z-index:2;
  background:linear-gradient(to right,transparent,var(--ink))}
.rail{cursor:grab}.rail:active{cursor:grabbing}
/* mandatory snap would fight a manual drag on every pointermove */
.rail.is-dragging{scroll-snap-type:none;cursor:grabbing;scroll-behavior:auto;user-select:none}
.rail:focus-visible{outline:2px solid var(--gold);outline-offset:-4px}
.rcard{
  /* clip, not hidden — same view()-capture reason as .mtile */
  position:relative;scroll-snap-align:start;border-radius:var(--radius);overflow:hidden;overflow:clip;
  aspect-ratio:3/4;display:block;isolation:isolate;background:var(--ink-2)
}
.rcard img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;
  transform:scale(1.03);transition:transform .7s cubic-bezier(.22,.61,.36,1)}
.rcard::after{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(to top,rgba(5,14,26,.94) 4%,rgba(5,14,26,.55) 38%,rgba(5,14,26,.06) 74%)}
.rcard__in{position:relative;z-index:2;height:100%;display:flex;flex-direction:column;
  justify-content:flex-end;gap:7px;padding:clamp(18px,1.7vw,26px);color:#fff}
.rcard__k{font-size:12.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--gold)}
.rcard__t{font-family:var(--font-display);font-weight:500;font-size:clamp(19px,1.5vw,25px);
  letter-spacing:-.01em;line-height:1.14}
.rcard__m{font-size:14px;line-height:1.5;color:#C3D0E2;
  opacity:0;max-height:0;transform:translateY(6px);
  transition:opacity .3s ease,max-height .35s ease,transform .3s ease}
.rcard:hover .rcard__m,.rcard:focus-visible .rcard__m{opacity:1;max-height:6em;transform:none}
.rcard:hover img,.rcard:focus-visible img{transform:scale(1.08)}
@media(hover:none){.rcard__m{opacity:1;max-height:6em;transform:none}}

/* ── process: the connector DRAWS as the section is scrolled through ────────
   Dave: "there's no movement." A scroll-progress timeline is movement that
   means something — it is the clearance sequence advancing — rather than
   decoration. Pure compositor work via a view() timeline; where that is not
   supported the line is simply already drawn. */
/* geometry matches the existing track: 60px nodes, so the line sits at 30px
   and runs from the first node's centre to the last one's */
.proc__track--draw{position:relative}
.proc__track--draw::before{
  content:"";position:absolute;left:30px;right:calc(25% - 30px);top:30px;height:2px;
  background:var(--ink-line);z-index:0
}
.proc__track--draw::after{
  content:"";position:absolute;left:30px;right:calc(25% - 30px);top:30px;height:2px;
  transform-origin:left center;transform:scaleX(1);
  background:linear-gradient(90deg,var(--gold),var(--gold-soft));z-index:1
}
/* Dave: "make sure it doesn't reverse scroll as you scroll back up the page."
   It did, and it had to: animation-timeline:view() binds progress to scroll
   POSITION, so scrolling up necessarily un-draws the line. There is no
   one-way variant of a scroll-progress timeline.

   So the line is now driven by the same IntersectionObserver latch every other
   reveal on this page uses: .rev gets .in once, the observer unobserves, and a
   normal time-based animation runs to completion and stays. Scroll direction
   stops mattering. The default state below is the FINISHED line, so if the
   observer never fires the line is simply drawn. */
.proc__track--draw::after{transform:scaleX(1)}
@media(prefers-reduced-motion:no-preference){
  @keyframes procDraw{from{transform:scaleX(0)}to{transform:scaleX(1)}}
  .home .proc__track--draw.rev.in::after{
    animation:procDraw 1.5s var(--ease-out) .25s backwards}
}
.proc__track--draw .step{position:relative;z-index:2}
.proc__track--draw .step__node{background:var(--ink);position:relative}

/* Dave: "will there be any minor movement added to it? Like the numbers
   turning orange when hovered over ... It looks like something should be
   moving on this graphic." Deliberately MINOR — the ring and numeral take the
   accent and the node lifts 2px. No scale, no glow: this sits on a dark band
   next to a drawn line, and anything larger competes with it. */
.step{cursor:default}
.step__node{transition:border-color .25s var(--ease-out),
  background-color .25s var(--ease-out), transform .25s var(--ease-out)}
.step__node span{transition:color .25s var(--ease-out)}
.step:hover .step__node,.step:focus-within .step__node{
  border-color:var(--c-accent);transform:translateY(-2px)}
.step:hover .step__node span,.step:focus-within .step__node span{color:var(--c-accent)}
.step h3{transition:color .25s var(--ease-out)}
.step:hover h3{color:var(--c-accent)}

/* ── insights ───────────────────────────────────────────────────────────────
   Asked for by John, and moved up beside Who We Are. One lead story with real
   imagery plus a dated list — deliberately NOT another 3-up equal card grid,
   which is the primitive this page already over-uses. */
.ins{display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(56px,6vw,104px);align-items:start}
.ins__lead{display:block;text-decoration:none;color:inherit;position:relative}
/* navy offset plate behind the featured image removed: Mariah, Oct 20 email ("Last thing - can this be taken care of?") */
.ins__media{position:relative;z-index:1}
/* overflow:clip, NOT hidden — hidden makes this a scroll container, which
   captures the img's view() timeline and freezes the drift (the img never
   moves INSIDE this box). clip clips identically without becoming a scroller. */
.ins__media{display:block;border-radius:var(--radius);overflow:hidden;overflow:clip;aspect-ratio:16/10;background:var(--c-surface-2)}
.ins__media img{width:100%;height:100%;object-fit:cover;
  transition:transform .7s cubic-bezier(.22,.61,.36,1)}
.ins__lead:hover .ins__media img{transform:scale(1.04)}
.ins__body{display:block;padding-top:26px}
.ins__meta{display:block;font-size:12.5px;font-weight:600;letter-spacing:.05em;
  text-transform:uppercase;color:var(--c-muted);margin-bottom:9px}
.ins__t{display:block;font-family:var(--font-display);font-weight:500;
  font-size:clamp(20px,1.7vw,27px);line-height:1.2;letter-spacing:-.01em}
.ins__x{display:block;margin-top:10px;color:var(--c-muted);font-size:15.5px;line-height:1.55;max-width:48ch}
.ins__list{display:flex;flex-direction:column}
.ins__row{padding:20px 0;border-top:1px solid var(--c-border);text-decoration:none;color:inherit}
.ins__row:last-child{border-bottom:1px solid var(--c-border)}
.ins__row .ins__t{transition:transform .3s cubic-bezier(.22,.61,.36,1)}
.ins__row:hover .ins__t{transform:translateX(8px)}
.ins__row .ins__t{font-size:clamp(16.5px,1.15vw,19px);line-height:1.35}
@media(max-width:900px){.ins{grid-template-columns:1fr}}

/* ── motion ─────────────────────────────────────────────────────────────────
   Dave: "there's no movement … I was expecting something a little more
   dynamic." Before this the only thing on the page was a one-shot fade-up,
   fired identically on 39 elements, which is exactly the "block after block,
   same intro" tell.

   Every rule below is transform/opacity ONLY and driven by a view() timeline,
   so it is compositor work with no JS: it cannot be throttled the way this
   project's earlier rAF tweening was, and it is tied to scroll position rather
   than to a clock. It is also NOT page parallax — the page itself never
   detaches from the scroll. Dave: "Netra's website has way too much parallax …
   that's not appropriate for a environmental consulting company." What moves
   is the image INSIDE its own frame, by a few percent. */
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){

    /* images breathe within their frame as they cross the viewport */
    @keyframes imgDrift{from{transform:translate3d(0,-3.5%,0) scale(1.06)}
                          to{transform:translate3d(0, 3.5%,0) scale(1.06)}}
    .mtile img,.rcard img,.ins__media img{
      height:112%;top:-6%;
      animation:imgDrift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%
    }
    /* hover still wins over the drift */
    .mtile:hover img,.rcard:hover img{animation-play-state:paused}

    /* tiles rise into place, staggered, instead of one global fade */
    @keyframes tileRise{from{transform:translate3d(0,22px,0)}
                          to{transform:none}}
    .mtile,.rcard,.ins__row{
      animation:tileRise cubic-bezier(.22,.61,.36,1) both;
      animation-timeline:view();
      animation-range:entry 4% entry 46%
    }
    .mosaic .mtile:nth-child(2){animation-range:entry 8% entry 50%}
    .mosaic .mtile:nth-child(3){animation-range:entry 11% entry 53%}
    .mosaic .mtile:nth-child(4){animation-range:entry 6% entry 48%}
    .mosaic .mtile:nth-child(5){animation-range:entry 9% entry 51%}
    .mosaic .mtile:nth-child(6){animation-range:entry 12% entry 54%}

    /* the hero settles back as you leave it — gives the scroll a sense of depth
       without moving the hero media itself */
    @keyframes heroSettle{from{transform:none}
                            to{transform:translate3d(0,-38px,0)}}
    .hero__lede{
      animation:heroSettle linear both;
      animation-timeline:view();
      animation-range:exit 0% exit 92%
    }

    /* the big stat figures count in visually by rising, one after another */
    @keyframes proofRise{from{transform:translate3d(0,18px,0)}to{transform:none}}
  }
}


/* a section whose last child runs edge to edge ends ON that element */
/* the mosaic ends the section flush */
.home .sec:has(> .mosaic:last-child){padding-bottom:0}
/* the rail does NOT: the next section overlaps up into this band, so it needs
   clear dark to land on. Without this the white panel sat on the card titles. */
.home .sec:has(.railwrap:last-child){padding-bottom:calc(clamp(56px,7vw,112px) + clamp(44px,4.5vw,72px))}
.home /* the closing line breathes before the dark chapter — its white-side padding
   now mirrors the dark side's own top padding at the same boundary */
.sec:has(> .secfoot:last-child){padding-bottom:clamp(56px,4.6vw,76px)}

/* ── heading rule ───────────────────────────────────────────────────────────
   Lifted from SOAR's current site, which sets a short bar under its headings.
   It is the one confident move on that page and it costs nothing. Structural,
   not decorative — it belongs to the heading, so it is not one of the stray
   hairlines the client asked us to strip. */
.h2{position:relative}
.rule-under::after{content:"";display:block;width:56px;height:3px;
  background:var(--c-accent);margin-top:clamp(18px,1.8vw,26px)}
/* The rule is an ::after INSIDE the heading box, so anything following the
   heading butts against the accent mark rather than against type. Found three
   live instances: .pled__copy at a literal 0px gap, and both closing CTAs at
   16px, which leaves ~6px under a 3px accent bar. Any sibling after a ruled
   heading now clears it. Only fires when a sibling exists, so ruled headings
   that end their grid cell (every .shead) are untouched. */
.rule-under + *{margin-top:clamp(22px,2.4vw,30px)}
.band-paper .rule-under::after{background:var(--c-primary)}

/* the hero keeps the lighter display weight — 600 at 100px shouts */


/* ── overlapping panels (MIT Technology Review device) ──────────────────────
   Their white content panel rides UP over the dark band above it, hard-edged,
   so the boundary is a real overlap rather than two blocks meeting. It is what
   gives that page its depth without a single shadow or gradient.

   Here the same move does double duty: it kills the "harsh next section" join
   the mosaic used to have, and it is the reason the palette now needs a true
   near-black — a white panel over navy is mush, a white panel over #12171B is
   a hard edge. */
/* NB: named .oplap, not .panel — `.panel` is already the mega-menu dropdown
   (position:absolute), and colliding with it took this block out of flow: the
   section measured 92px tall while its content was 743px. */
.oplap{
  position:relative;z-index:3;
  background:var(--c-bg);          /* the ground, NOT a white card — see note */
  max-width:var(--maxw);
  margin-inline:auto;
  /* The overlap is GONE at client request - this used to pull up by
     clamp(56px,7vw,112px) so the panel rode over the dark projects band. It now
     sits in its own section. The background stays: it is the same #F3F3F0 as
     the body, so with no pull-up the panel simply reads as the light ground
     rather than a card. */
  padding:clamp(38px,4.4vw,72px) clamp(28px,3.4vw,64px) clamp(60px,6vw,100px);
}
/* The panel still supplies its own top inset, so the section adds none.
   `display:flow-root` is kept as a BFC guard: it stops the panel's margins
   collapsing through the section, which is what once measured this section at
   92px tall while its panel was 714px. */
.home .sec:has(> .oplap){padding-top:0;padding-bottom:clamp(20px,2vw,32px);display:flow-root}
@media(max-width:900px){
  .oplap{padding:clamp(28px,6vw,40px) clamp(20px,5vw,32px)}
}

/* ── category chip ──────────────────────────────────────────────────────────
   MIT marks every category label with a small solid square. It is a cheap,
   confident signature and it does a real job: it colour-codes without tinting
   whole surfaces, which is what the playbook asks for ("accent MARKS what
   matters; it doesn't coat every surface"). Replaces our eyebrow dash rule. */
.ins__meta,.rcard__k{display:inline-flex;align-items:center;gap:9px}
.ins__meta::before,.rcard__k::before{
  content:"";width:9px;height:9px;background:currentColor;display:inline-block;flex:0 0 auto}

/* ── outlined square buttons ────────────────────────────────────────────────
   MIT's READ NOW / MORE ISSUES are hard rectangles with a 1px border and no
   fill. Reads editorial and press-like rather than SaaS-CTA. */
.btn--ghost{border-width:1px;border-style:solid;background:transparent}

/* eyebrow micro-label — ONE definition. Sentence case, no dash, no stretch:
   the dash + wide-tracked caps combo is the 2020 AI-kicker tell. */
.eyebrow{display:inline-flex;align-items:center;
  font-family:var(--font-body);font-weight:600;font-size:14px;
  letter-spacing:.01em;text-transform:none;color:var(--gold);margin:0 0 18px}
.band-paper .eyebrow{color:var(--c-muted)}

/* headings */
.h2{font-family:var(--font-display);font-weight:500;font-size:var(--t-h2);
  letter-spacing:var(--track-display);line-height:1.04;margin:0}
.h2 .em{color:var(--gold)}
.band-paper .h2 .em{color:var(--c-ink);
  background-image:linear-gradient(var(--c-accent),var(--c-accent));
  background-repeat:no-repeat;background-position:0 92%;background-size:0% 3px;
  transition:background-size .6s var(--ease-out-strong) .55s}
.in .h2 .em,.h2.in .em{background-size:100% 3px}
.lead{font-size:var(--t-lead);line-height:1.55;color:var(--on-ink-dim);margin:0}
.band-paper .lead{color:var(--c-muted)}

/* section header (eyebrow + h2 + optional link), one layout */
.shead{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  align-items:start;gap:clamp(24px,3vw,56px);
  margin-bottom:clamp(30px,3.2vw,48px)}
/* the side text tops-align with the heading — it was bottom-aligned, which
   left it floating ~170px down and opened a diagonal void across the head */
.shead>p,.shead .lead{margin-top:6px}
.shead .h2{max-width:24ch}
.shead__t{max-width:62ch}
/* .shead had NO mobile breakpoint anywhere, so every section heading on every
   page stayed a two-column grid down to 375: measured 187px / 139px at a 390
   viewport. A 139px-wide paragraph column is not a column. Stacks below 760.
   NB the service page repeats this rule in its own <style>, because
   `.svcpage .shead` there loads after fs.css and would otherwise win. */
@media(max-width:760px){
  .shead{grid-template-columns:1fr;gap:14px}
  .shead .h2,.shead__t{max-width:none}
}

/* ONE button system (matches brand tokens: 12px radius, sentence case,
   weight 500, ~14x26 padding) */
.btn{display:inline-flex;align-items:center;gap:10px;cursor:pointer;
  font-family:var(--font-body);font-weight:var(--btn-weight,600);font-size:15px;letter-spacing:0;
  text-transform:none;padding:14px 26px;border-radius:var(--r-2);
  border:1px solid transparent;background:transparent;color:inherit;
  transition:background .2s var(--ease-out),border-color .2s,color .2s,transform .2s}
.btn .ar{transition:transform .3s var(--ease-out)}
.btn:hover .ar{transform:translateX(4px)}
.btn--primary{background:var(--gold);color:var(--c-on-accent);border-color:var(--gold)}
.btn--primary:hover{background:var(--gold-soft);border-color:var(--gold-soft);transform:translateY(-1px)}
.btn--ghost{border-color:var(--ink-line);color:#fff}
.btn--ghost:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.55)}
.band-paper .btn--ghost{border-color:var(--c-border);color:var(--c-ink)}
.band-paper .btn--ghost:hover{background:rgba(15,44,82,.05);border-color:var(--c-ink)}

/* text link with arrow — ONE definition */
.tlink{display:inline-flex;align-items:center;gap:9px;font-weight:600;font-size:14px;
  letter-spacing:.01em;color:inherit}
.tlink .ar{color:var(--gold);font-weight:700;transition:transform .3s var(--ease-out)}
.band-paper .tlink .ar{color:var(--c-muted)}
.tlink:hover .ar{transform:translateX(5px)}

/* duotone imagery wash (navy + gold), single treatment */
.duo{position:relative;isolation:isolate;background:var(--c-primary-strong);overflow:hidden}
.duo>img{width:100%;height:100%;object-fit:cover;filter:grayscale(.55) contrast(1.05) brightness(.95)}
.duo::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(150deg,rgba(10,27,49,.42),rgba(10,27,49,.10) 55%,rgba(221,166,62,.14));
  mix-blend-mode:multiply}

/* hairline divider */
.rule{height:1px;background:var(--ink-line);border:0;margin:0}
.band-paper .rule{background:var(--c-border)}

/* ============================================================
   NAV
   ============================================================ */
.nav{position:fixed;top:0;left:0;right:0;z-index:60;background:transparent;
  border-bottom:1px solid transparent;transition:background .3s,border-color .3s,backdrop-filter .3s}
.nav::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(180deg,rgba(10,27,49,.55),transparent);transition:opacity .3s}
.nav.fixed{background:rgba(10,27,49,.78);border-bottom-color:var(--ink-line-2)}
.nav.fixed::before{opacity:0}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav.fixed{background:rgba(10,27,49,.55);-webkit-backdrop-filter:blur(16px) saturate(140%);backdrop-filter:blur(16px) saturate(140%)}}
.nav__in{max-width:var(--maxw);margin:0 auto;padding:16px var(--pad);display:flex;align-items:center;gap:28px;min-height:66px;transition:padding .26s,min-height .26s}
.nav.fixed .nav__in{padding:13px var(--pad);min-height:62px}
.nav__links{display:flex;gap:28px}
.nav__links a{position:relative;font-weight:500;font-size:16px;color:rgba(255,255,255,.9);padding-block:6px;transition:color .15s}
.nav__links a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--gold);
  transform:scaleX(0);transform-origin:left;transition:transform .28s var(--ease-out)}
.nav__links a:hover{color:#fff}
.nav__links a:hover::after{transform:scaleX(1)}
.nav__right{margin-left:auto;display:flex;align-items:center;gap:22px}
.nav__tel{font-weight:500;font-size:15px;color:#fff;opacity:.92}
.nav .btn--primary{padding:11px 22px;font-weight:600}
.nav.fixed .btn--primary{padding:10px 20px}
.nav__burger{display:none;background:none;border:0;color:#fff;cursor:pointer}
/* ---- MOBILE MENU ------------------------------------------------------
   Was a JS display:none/flex toggle writing inline styles: it popped with no
   animation, carried no aria state, could only be dismissed by hitting the
   burger a second time, and hardcoded top:64px while the bar actually
   measures 73px. Now a class-driven panel.

   The open class is `mobile-open`, NOT `menu-open` — the desktop mega-menu JS
   already owns `menu-open` on the same element.

   --navh is written by the script from the real measured bar height, so the
   panel always meets the bar instead of sliding under it. */
@media(max-width:960px){
  .nav__tel{display:none}
  .nav__burger{display:inline-flex}
  .nav__links{
    display:flex;flex-direction:column;gap:0;
    position:fixed;left:0;right:0;top:var(--navh,66px);
    background:var(--ink);border-bottom:1px solid var(--ink-line-2);
    padding:6px var(--pad) 18px;
    max-height:calc(100dvh - var(--navh,66px));overflow-y:auto;
    opacity:0;visibility:hidden;transform:translateY(-10px);
    transition:opacity .2s ease,transform .26s var(--ease-out),visibility 0s linear .26s}
  .nav.mobile-open .nav__links{
    opacity:1;visibility:visible;transform:none;
    transition:opacity .2s ease,transform .26s var(--ease-out),visibility 0s}
  /* the caret items are <button> and the plain ones are <a>; without this they
     sit at two different left edges */
  /* <button> and <a> rendered at 46px and 55px; matched so the rows keep one
     rhythm, and floored at 48px for a comfortable tap target */
  .nav__links .navlink{display:flex;align-items:center;width:100%;
    justify-content:flex-start;text-align:left;min-height:48px;
    padding:11px 0;margin:0;font-size:17px;line-height:1.25;
    border-bottom:1px solid var(--ink-line-2)}
  .nav__links .navlink:last-child{border-bottom:0}
  .nav__links a::after{display:none}
  .nav__burger svg{transition:transform .26s var(--ease-out)}
  .nav.mobile-open .nav__burger svg{transform:rotate(90deg)}
}
@media(prefers-reduced-motion:reduce){
  .nav__links,.nav__burger svg{transition:none}
}

/* ---- nav mega / dropdown menu (category rail) ---- */
.navlink{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-body);font-weight:500;
  font-size:15px;color:rgba(255,255,255,.9);background:none;border:0;cursor:pointer;padding-block:6px;
  position:relative;transition:color .15s}
.navlink:hover,.navlink[aria-expanded="true"]{color:#fff}
.navlink .caret{width:11px;height:11px;opacity:.6;transition:transform .22s var(--ease-out)}
.navlink[aria-expanded="true"] .caret{transform:rotate(180deg);opacity:1}
.navlink[data-menu]::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--gold);
  transform:scaleX(0);transform-origin:left;transition:transform .25s var(--ease-out-strong)}
.navlink[data-menu]:hover::after,.navlink[aria-expanded="true"][data-menu]::after{transform:scaleX(1)}
.nav.menu-open{background:rgba(10,27,49,.96);border-bottom-color:var(--ink-line-2)}
.nav.menu-open::before{opacity:0}

.panels{position:absolute;left:0;right:0;top:100%;height:0;z-index:60}
.panel{position:absolute;top:0;opacity:0;visibility:hidden;transform:translateY(-9px);pointer-events:none;
  transition:opacity .18s ease,transform .24s var(--ease-out);color:var(--c-ink)}
.panel.open{opacity:1;visibility:visible;transform:none;pointer-events:auto}
.panel a{text-decoration:none}
/* mega (category rail) */
.panel:not(.panel--simple){left:0;right:0;background:#fff;border-bottom:1px solid var(--c-border);box-shadow:0 34px 64px -34px rgba(10,27,49,.4)}
.panel:not(.panel--simple) .panel__in{display:grid;grid-template-columns:282px 1fr 326px;gap:0;min-height:356px;align-items:stretch}
.menu__rail{padding:22px 16px;border-right:1px solid var(--c-border);display:flex;flex-direction:column;gap:2px}
.menu__rail .eyebrow{margin:6px 10px 12px}
.rail-item{display:flex;align-items:center;gap:12px;width:100%;padding:11px 12px;border-radius:var(--radius);position:relative;cursor:pointer;color:var(--c-ink);background:none;border:0;text-align:left;font-family:var(--font-display);transition:background .15s}
.rail-item::before{content:"";position:absolute;left:3px;top:9px;bottom:9px;width:3px;border-radius:var(--radius);background:var(--c-accent);transform:scaleY(0);transition:transform .2s var(--ease-out-strong)}
.rail-item .ic{width:18px;height:18px;color:var(--c-muted);flex:0 0 auto;transition:color .15s}
.rail-item .rcol{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.rail-item .lab{font-weight:600;font-size:14.5px;letter-spacing:-.01em;color:var(--c-ink)}
.rail-item .rdesc{display:none;font-weight:400;font-size:12px;line-height:1.35;color:var(--c-muted)}
.rail-item .ch{width:14px;height:14px;color:var(--c-accent);opacity:0;transform:translateX(-4px);transition:opacity .16s,transform .16s;flex:0 0 auto}
.rail-item:hover{background:#F4F6F9}
.rail-item.active{background:#F1F4F8}
.rail-item.active::before{transform:scaleY(1)}
.rail-item.active .ic{color:var(--c-accent-2)}
.rail-item.active .lab{color:var(--c-primary)}
.rail-item.active .ch{opacity:1;transform:none}
.menu__detailwrap{padding:34px 40px;position:relative}
.detail{display:none}
.detail.active{display:block;animation:mfade .22s var(--ease-out)}
@keyframes mfade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.detail .eyebrow{margin-bottom:12px}
.detail h3{font-family:var(--font-display);font-weight:600;font-size:26px;letter-spacing:-.01em;color:var(--c-primary)}
.detail .lead{font-size:14.5px;line-height:1.5;color:var(--c-muted);margin-top:10px;max-width:46ch}
.detail .deeplinks{margin-top:24px;border-top:1px solid var(--c-border)}
.deep{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 4px;border-bottom:1px solid var(--c-border);transition:padding .15s}
.deep:hover{padding-left:10px}
.deep .t{display:block;font-family:var(--font-display);font-weight:600;font-size:14px;color:var(--c-ink)}
.deep .d{display:block;font-size:12.5px;color:var(--c-muted);margin-top:2px}
.deep .go{color:var(--c-accent);opacity:0;transform:translateX(-4px);transition:opacity .15s,transform .15s}
.deep:hover .go{opacity:1;transform:none}
.detail .all{display:inline-flex;align-items:center;gap:9px;margin-top:22px;font-family:var(--font-display);font-weight:600;font-size:13.5px;color:var(--c-accent-2)}
.detail .all svg{width:15px;height:15px;transition:transform .2s}
.detail .all:hover svg{transform:translateX(4px)}
.menu__feat{position:relative;margin:22px 22px 22px 0;border-radius:var(--radius);overflow:hidden;isolation:isolate;display:block}
.menu__feat img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:-2}
.menu__feat::after{content:"";position:absolute;inset:0;z-index:-1;background:linear-gradient(180deg,rgba(10,27,49,.12),rgba(10,27,49,.5) 52%,rgba(8,18,34,.92))}
.menu__feat .cap{position:absolute;left:18px;right:18px;bottom:16px;color:#fff}
.menu__feat .cap .k{font-family:var(--font-body);font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--c-accent);display:block;margin-bottom:7px}
.menu__feat .cap .h{font-family:var(--font-display);font-weight:600;font-size:18px;letter-spacing:-.01em;display:block}
.menu__feat .cap .s{font-size:12.5px;color:#d4dce6;display:block;margin-top:4px}
.menu__feat .cap .arr{position:absolute;right:0;bottom:2px;width:34px;height:34px;border-radius:50%;border:1px solid rgba(255,255,255,.4);display:flex;align-items:center;justify-content:center;transition:background .2s,border-color .2s}
.menu__feat:hover .cap .arr{background:var(--c-accent);border-color:var(--c-accent);color:var(--c-on-accent)}
.panel:not(.panel--simple) .panel__sfoot{display:none}
/* simple dropdown */
.panel--simple{left:var(--anchor);width:344px;max-width:min(344px,92vw);background:#fff;margin-top:10px;border:1px solid var(--c-border);border-radius:var(--radius);box-shadow:0 30px 60px -30px rgba(10,27,49,.45);overflow:hidden}
.panel--simple .panel__in{display:block;padding:8px}
.panel--simple .menu__rail{padding:0;border-right:0;gap:1px}
.panel--simple .menu__rail .eyebrow{margin:8px 12px 8px}
.panel--simple .menu__detailwrap,.panel--simple .menu__feat{display:none}
.panel--simple .rail-item{align-items:flex-start}
.panel--simple .rail-item .rdesc{display:block}
.panel--simple .rail-item::before{display:none}
.panel--simple .rail-item.active{background:none}
.panel--simple .panel__sfoot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:13px;margin-top:4px;border-top:1px solid var(--c-border)}
.panel__sfoot .all{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-display);font-weight:600;font-size:13px;color:var(--c-accent-2)}
.panel__sfoot .all svg{width:14px;height:14px}
.panel__offices{font-family:var(--font-body);font-size:11px;letter-spacing:.06em;color:var(--c-muted)}

/* ============================================================
   HERO — cinematic full-video, oversized headline with ONE gold word,
   floating glass fact-cards, credential strip
   ============================================================ */
/* homepage scrim: neutral smoke, not blue. Legibility from the left, a low
   band for the credential strip, zero hue. */
.home .hero__scrim{background:
  linear-gradient(100deg,rgba(12,12,13,.74) 0%,rgba(14,14,15,.42) 38%,rgba(16,16,17,.10) 62%,rgba(16,16,17,0) 100%),
  linear-gradient(to top,rgba(10,10,11,.62) 0%,rgba(10,10,11,0) 26%)}

/* SHARED hero base — inner pages use this. Homepage overrides live under
   .home (index.html <body class="home">) and must NEVER be written here. */
.hero{position:relative;min-height:clamp(580px,82vh,820px);
  display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden;background:var(--ink)}
.home .hero{min-height:100vh;min-height:100svh}
.hero__media{position:absolute;inset:0;z-index:0}
.hero__media video,.hero__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:grayscale(.18) contrast(1.04) brightness(1.02)}
/* cool blue cinematic grade: deep ink shadows -> luminous steel-blue toward the
   open right, with a soft sky bloom. A graded duotone, not a flat navy wash. */
.hero__scrim{position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(116deg,rgba(7,16,30,.82) 0%,rgba(10,26,48,.55) 32%,rgba(18,52,92,.20) 64%,rgba(60,118,180,.06) 100%),
    linear-gradient(0deg,rgba(6,13,26,.86) 0%,rgba(6,13,26,.12) 30%,transparent 52%),
    radial-gradient(60% 55% at 86% 6%,rgba(130,180,232,.20),transparent 60%)}
.hero__scrim::before{content:"";position:absolute;inset:0;
  background:radial-gradient(70% 70% at 18% 86%,rgba(4,10,22,.32),transparent 62%)}
.hero__scrim::after{content:"";position:absolute;inset:0;opacity:.06;background-image:var(--grain)}
.hero__in{position:relative;z-index:3;width:100%;max-width:var(--maxw);margin:0 auto;
  padding-inline:var(--pad);padding-top:clamp(110px,14vh,140px);padding-bottom:clamp(28px,3vw,44px);
  display:grid;grid-template-columns:1.25fr .75fr;gap:clamp(28px,4vw,56px);align-items:end}
.hero__lede{max-width:760px}
.hero h1{font-family:var(--font-display);font-weight:500;color:#fff;
  letter-spacing:var(--track-display);line-height:.94;font-size:clamp(44px,6.4vw,90px);margin:0}
/* homepage hero composition */
.home .hero__in{grid-template-columns:1.25fr minmax(300px,.75fr);gap:clamp(28px,4vw,64px);
  padding-bottom:clamp(34px,3.6vw,52px)}
.home .hero__lede{max-width:820px}
.home .hero__side{display:flex;flex-direction:column;gap:clamp(20px,2.2vh,30px);
  align-items:flex-start;padding-bottom:6px}
/* The v3.04 headline is 44 characters against v3.02's 29. At the old
   clamp(50px,7vw,104px) each mask span wrapped to THREE lines and the h1 box
   went 281px -> 562px, doubling the hero's text block. The column cannot be
   widened: the hero grid is 1.25fr/.75fr, and giving the headline the ~820px
   it would need to hold "Every Project Begins" on one line leaves 274px for
   the subhead, which is now a full sentence.

   So the scale comes down instead of the layout changing. Measured at 684px
   of column: 80px and below yields 2+2 lines at every tested viewport, and the
   h1 box lands at 253-298px — the same vertical space the approved v3.02 hero
   occupied (281px). Nothing else about the hero moves.

   LEADING: .93 -> 1.02. The leading was already mathematically even (71.0 /
   71.0 / 70.9px, block boundary included), but at .93 the INK gap between
   lines measured ONE pixel — the descender of "g" in Begins sat on the
   ascender of "h" in With, and "Every Project"'s y/j did the same to "Begins".
   Scanned the rendered ink at several values: .98 -> 5px, 1.02 -> 8px,
   1.06 -> 11px. 1.02 clears the collision and is still tight for display type.
   The old copy never exposed this because its wraps put no descender above an
   ascender. */
.home .hero h1{font-weight:600;font-variation-settings:"wdth" var(--wd-display);
  line-height:1.02;font-size:clamp(44px,5.3vw,80px)}
.hero h1 .g{color:#fff}
/* the line under every page title: full white with a soft dark halo, so a bright photo behind it cannot wash it out
   (client note, Oct 27 2026; it was #d4deeb with no halo) */
.hero .sub{color:#fff;font-size:clamp(16px,1.35vw,20px);line-height:1.5;max-width:46ch;margin:22px 0 0;
  text-shadow:0 1px 2px rgba(4,10,22,.7),0 0 18px rgba(4,10,22,.55)}
/* homepage: neutral near-white, no blue cast */
.home .hero .sub{color:rgba(255,255,255,.86);font-size:clamp(16px,1.3vw,19px);
  line-height:1.55;max-width:38ch;margin:0}
.hero .actions{display:flex;gap:14px;flex-wrap:wrap;margin-top:30px}

/* floating glass fact-cards (glassmorphism) */
.hero__cards{display:flex;flex-direction:column;gap:14px;align-items:stretch;justify-self:end;width:min(100%,360px)}
.gcard{position:relative;border-radius:var(--r-3);padding:20px 22px;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.16);
  box-shadow:var(--shadow-glass);overflow:hidden}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .gcard{-webkit-backdrop-filter:blur(16px) saturate(130%);backdrop-filter:blur(16px) saturate(130%)}}
.gcard::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(135deg,rgba(255,255,255,.14),transparent 45%);mix-blend-mode:screen}
.gcard__lab{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--on-ink-dim)}
.gcard__big{font-family:var(--font-display);font-weight:500;font-size:clamp(26px,2.4vw,34px);
  letter-spacing:var(--track-display);color:#fff;margin:8px 0 2px;line-height:1.05}
.gcard__sub{font-size:13px;color:#c4d0de}
.gcard--gauge{display:flex;align-items:center;gap:18px}
.gauge{flex:none;width:72px;height:72px;border-radius:50%;display:grid;place-items:center;position:relative;
  background:var(--gold)}
.gauge::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:54px;height:54px;border-radius:50%;background:rgba(12,28,46,.55)}
.gauge span{font-size:24px}
.gauge span{position:relative;z-index:2;font-family:var(--font-display);font-weight:500;font-size:15px;color:#fff}
.gauge-wrap{position:relative;width:74px;height:74px;display:grid;place-items:center;flex:none}

/* credential strip pinned under hero */
.hero__creds{position:relative;z-index:3;width:100%;margin-top:clamp(32px,4vw,56px);
  border-top:1px solid var(--ink-line-2);
  background:var(--ink) 60%)}
.hero__creds .wrap{display:flex;flex-wrap:wrap;align-items:center;gap:clamp(18px,4vw,52px);padding-top:22px;padding-bottom:22px}
.cred{display:flex;align-items:baseline;gap:9px;font-size:13px;color:#c8d3e1}
.cred b{font-family:var(--font-display);font-weight:600;font-variation-settings:"wdth" var(--wd-display);font-size:18px;color:#fff;letter-spacing:-.01em}
.cred .gold{color:#fff}
/* ── service-page hero bar: filled, not wireframed ─────────────────────────
   Victor: the bar at the foot of every service hero reads as wireframe. Three
   of its parts were outline-only: the bar itself (transparent, one 8% hairline
   floating over the photograph), the secondary button (transparent, 1px
   outline — an empty box), and the scroll cue (a 1x26px stroke).

   The bar was never MEANT to be hollow. The shared rule above declares
   `background:var(--ink) 60%)` — a stray `60%)` with no function around it,
   so the browser discards the whole declaration and the bar falls through to
   transparent. That is left as-is for the homepage, which is signed off and
   must not change; the service pages get a real fill here instead.

   Same content, same layout, same buttons. Only the hollowness goes. */
.svcpage .hero__creds{border-top:0;background:rgba(9,17,30,.9)}
.svcpage .hero .btn--ghost{background:rgba(255,255,255,.13);border-color:transparent}
.svcpage .hero .btn--ghost:hover{background:rgba(255,255,255,.22);border-color:transparent}
.svcpage .hero__scroll__rail{display:none}

/* ── internal pages: audit fixes (2026-09-16) ─────────────────────────────
   Found by a mechanical pass over every internal page at ten widths rather
   than by eye. Scoped to .svcpage / .cpage so the approved homepage does not
   move. */

/* E · textures cut by their section box — the #contact motif is now painted
   inside its section on each page, with an edge fade; see the page rules. */
/* the shared contour ground starts exactly on its section's top edge; give
   the head of it the same dissolve the tail already has */
.svcpage .topo-ground::before{
  -webkit-mask-image:var(--topo-mask, linear-gradient(to bottom,transparent 0%,#000 12%,#000 56%,rgba(0,0,0,.42) 82%,transparent 100%));
          mask-image:var(--topo-mask, linear-gradient(to bottom,transparent 0%,#000 12%,#000 56%,rgba(0,0,0,.42) 82%,transparent 100%))}

/* D · two dark sections back to back read as one dead slab. Valued Partners
   sits between two band-dark sections on every service page; it takes the
   lifted brand navy so each boundary is a real change of ground. */
.svcpage #partners{background:var(--c-primary)}

/* a section-head link ("All projects →") sat at the top of its cell, level
   with the eyebrow and well above the heading it belongs to */
.svcpage .shead>.tlink{align-self:end;justify-self:start}

/* a section head whose authored lines are longer than half the measure gets
   the wider share, rather than letting its lines break */
@media(min-width:761px){.svcpage .shead.shead--wide{grid-template-columns:1.7fr 1fr}}

/* C · one-word last lines in headings ("…Waste / Consulting", "…the /
   ground."). Balanced wrapping lets the browser even the lines instead of
   stranding a word; body copy gets pretty wrapping for the same reason. */
/* balance evens heading lines; the last-two-words binding in the markup is
   what guarantees no word is stranded (pretty on headings made H1s worse) */
.svcpage h1,.svcpage h2,.svcpage h3,.cpage h1,.cpage h2,.cpage h3{text-wrap:balance}
/* pairs too long to bind at phone width stay together from 700px up only */
@media(min-width:700px){.nw{white-space:nowrap}}
.svcpage p,.svcpage li,.svcpage blockquote,.cpage p{text-wrap:pretty}

/* F · the brand orange is 2.6-2.9:1 on the paper grounds — fine for large
   display type and fills, not for small text. Small accent text on light
   ground uses a deeper step of the same hue (4.5-5.5:1 on all three). */
.svcpage{--c-accent-text:#A4520A}
.svcpage .band-paper .svx__go,.svcpage .band-paper .pcell__n,.svcpage .band-paper .custody__n,
.svcpage .band-paper .req,.svcpage .band-paper .tlink,
.cpage .field label .req,.cpage .cform__head p .req,.cpage .cform p a{color:var(--c-accent-text,#A4520A)}
.cpage{--c-accent-text:#A4520A}

/* Scroll cue - the credential strip's right-hand counterweight, in the slot the
   video pause button used to hold. Deliberately a DIFFERENT species from the
   .cred blocks beside it: a UI affordance in the micro-label register, so the
   bar reads text-left / control-right rather than as one longer list of
   credentials. Own class, not .hero__showreel - the CEQA page still uses that. */
.hero__scroll{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:12px;
  font-size:11px;font-weight:500;letter-spacing:.05em;text-transform:uppercase;
  color:rgba(255,255,255,.66);text-decoration:none;transition:color .25s ease}
.hero__scroll:hover{color:#fff}
/* The cert number is supporting detail, not a third headline credential, so it
   breaks the DVBE/SDVOSB rhythm on purpose: sentence case, lighter weight and
   lower contrast instead of the bold-acronym-plus-descriptor treatment. No
   hairline rule - decorative hairlines were cut in round one. */
.hero__certno{font-size:12px;font-weight:500;letter-spacing:.02em;color:rgba(255,255,255,.46)}
.hero__certno b{font-weight:500;color:rgba(255,255,255,.8);font-variant-numeric:tabular-nums}
/* overflow:clip alongside hidden so the rail never becomes a scroll container -
   that is what froze the image drifts (motion spec trap #10). */
.hero__scroll__rail{position:relative;display:block;width:1px;height:26px;flex:none;
  background:rgba(255,255,255,.28);overflow:hidden;overflow:clip}
.hero__scroll__rail::after{content:"";position:absolute;left:0;top:0;width:1px;height:9px;
  background:#fff;transform:translate3d(0,-9px,0)}
@media(prefers-reduced-motion:no-preference){
  .hero__scroll__rail::after{animation:scrollCue 2.4s cubic-bezier(.4,0,.2,1) infinite}
}
@keyframes scrollCue{
  0%{transform:translate3d(0,-9px,0)}
  55%{transform:translate3d(0,26px,0)}
  100%{transform:translate3d(0,26px,0)}
}
.hero__showreel{margin-left:auto;display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:#fff;
  background:none;border:0}
.hero__showreel svg{width:10px;height:10px}
@media(max-width:900px){
  /* stays full-height on mobile too. `auto` was only here so the survey card
     could stack under the hero copy; that card is gone in v3.01, and a
     short hero is the opposite of the "blow it up to full screen" ask. */
  .home .hero{min-height:100vh;min-height:100svh;flex-direction:column;align-items:stretch}
  /* .home .hero__in (0,2,1) sets 1.25fr minmax(300px,.75fr) and OUT-SPECIFIES
     a bare .hero__in (0,1,1) even inside this media query, so the homepage
     hero never stacked: the sub and both CTAs sat in a 300px column pushed off
     the right edge and were clipped. Selector matched so the home hero stacks
     like every other page. */
  .hero__in,.home .hero__in{grid-template-columns:1fr;gap:26px;padding-top:clamp(104px,16vh,132px);padding-bottom:36px}
  .home .hero__side{padding-bottom:0}
  .hero__cards{width:100%;flex-direction:row;flex-wrap:wrap}
  .gcard{flex:1 1 240px}
  /* creds flow below the hero content on mobile (no overlap) */
  .hero__creds{position:static;background:rgba(8,18,34,.55);border-top:1px solid var(--ink-line-2)}
  .hero__creds .wrap{padding-block:18px;gap:14px 22px}
  .hero__showreel{margin-left:0}
  /* the cue is a desktop affordance; on touch the creds stack and a fourth row
     would only make the strip taller. */
  .hero__scroll{display:none}
}

/* ============================================================
   TRUST / PARTNER LOGO ROW
   ============================================================ */
.trust{padding-block:var(--sp-5)}
.trust__lab{margin:0 0 0;text-align:center;font-size:12px;font-weight:600;letter-spacing:.05em;
  text-transform:uppercase;color:var(--on-ink-dim);margin:0 0 40px}
.band-paper .trust__lab{color:var(--c-muted)}
.logos{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:clamp(28px,5vw,64px)}
/* partner logos are WHITE-on-transparent assets → show as-is on the dark band */
/* Dave: "The logos under the fold shoudl be much bigger. possibly by 300%."
   ONE ROW — six marks, never wrapped.

   Every asset is a 500x250 canvas, so all six are exactly 2:1 and the row's
   width is 6*2*h + 5*gap. Against the 1280px wrap that caps the height at 93px
   with a 32px gap — 1.6x the old 58px, and the largest that still holds a
   single line. Trying to go bigger is what produced the wrapped 4+2 and then
   the 3+3.

   Sized by COLUMN, not by a fixed px height: six equal tracks with the images
   at width:100% means the row scales with the container and can never wrap,
   at any viewport, without a magic number to re-tune. */
.logos{display:grid;grid-template-columns:repeat(6,1fr);
  gap:clamp(18px,2.2vw,32px);align-items:center;justify-items:center;
  width:100%;margin-inline:auto}
.logos img{width:100%;height:auto;max-height:104px;object-fit:contain;
  filter:none;opacity:.85;transition:opacity .2s}
/* below the desktop wrap a six-across row would shrink the marks past
   legibility, so it steps down — the single-row rule is a desktop rule */
@media(max-width:900px){.logos{grid-template-columns:repeat(3,1fr);gap:24px}}
@media(max-width:520px){.logos{grid-template-columns:repeat(2,1fr);gap:20px}}
.band-paper .logos img{filter:brightness(0);opacity:.55}
.logos img:hover{opacity:1}
/* v2.01: named-program proof line under the logos (trust signals near the top) */
.trust__meta{text-align:center;margin:30px 0 0;font-size:13px;letter-spacing:.03em;color:#8FA3BA}
.trust__meta a{color:#EAF0F7;font-weight:600}
.trust__meta a .ar{color:var(--gold)}

/* ============================================================
   POSITIONING — accent eyebrow + one emphasized word, big statement
   ============================================================ */
.pos__big{font-family:var(--font-display);font-weight:500;
  font-size:clamp(30px,4.4vw,64px);line-height:1.06;letter-spacing:var(--track-display);
  max-width:100%;margin:0}
.pos__big .em{color:var(--c-ink);
  background-image:linear-gradient(var(--c-accent),var(--c-accent));
  background-repeat:no-repeat;background-position:0 94%;background-size:0% 4px;
  transition:background-size .6s var(--ease-out-strong) .55s}
.pos__big.in .em{background-size:100% 4px}
.band-dark .pos__big .em,.band-navy .pos__big .em{color:var(--c-ink);
  background-image:linear-gradient(var(--c-accent),var(--c-accent));
  background-repeat:no-repeat;background-position:0 94%;background-size:0% 4px;
  transition:background-size .6s var(--ease-out-strong) .55s}
.pos__big.in .em{background-size:100% 4px}
.pos__row{display:grid;grid-template-columns:1.5fr 1fr;gap:clamp(36px,6vw,90px);
  align-items:end;margin-top:clamp(36px,5vw,60px)}
.pos__row .lead{max-width:40ch}
@media(max-width:820px){.pos__row{grid-template-columns:1fr;gap:28px}}

/* ============================================================
   SERVICES — type-as-interface hairline list with line-icon squares
   + photographic feature reveal (bento feel)
   ============================================================ */
.svc{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(32px,4vw,64px);align-items:start}
.svc__list{display:flex;flex-direction:column;border-top:1px solid var(--c-border)}
.svc__item{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:20px;
  padding:24px 6px;border-bottom:1px solid var(--c-border);cursor:pointer;
  transition:padding-left .25s var(--ease-out)}
.svc__item:hover{padding-left:14px}
.svc__ico{width:48px;height:48px;border-radius:var(--r-2);display:grid;place-items:center;
  border:1px solid var(--c-border);background:#fff;color:var(--c-primary);transition:background .2s,border-color .2s,color .2s}
.svc__item:hover .svc__ico{background:var(--c-primary);border-color:var(--c-primary);color:#fff}
.svc__ico svg{width:22px;height:22px}
.svc__txt .k{display:block;font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--c-muted);margin-bottom:4px}
.svc__txt .n{display:block;font-family:var(--font-display);font-weight:500;font-size:clamp(19px,1.6vw,24px);letter-spacing:var(--track-display);color:var(--c-ink)}
.svc__arrow{color:var(--c-muted);font-size:22px;transition:transform .25s,color .2s}
.svc__item:hover .svc__arrow{transform:translateX(5px);color:var(--c-muted)}
/* feature tile w/ photo + embedded mini-detail (bento) */
.svc__feature{position:relative;border-radius:var(--r-3);overflow:hidden;min-height:480px;
  box-shadow:var(--shadow-card);display:flex;flex-direction:column;justify-content:flex-end}
.svc__feature .duo{position:absolute;inset:0}
.svc__feat-body{position:relative;z-index:2;padding:clamp(24px,2.4vw,34px);
  background:linear-gradient(transparent,rgba(8,20,38,.30) 30%,rgba(8,20,38,.92))}
.svc__feat-body .k{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--gold-soft)}
.svc__feat-body h3{font-family:var(--font-display);font-weight:500;font-size:clamp(22px,2vw,30px);
  letter-spacing:var(--track-display);color:#fff;margin:10px 0 10px}
.svc__feat-body p{color:#cdd9e6;font-size:14.5px;line-height:1.5;margin:0 0 18px;max-width:38ch}
/* embedded mini-detail chip row */
.minirow{display:flex;gap:10px;flex-wrap:wrap;margin-top:4px}
.chip{display:inline-flex;align-items:center;gap:8px;border-radius:var(--r-pill);
  padding:7px 13px;font-size:12px;font-weight:500;color:#fff;
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.18)}
.chip i{width:6px;height:6px;border-radius:50%;background:var(--gold);display:inline-block}
@media(max-width:880px){.svc{grid-template-columns:1fr}.svc__feature{min-height:360px}}

/* ============================================================
   PROCESS — numbered horizontal stepper, glowing center node
   ============================================================ */
/* #process uses the shared dark-band recipe. It previously carried a hidden
   greyscale aerial photo plus its own radial overlay — a fourth dark treatment
   nobody had decided on, and the reason this band alone reported a gradient. */
.proc__track{position:relative;display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(16px,2vw,28px);margin-top:clamp(40px,5vw,64px)}
.proc__track::before{content:"";position:absolute;top:30px;left:30px;right:calc(25% - 30px);height:2px;
  background:linear-gradient(90deg,rgba(221,166,62,.55),var(--ink-line));z-index:0}
.step{position:relative;z-index:1;text-align:left}
.step__node{width:60px;height:60px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--font-display);font-weight:500;font-size:19px;color:#fff;
  background:var(--ink-2);border:1px solid var(--ink-line);margin-bottom:22px;
  transition:transform .3s var(--ease-out)}
.step:first-child .step__node{border-color:var(--gold);color:var(--gold-soft);
  box-shadow:0 0 0 5px rgba(221,166,62,.12)}
.step:hover .step__node{transform:translateY(-3px)}
.step .k{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--gold-soft);margin-bottom:8px}
.step h3{font-family:var(--font-display);font-weight:500;font-size:clamp(18px,1.4vw,22px);
  letter-spacing:var(--track-display);color:#fff;margin:0 0 8px}
.step p{font-size:14px;line-height:1.5;color:var(--on-ink-dim);margin:0}
.proc__note{margin-top:clamp(32px,4vw,48px);font-size:13px;color:var(--on-ink-dim);
  display:flex;align-items:center;gap:10px}
.proc__note::before{content:"";width:26px;height:1px;background:var(--ink-line)}
@media(max-width:820px){
  .proc__track{grid-template-columns:1fr 1fr;gap:36px 20px}
  .proc__track::before{display:none}
}
@media(max-width:480px){.proc__track{grid-template-columns:1fr}}

/* ============================================================
   PROOF STAT BAND — navy color block, real facts, vertical rules
   ============================================================ */
@media(max-width:820px){
}

/* ============================================================
   WORK — cinematic project cards, one overlapping framed treatment
   ============================================================ */
.work__grid{display:grid;grid-template-columns:1.45fr 1fr;grid-template-rows:1fr 1fr;gap:clamp(16px,2vw,24px)}
.pcard{position:relative;display:block;overflow:hidden;border-radius:var(--r-3);
  box-shadow:var(--shadow-card);min-height:300px}
.pcard--lead{grid-row:span 2;min-height:480px}
.pcard--lead .pcard__t{font-size:clamp(24px,2.2vw,34px)}
.pcard .duo{position:absolute;inset:0}
.pcard .duo>img{transition:transform .9s var(--ease-out-strong)}
.pcard:hover .duo>img{transform:scale(1.06)}
.pcard__body{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:clamp(22px,2vw,30px);
  background:linear-gradient(transparent,rgba(8,20,38,.45) 36%,rgba(8,20,38,.95))}
.pcard__k{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--gold-soft)}
.pcard__t{display:block;font-family:var(--font-display);font-weight:500;font-size:clamp(20px,1.7vw,27px);
  line-height:1.1;letter-spacing:var(--track-display);color:#fff;margin:10px 0 14px}
.pcard .tlink{color:#fff}
.pcard .tlink .ar{color:var(--gold)}
@media(max-width:960px){.work__grid{grid-template-columns:1fr 1fr;grid-template-rows:auto}.pcard--lead{grid-row:auto;grid-column:span 2}}
@media(max-width:620px){.work__grid{grid-template-columns:1fr}.pcard--lead{grid-row:auto;grid-column:span 1}.pcard,.pcard--lead{min-height:340px}}

/* ============================================================
   FOUNDERS / WHY SOAR — photo + divided value list
   ============================================================ */
/* ── the Who We Are dead space ──────────────────────────────────────────────
   Dave: "that white space is pretty bare. The client wants to know if you can
   implement either topography lines, or mountain range lines (fairly
   transparent - just something to fill up the space more)."

   Topography, not a mountain range: the contour field is already this site's
   motif (statement band, dark bands, footer), so it fills the space without
   introducing a new decorative idea. Sits BEHIND the content, keyed to the
   bottom of the section where the emptiness actually is, and masked so it
   fades in from nothing rather than starting on an edge. */
#about{isolation:isolate}
#about::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:72%;
  z-index:0;pointer-events:none;
  background-image:url("../assets/topo-tile.svg");
  background-repeat:repeat;background-size:58% auto;background-position:62% 108%;
  /* .05 was too polite to actually fill anything — the dead zone still read as
     blank. .085 with a denser tile puts visible contour in the empty area
     while staying well under the statement band's own texture. */
  opacity:.085;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 30%,#000 72%);
          mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 30%,#000 72%)}
#about > *{position:relative;z-index:1}

.found{display:grid;grid-template-columns:.95fr 1.05fr;gap:clamp(56px,6.5vw,110px);align-items:stretch}
.found__media{position:relative}
.found__media{display:flex}
.found__media .duo{border-radius:var(--radius);aspect-ratio:auto;width:100%;min-height:360px}
.found__media .duo>img{width:100%;height:100%;object-fit:cover}
.found__media .duo>img{filter:grayscale(.32) contrast(1.02)}
/* the badge rides the photo — absolute, so it never joins the flex flow
   (unstyled it squeezed the image into a strip; that is what broke) */
.found__badge{position:absolute;left:18px;bottom:18px;z-index:2;
  background:var(--c-primary);color:#fff;border-radius:var(--radius);
  padding:16px 20px;max-width:230px;
  box-shadow:0 18px 40px -20px rgba(6,19,31,.5)}
.found__badge b{display:block;font-family:var(--font-display);font-weight:600;
  font-variation-settings:"wdth" var(--wd-display);
  font-size:19px;letter-spacing:-.01em;color:#fff;margin:0 0 5px}
.found__badge span{font-size:13.5px;line-height:1.5;color:#C9D3E0}
.found__vals{display:flex;flex-direction:column;margin-top:var(--sp-5);border-top:1px solid var(--c-border)}
.vrow{display:grid;grid-template-columns:auto 1fr;gap:18px;padding:22px 4px;border-bottom:1px solid var(--c-border)}
.vrow .vn{font-family:var(--font-display);font-weight:500;font-size:14px;color:var(--c-muted);letter-spacing:.08em}
.vrow h4{font-family:var(--font-display);font-weight:600;font-size:19px;letter-spacing:-.01em;color:var(--c-ink);margin:0 0 7px}
.vrow p{font-size:14.5px;line-height:1.55;color:var(--c-muted);margin:0;max-width:50ch}
/* ISSUE #1 ROOT CAUSE. The homepage measured a constant 544px of horizontal
   overflow at every width below 860, and it was neither the mega-menu panels
   (the original guess) nor a grid-specificity conflict (the second guess).

   .found__media is a GRID ITEM, so it carries the default min-width:auto and
   will not shrink below its content. Below 860 its .duo child takes
   aspect-ratio:16/11 with no definite width, so the ratio resolves width FROM
   height — and the row is stretched tall by the text column beside it. 361px
   of height x 16/11 = 524px of width, + the 20px gutter = the 544 that has
   been in the bug report since August.

   min-width:0 lets the item shrink; the ratio then resolves height from width,
   which is the intended direction. width:100% on .duo alone does nothing —
   the item, not the child, is what refuses to shrink. */
@media(max-width:860px){.found{grid-template-columns:1fr;gap:36px}
  .found__media{min-width:0}
  .found__media .duo{aspect-ratio:16/11}}

/* ============================================================
   FAQ accordion
   ============================================================ */
.faq{max-width:840px}
.faq__item{border-bottom:1px solid var(--c-border)}
.faq__q{width:100%;display:flex;align-items:center;justify-content:space-between;gap:20px;
  background:none;border:0;cursor:pointer;text-align:left;padding:24px 4px;
  font-family:var(--font-display);font-weight:600;font-size:clamp(17px,1.4vw,21px);
  letter-spacing:var(--track-display);color:var(--c-ink)}
.faq__q .pm{flex:none;width:26px;height:26px;border-radius:50%;border:1px solid var(--c-border);
  display:grid;place-items:center;color:var(--c-muted);font-size:18px;transition:transform .3s,background .2s,color .2s}
.faq__item.open .faq__q .pm{background:var(--green);color:#fff;transform:rotate(45deg)}
.faq__a{max-height:0;overflow:hidden;transition:max-height .35s var(--ease-out)}
.faq__a p{margin:0 4px 24px;color:var(--c-muted);font-size:15.5px;line-height:1.6;max-width:64ch}

/* ============================================================
   CLOSING CTA — cinematic dark banner over image
   ============================================================ */
.cta{position:relative;overflow:hidden}
.cta__bg{position:absolute;inset:0;z-index:0}
.cta__bg img{width:100%;height:100%;object-fit:cover}
.cta__bg::after{content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,rgba(10,27,49,.86),rgba(10,27,49,.55))}
.cta__in{position:relative;z-index:2;padding-block:clamp(90px,11vw,150px);text-align:center}
.cta h2{font-family:var(--font-display);font-weight:500;font-size:clamp(36px,5.2vw,80px);
  letter-spacing:var(--track-display);line-height:1;color:#fff;max-width:16ch;margin:0 auto}
.cta p{color:#cdd9e6;max-width:48ch;margin:20px auto 0;line-height:1.55}
.cta .actions{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:34px}

/* ============================================================
   FOOTER — solid hairline above meta, offices row, logo
   (cool / navy palette; gold reserved for the single CTA button)
   ============================================================ */
.foot{position:relative;isolation:isolate;color:var(--on-ink-dim);
  padding-top:clamp(72px,9vw,112px);padding-bottom:30px;
  background:var(--c-primary-strong)}
.foot::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;z-index:2;background:rgba(255,255,255,.10)}
/* footer: SAME wavy contour texture as the navy bands, just larger/sparser and
   faded from the bottom-right, so it reads as a variation, not an identical copy */
.home .foot .wrap{position:relative;z-index:1}
.foot__top{display:grid;grid-template-columns:1.45fr 1fr 1fr 1fr;gap:56px clamp(40px,4.5vw,72px)}
.foot__logo img{height:60px}
.foot__tag{margin:22px 0;font-size:15px;line-height:1.6;color:#cfdae8;max-width:34ch}
.foot__actions{display:flex;align-items:center;gap:22px;flex-wrap:wrap;margin-bottom:22px}
.foot__tel{font-family:var(--font-display);font-weight:600;font-variation-settings:"wdth" var(--wd-display);font-size:19px;color:#fff;letter-spacing:-.01em}
.foot__tel:hover{color:#cfe0f5}
.foot__badges{display:flex;gap:8px;flex-wrap:wrap}
.foot__col{display:flex;flex-direction:column}
.foot__col .eyebrow{color:#cfdae8;margin:0 0 16px;font-variation-settings:"wdth" var(--wd-display)}
.foot__col a{display:block;width:fit-content;font-size:14.5px;color:var(--on-ink-dim);
  padding:7px 0;transition:color .15s;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:0 100%;background-size:0% 1px;
  transition:color .15s,background-size .25s var(--ease-out)}
.foot__col a:hover{color:#fff;background-size:100% 1px}
.foot__offices{display:block;font-size:13.5px;line-height:1.7;color:#cfdae8}
.foot__bottom{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-top:clamp(48px,5.6vw,76px);padding-top:28px;padding-bottom:6px;border-top:1px solid var(--ink-line);font-size:13px;color:var(--on-ink-dim)}
.foot__totop{display:inline-flex;align-items:center;gap:7px;color:var(--on-ink-dim)}
.foot__totop:hover{color:#fff}.foot__totop svg{width:12px;height:12px}
.foot__social{margin-left:auto;display:flex;gap:10px}
.foot__social a{width:36px;height:36px;border-radius:var(--radius);border:1px solid var(--ink-line);display:flex;align-items:center;justify-content:center;color:var(--on-ink-dim);transition:.18s}
.foot__social a:hover{color:#fff;background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.42)}
@media(max-width:880px){.foot__top{grid-template-columns:1fr 1fr;gap:40px 30px}}
@media(max-width:520px){.foot__top{grid-template-columns:1fr}}

/* mobile nav — keep CTA + burger from crowding the bar */
@media(max-width:600px){
  .nav__right{gap:12px}
  .nav .btn--primary{padding:9px 14px;font-size:13px}
  /* primary mobile control: hold a 44px touch target, pull the optical
     edge back so the icon still lines up with the nav gutter. */
  .nav__burger{min-width:44px;min-height:44px;padding:0;margin:0 -12px 0 0;
    align-items:center;justify-content:center}
}

/* ============================================================
   REVEAL motion
   ============================================================ */
.rev{opacity:0;transform:translateY(18px);transition:opacity .8s var(--ease-out),transform .8s var(--ease-out)}
.rev.in{opacity:1;transform:none}
.rev.d1{transition-delay:.08s}.rev.d2{transition-delay:.16s}.rev.d3{transition-delay:.24s}
@media(prefers-reduced-motion:reduce){
  .rev{opacity:1;transform:none;transition:none}
  .hero__media video{display:none}
  html{scroll-behavior:auto}
}

/* ============================================================
   v2.02 HERO CARD - interactive survey cross-section (ground/air/water
   scenarios; gold pulse marks the active domain; drone rides the path)
   ============================================================ */
.hero__card{position:relative;justify-self:end;align-self:end;width:min(100%,344px);border-radius:var(--radius);
  padding:20px 22px 18px;background:rgba(8,19,34,.46);border:1px solid rgba(255,255,255,.18);
  box-shadow:var(--shadow-glass),inset 0 1px 0 rgba(255,255,255,.10);overflow:hidden;isolation:isolate}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero__card{-webkit-backdrop-filter:blur(18px) saturate(140%);backdrop-filter:blur(18px) saturate(140%)}}
.hc__lab{font-family:var(--font-body);font-weight:600;font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;color:#B9C7D8;display:block;margin-bottom:10px}
.hc__scene{display:block;width:100%;height:auto}
.hc__scene [data-gz]{cursor:pointer;outline:none}
.hc__scene .zl{font-family:var(--font-body);font-weight:600;font-size:9.5px;letter-spacing:.05em;fill:#8FA3BA;text-transform:uppercase;transition:fill .25s}
.hero__card[data-zone="air"] .zl--air,.hero__card[data-zone="ground"] .zl--ground,.hero__card[data-zone="water"] .zl--water{fill:#fff}
.hc__scene .zanchor{fill:#9FB1C6;transition:fill .2s}
.hc__scene .zanchor:hover{fill:#DCE6F2}
.hc__scene .zdot{fill:var(--gold);opacity:0;transition:opacity .3s}
.hc__scene .zpulse{fill:none;stroke:var(--gold);stroke-width:1.2;opacity:0;transform-box:fill-box;transform-origin:center}
.hero__card[data-zone="air"] .zdot--air,.hero__card[data-zone="ground"] .zdot--ground,.hero__card[data-zone="water"] .zdot--water{opacity:1}
.hc__scene .drone{transform:translate(200px,34px)}
@supports (offset-path:path("M0,0 L1,1")){
  .hc__scene .drone{transform:none;offset-path:path("M12,58 Q150,16 288,46");offset-rotate:0deg;offset-distance:67%}
  @media(prefers-reduced-motion:no-preference){
    .hc__scene .drone{animation:hcdrift 12s ease-in-out infinite alternate}
  }
}
@keyframes hcdrift{from{offset-distance:70%}to{offset-distance:40%}}
@media(prefers-reduced-motion:no-preference){
  .hero__card[data-zone="air"] .zpulse--air,
  .hero__card[data-zone="ground"] .zpulse--ground,
  .hero__card[data-zone="water"] .zpulse--water{animation:hcpulse 2.8s var(--ease-out) infinite}
}
@keyframes hcpulse{0%{transform:scale(.35);opacity:.75}70%,100%{transform:scale(1.2);opacity:0}}
.hc__cap{margin-top:12px;padding-top:14px;border-top:1px solid rgba(255,255,255,.12)}
.hc__cap b{display:block;font-family:var(--font-display);font-weight:600;font-size:15.5px;color:#fff;letter-spacing:-.01em}
.hc__cap span{display:block;font-size:12.5px;color:#c4d0de;margin-top:2px}
@media(max-width:900px){.hero__card{display:none}}

/* ============================================================
   ENTRANCE motion — v3.01 (homepage only)
   One easing family, several devices, applied by editorial role:
   - headlines: masked line-rise (.hl/.hl__t spans) + the rule draws after
   - hero: a load sequence (pure CSS — runs with no JS at all)
   - data (record, logos, steps): short staggered cascades
   - photos: settle from a slight zoom inside their own frame
   - plate (insights): steps out from behind the image after it lands
   Mosaic, rail, FAQ and footer are deliberately NOT animated: the first
   two already move on scroll timelines, the last two are utility.
   Stagger keyframes omit `to{}` on purpose — they resolve to each
   element's own resting style (logos rest at .85, not 1), and with
   `backwards` fill the animation releases afterwards so hover
   transitions keep working.
   ============================================================ */
/* structural — the mask spans replace <br>, so block display must hold
   even under reduced motion */
.home .hl{display:block}
.home .hl__t{display:block}

@media(prefers-reduced-motion:no-preference){
  /* 120% of the line's OWN box: a headline unit that wraps internally
     (the hero's first line does at ~1440) still parks fully below its
     clip window, slack included */
  @keyframes entLine{from{transform:translate3d(0,120%,0)}}
  @keyframes entLineFade{from{transform:translate3d(0,120%,0);opacity:0}}
  @keyframes entRise{from{opacity:0;transform:translate3d(0,16px,0)}}
  @keyframes entFade{from{opacity:0}}
  @keyframes chartDraw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}

  /* the mask window: negative insets leave room for ascenders/descenders
     at line-height .93 while still hiding the line parked below */
  .home .hl{clip-path:inset(-0.12em -0.15em -0.14em -0.15em)}

  /* ── hero load sequence — GATED ON THE VIDEO (v3.02) ─────────────────
     Jon: "Integrate a fade in animation for content appearing after the
     initial video. Ensure the logo and text display timing aligns with the
     background video sequence … transparent to fully opaque within 2-3
     seconds of the site loading."

     Two changes from v3.01. The ladder now starts when the VIDEO does, not
     when the document parses; and it is stretched so the last element lands
     at ~2.05s after that start, inside Jon's 2-3s window (v3.01 finished at
     1.6s, tied to page load). The NAV LOGO joins the ladder — he asked for
     "the logo and text display timing", and in v3.01 the logo never animated.

     ⚠️ THE FAILURE MODE THIS MUST NOT HAVE: gate the hero on a media event and
     a video that never loads leaves the headline invisible forever. So the
     hidden state is applied ONLY by script (no JS = no hiding, content is
     simply there), and `go` fires on whichever of these comes first:
     playing / loadeddata / an 800ms timer / window load. Four ways out. */
  .hero-wait .home .nav__in,
  .hero-wait .home .hero__lede .eyebrow,
  .hero-wait .home .hero h1 .hl__t,
  .hero-wait .home .hero__side,
  .hero-wait .home .hero__creds{opacity:0}

  /* the WHOLE header fades as one. Gating only the logo left the nav links and
     the CTA painted while the logo was still hidden — which reads as a broken
     header for the first second, not as a designed sequence. Jon's words are
     "the logo AND TEXT display timing", so the header arrives together. */
  .hero-go .home .nav__in{animation:entFade .7s ease .05s both}
  .hero-go .home .hero__lede .eyebrow{animation:entRise .8s var(--ease-out) .15s both}
  .hero-go .home .hero h1 .hl__t{animation:entLineFade 1s var(--ease-out-strong) .3s both}
  .hero-go .home .hero h1 .hl:nth-child(2) .hl__t{animation-delay:.5s}
  .hero-go .home .hero__side{animation:entRise .85s var(--ease-out) .72s both}
  .hero-go .home .hero__creds{animation:entFade .8s ease .95s both}

  /* ── headline line-rise on reveal ── */
  /* a headline that carries mask spans hands its travel to the lines:
     the container only does a quick fade */
  .home .rev:has(.hl){transform:none;transition-duration:.4s}
  .home .rev .hl__t{transform:translate3d(0,120%,0);
    transition:transform .85s var(--ease-out-strong)}
  .home .rev .hl:nth-child(2) .hl__t{transition-delay:.12s}
  .home .rev.in .hl__t{transform:none}
  /* the short rule draws once the lines have landed */
  .home .rev.rule-under::after{transform:scaleX(0);transform-origin:left;
    transition:transform .7s var(--ease-out-strong) .55s}
  .home .rev.rule-under.in::after{transform:scaleX(1)}

  /* ── statement: same move as body copy, but slower and deeper —
       the em-underline draw (already delayed .55s) finishes it.
       The parked offset lives on :not(.in): this selector outranks
       `.rev.in`, so an unconditional transform here would never release */
  .home .pos__big.rev{transition-duration:1s}
  .home .pos__big.rev:not(.in){transform:translateY(26px)}

  /* ── trust logos: opacity-only stagger (no travel — different texture) ── */
  .home .trust .rev.in .logos img{animation:entFade .6s ease backwards}
  .home .trust .rev.in .logos img:nth-child(2){animation-delay:.06s}
  .home .trust .rev.in .logos img:nth-child(3){animation-delay:.12s}
  .home .trust .rev.in .logos img:nth-child(4){animation-delay:.18s}
  .home .trust .rev.in .logos img:nth-child(5){animation-delay:.24s}
  .home .trust .rev.in .logos img:nth-child(6){animation-delay:.30s}

  /* ── growth panel: the slab rises as before, then the chart DRAWS ──
       Jon: "Ensure the visualization provides clear movement and visual
       interest." The movement is the draw, not a fade — a line that arrives
       already complete is the static shot he is reacting against.

       `pathLength="1"` normalises each polyline to a length of 1 regardless of
       its real geometry, so one dasharray value drives both series and the
       data can be swapped for SOAR's real numbers without retuning the motion.

       Everything here is additive: the default state further up is the FINISHED
       chart (dashoffset 0, dots opaque). With reduced motion on, none of this
       block applies and the chart is simply present and readable. */
  .home .growth.rev.in .growth__head{animation:entRise .6s var(--ease-out) .12s backwards}

  .home .growth.rev.in .chart__grid line{animation:entFade .5s ease .18s backwards}
  .home .growth.rev.in .chart__tick text{animation:entFade .6s ease .26s backwards}

  .home .growth.rev.in .chart__line{
    stroke-dasharray:1;animation:chartDraw 2.8s var(--ease-out) .34s backwards}
  .home .growth.rev.in .chart__line--a{animation-delay:.62s}
  .home .growth.rev.in .chart__end{animation:entFade .5s ease backwards}
  .home .growth.rev.in .chart__end--b{animation-delay:3.14s}
  .home .growth.rev.in .chart__end--a{animation-delay:3.42s}
  .home .growth.rev.in .chart__key{animation:entRise .6s var(--ease-out) 3.5s backwards}

  .home .growth.rev.in .growth__lead{animation:entRise .6s var(--ease-out) .3s backwards}
  .home .growth.rev.in .growth__row{animation:entRise .55s var(--ease-out) backwards}
  .home .growth.rev.in .growth__row:nth-child(1){animation-delay:.46s}
  .home .growth.rev.in .growth__row:nth-child(2){animation-delay:.56s}

  /* ── process: the four steps arrive in order (it IS a sequence);
       the container keeps only a quick fade ── */
  .home .proc__track.rev{transform:none;transition-duration:.45s}
  .home .proc__track.rev.in .step{animation:entRise .65s var(--ease-out) backwards}
  .home .proc__track.rev.in .step:nth-child(2){animation-delay:.1s}
  .home .proc__track.rev.in .step:nth-child(3){animation-delay:.2s}
  .home .proc__track.rev.in .step:nth-child(4){animation-delay:.3s}

  /* ── about photo: settles from a slight zoom inside its frame (.duo is
       overflow:hidden); the badge follows once the photo has landed ── */
  .home .found__media.rev .duo>img{transform:scale(1.05);
    transition:transform 1.2s var(--ease-out)}
  .home .found__media.rev.in .duo>img{transform:none}
  .home .found__media.rev .found__badge{opacity:0;transform:translateY(12px);
    transition:opacity .6s ease .45s,transform .6s var(--ease-out) .45s}
  .home .found__media.rev.in .found__badge{opacity:1;transform:none}


  /* ── CTA background: scroll-scrubbed push-in (the DJI/Apple grammar —
       the camera drives INTO the scene as you arrive). The keyframe track
       overshoots to 1.14 at 82% then settles to 1.10 — a subtle bounce
       ending. Range ends at cover 45% so the settle is always REACHABLE
       before the page runs out of scroll (the CTA sits above only the
       footer; a longer range would freeze mid-zoom at max scroll).
       Transform only — the scroll-timeline hard rule. ── */
  @supports (animation-timeline:view()){
    @keyframes ctaPush{0%{transform:scale(1)}82%{transform:scale(1.14)}100%{transform:scale(1.10)}}
    /* the timeline lives on the SECTION, not the img: .cta{overflow:hidden}
       makes the section a scroll container, so a view() declared on the img
       would resolve against .cta itself (where the img never moves) and pin
       at 100%. Named timelines are visible to descendants. */
    .home .cta{view-timeline-name:--ctapush}
    .home .cta__bg img{
      animation:ctaPush linear both;
      animation-timeline:--ctapush;
      animation-range:entry 0% cover 45%}
  }
}

/* phones: the hero's two buttons stack at one full width instead of two
   ragged widths on two rows */
@media(max-width:560px){
  .svcpage .hero .actions .btn,.svcpage .ahero__actions .btn{flex:1 1 100%;justify-content:center}
}

/* a heading with authored lines is already measured by its lines; the 20ch
   cap only broke those lines again once the type reached full size */
.svcpage .shead .h2:has(.hl + .hl){max-width:none}
@media(min-width:761px){
  .svcpage .shead:not(.shead--wide):has(> div > .h2 .hl + .hl){grid-template-columns:minmax(0,1.32fr) minmax(0,.88fr)}
}

/* phones --------------------------------------------------------------- */
/* breadcrumb items wrap whole, never mid-label */
.svcpage .crumb{flex-wrap:wrap;row-gap:4px}
.svcpage .crumb a,.svcpage .crumb .here{white-space:nowrap}
@media(max-width:700px){
  /* the diagonal scrim leaves the right of a narrow hero bare, and on a
     phone the copy runs the full width over it; an even, soft wash instead */
  .svcpage .hero__scrim{background:
    linear-gradient(0deg,rgba(6,13,26,.92) 0%,rgba(7,16,30,.78) 45%,rgba(7,16,30,.64) 100%)}
}
@media(max-width:860px){
  /* the lead quote already closes on a rule; the first short quote does not
     need a second one under it */
  .svcpage .qsub .q:first-child{border-top:0;margin-top:0}
}
@media(max-width:860px){.svcpage .qsub .q:first-child{padding-top:0}}
/* small phones: the nav button's label wrapped to two lines at 375px */
@media(max-width:420px){.nav__right .btn{white-space:nowrap;padding-inline:14px;font-size:13.5px}}

/* footer Offices column — the four offices with the line each one answers */
.foff{margin:0 0 18px}
.foff:last-child{margin-bottom:0}
.foff b{display:block;font-size:14px;font-weight:600;color:#fff;letter-spacing:.01em}
.foff span{display:block;margin-top:3px;font-size:13px;line-height:1.55;color:var(--on-ink-dim)}
.foff a{margin-top:3px;font-size:13.5px}
@media(min-width:881px){.foot__top{grid-template-columns:1.35fr 1fr 1.15fr 1fr}}

/* testimonials: the same Valued Partners component the inner pages use,
   moved here so the home page can carry it too */
.quotes{margin-top:clamp(28px,3.2vw,44px)}
/* attribution sits BESIDE the pull-quote, bottom-aligned: at a 46ch display
   measure the quote stops around two thirds of the wrap, and stacking the
   name under it left the whole right third of the band empty */
/* The attribution used to sit in its own narrow right-hand column, bottom
   aligned — which stranded it in ~460px of dead space beside a quote that
   ended well short of it. It now follows the quote directly, the way an
   attribution reads on the page, and the quote keeps the width. */
.qlead{margin:0;padding-bottom:clamp(26px,3vw,40px);border-bottom:1px solid var(--ink-line)}
.qlead .qwho{margin-top:clamp(20px,2.2vw,28px)}
.qlead blockquote{margin:0;color:#fff;
  font-family:var(--font-display);font-variation-settings:"wdth" var(--wd-display);
  font-weight:500;font-size:clamp(19px,1.9vw,25px);line-height:1.45;
  letter-spacing:var(--track-display);max-width:46ch}
@media(max-width:760px){.qlead{grid-template-columns:1fr;gap:22px;align-items:start}}
.qsub{display:grid;grid-template-columns:repeat(3,1fr);gap:0 clamp(26px,3.2vw,52px);
  margin-top:clamp(24px,2.8vw,38px)}
/* flex column with the attribution pushed to the bottom, so all three names
   land on one baseline however long the quotes run */
.qsub .q{margin:0;display:flex;flex-direction:column;
  padding-right:clamp(26px,3.2vw,52px);border-right:1px solid var(--ink-line)}
.qsub .q .qwho{margin-top:auto;padding-top:4px}
.qsub .q:last-child{border-right:0;padding-right:0}
.qsub blockquote{margin:0 0 18px;color:#dbe5f1;font-size:15px;line-height:1.6}
/* attribution: monogram + name, flat — no container around the container */
.qwho{display:flex;align-items:center;gap:13px}
/* Two faults, both measured: the glyphs sat 1.06px high because the 13px type
   inherited a 21px line box and the caps rode the top of it, and the outlined
   40px box read as a hard little square. line-height:1 lets place-items
   actually centre it; text-indent cancels the trailing letter-space so the
   ink is centred, not the advance width; and a soft fill with no outline
   sits easier than a bordered box at the 2px house radius. */
/* caps sit above the baseline, so a line box centred by the grid still leaves
   the ink riding high; the 2px top padding pushes the content box down 1px,
   which is what actually centres the letterforms. */
.qmono{flex:none;width:44px;height:44px;display:grid;place-items:center;line-height:1;padding-top:2px;
  background:rgba(255,255,255,.07);border-radius:var(--radius);
  font-family:var(--font-display);font-variation-settings:"wdth" var(--wd-label);
  font-weight:600;font-size:13px;letter-spacing:.04em;text-indent:.04em;color:#fff}
.qn{display:block;font-family:var(--font-display);
  font-variation-settings:"wdth" var(--wd-display);font-weight:500;font-size:15.5px;
  color:#fff;letter-spacing:var(--track-display);line-height:1.2}
.qr{display:block;font-size:12.5px;color:var(--on-ink-dim);margin-top:4px}
@media(max-width:860px){
  .qsub{grid-template-columns:1fr;gap:0}
  .qsub .q{padding-right:0;border-right:0;padding-top:24px;margin-top:24px;
    border-top:1px solid var(--ink-line)}
}
.nav__right .btn{white-space:nowrap}

/* partner logos as a slow, continuous scroll — the client's current site runs
   the same strip as a carousel. Two identical sets translate by exactly half
   the track, so the loop has no seam; hover and reduced motion stop it. */
.logorail{overflow:hidden;margin-top:clamp(20px,2.4vw,30px);
  -webkit-mask-image:linear-gradient(to right,transparent,#000 8%,#000 92%,transparent);
          mask-image:linear-gradient(to right,transparent,#000 8%,#000 92%,transparent)}
.logorail__t{display:flex;align-items:center;gap:clamp(34px,5vw,80px);width:max-content;
  animation:logodrift 46s linear infinite}
.logorail:hover .logorail__t{animation-play-state:paused}
.logorail img{height:clamp(34px,4.4vw,58px);width:auto;flex:none;object-fit:contain;
  opacity:.72;transition:opacity .25s ease}
.logorail img:hover{opacity:1}
@keyframes logodrift{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@media(prefers-reduced-motion:reduce){.logorail__t{animation:none;flex-wrap:wrap;width:auto;justify-content:center}}
.stmt__hook{margin:0 0 14px;font-size:clamp(18px,1.7vw,22px);font-weight:600;line-height:1.3;
  letter-spacing:var(--tracking-display);color:var(--c-ink)}
/* hero lede over a bright photo: a soft dark halo so the line holds */
.hero .sub.hero__sub--lift{text-shadow:0 1px 2px rgba(4,10,22,.95),0 2px 6px rgba(4,10,22,.9),0 0 22px rgba(4,10,22,.9),0 0 44px rgba(4,10,22,.65)}
.home #partners{background:var(--c-primary)}

/* ---- Logo (client color logo, Oct 14) + nav fit at mid widths ---- */
@media(min-width:961px){.nav__links .navlink{font-size:15px}}
.nav__logo img{height:64px;width:auto;transition:height .26s}
.nav.fixed .nav__logo img{height:50px}
.nav__logo{flex:none}.nav__logo img{max-width:none}
.nav__in:has(.nav__logo){padding-block:10px}
.nav.fixed .nav__in:has(.nav__logo){padding-block:8px}
@media(min-width:961px) and (max-width:1260px){.nav__logo img{height:52px}.nav.fixed .nav__logo img{height:44px}.nav__in{gap:22px}.nav__links{gap:20px}.nav__links .navlink{font-size:15px;white-space:nowrap}}
@media(min-width:961px) and (max-width:1100px){.nav__logo img{height:46px}.nav.fixed .nav__logo img{height:42px}.nav__in{gap:16px}.nav__links{gap:14px}.nav__links .navlink{font-size:14px}}
@media(min-width:961px) and (max-width:1040px){.nav__logo img,.nav.fixed .nav__logo img{height:40px}.nav__in{gap:14px}.nav__links{gap:12px}.nav__links .navlink{font-size:13.5px}.nav__right .btn{padding-inline:14px}}
/* the color logo needs a near-solid navy bar once the page scrolls under it */
.nav.fixed{background:rgba(10,27,49,.94)}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.nav.fixed{background:rgba(10,27,49,.92)}}
@media(max-width:600px){.nav__logo img{height:44px}.nav.fixed .nav__logo img{height:40px}}

/* CEQA page: photo break between the intro and the detail block */
.ovband{margin:clamp(40px,5vw,72px) 0;aspect-ratio:21/8;overflow:hidden;border-radius:var(--r-card,0)}
.ovband img{width:100%;height:100%;object-fit:cover;display:block}
@media(max-width:700px){.ovband{aspect-ratio:4/3}}
/* hero paragraph: full white, medium weight, and a soft dark pool behind it so it holds on bright video frames */
.home .hero .sub.hero__sub--lift{color:#fff;font-weight:500;position:relative;isolation:isolate}
.home .hero .sub.hero__sub--lift::before{content:"";position:absolute;inset:-36px -56px;z-index:-1;pointer-events:none;
  background:radial-gradient(closest-side,rgba(4,10,22,.55),rgba(4,10,22,.3) 60%,transparent)}

/* Oct 16 call: no contour circles on the navy growth panel */
.growth::after{content:none}
/* Projects: Engage With Us video above the project list */
.pvid{margin:0 0 clamp(40px,4.6vw,64px);aspect-ratio:16/9;border-radius:var(--radius);overflow:hidden;background:var(--ink)}
.pvid iframe{width:100%;height:100%;border:0;display:block}

/* ===================== Responsive QA, Oct 21 (mobile + tablet) =====================
   Each block below is owned by one QA pass. Rules here must not change >=1200px. */
/* == QA:global == */
/* phone header: logo + CTA + burger needed 389px and nothing could shrink, so the burger fell off-screen below 390.
   The logo now gives way at its own ratio, the gaps tighten, and the CTA is a full 44px target (scrolled bar too). */
@media(max-width:600px){
  .nav__in{gap:12px}
  .nav__logo{flex:0 1 auto;min-width:0}
  .nav__logo img,.nav.fixed .nav__logo img{height:auto;max-height:44px;max-width:100%;transition:max-height .26s}
  .nav.fixed .nav__logo img{max-height:40px}
  .nav__right{flex:none;gap:6px}
  .nav__right .btn,.nav.fixed .nav__right .btn{min-height:44px;padding:10px 14px}
}
@media(max-width:359px){
  .nav__in,.nav.fixed .nav__in{padding-inline:16px}
  .nav__right .btn,.nav.fixed .nav__right .btn{padding-inline:10px;font-size:12.5px}
}
/* tablets + landscape phones: the burger is the only way into the menu and was a 36x26 target */
@media(min-width:601px) and (max-width:960px){.nav__burger{min-width:44px;min-height:44px;padding:0;margin-right:-12px;align-items:center;justify-content:center}}
/* tablet portrait: the 64px logo made the bar 85px against 67px on phones and 69px on landscape tablets; 52px (the
   961-1260 size) lands it near 73px. Before the landscape-phone rule below, which must still win at <=500px tall */
@media(min-width:601px) and (max-width:960px){.nav__logo img{height:52px}.nav.fixed .nav__logo img{height:44px}}
/* landscape phones fall in the tablet range: the 64px logo made the bar ~23% of a 375px-tall screen */
@media(min-width:601px) and (max-width:960px) and (max-height:500px){.nav__logo img{height:44px}.nav.fixed .nav__logo img{height:40px}}
/* drawer sub-lists (built by /js/navfix.js): the three menu buttons expand their links inline instead of
   opening the desktop mega panel behind the drawer */
@media(min-width:961px){.msub{display:none}}
@media(max-width:960px){
  .nav__links{overscroll-behavior:contain}
  .nav__links .navlink[data-menu]::after{display:none}
  .nav__links .navlink[aria-expanded="true"]{border-bottom-color:transparent}
  .nav__links>.navlink:has(+ .msub:last-child[hidden]){border-bottom:0}
  .msub:not([hidden]){display:flex;flex-direction:column;padding:0 0 12px 16px;border-bottom:1px solid var(--ink-line-2)}
  .nav__links>.msub:last-child{border-bottom:0;padding-bottom:0}
  .nav__links .msub a{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:0;font-size:16px;font-weight:400;line-height:1.3;color:var(--on-ink-dim)}
  .nav__links .msub a:hover,.nav__links .msub a:focus-visible{color:#fff}
  .nav__links .msub .msub__all{color:var(--gold);font-weight:600}
  .msub__all svg{width:15px;height:15px;flex:none}
  /* current page (aria-current set by navfix.js): weight + white, the house emphasis */
  .nav__links .navlink[aria-current],.nav__links .msub a[aria-current]{color:#fff;font-weight:600}
  /* drawer open: the bar goes solid so bar + drawer read as one surface, and a scrim dims the locked page
     (tapping it closes the menu without reaching the page underneath) */
  .nav.mobile-open{background:var(--ink);border-bottom-color:var(--ink-line-2)}
  .nav.mobile-open::before{opacity:0}
  .nav::after{content:"";position:absolute;left:0;right:0;top:100%;height:100vh;height:100lvh;z-index:-1;
    background:rgba(6,13,26,.72);opacity:0;visibility:hidden;pointer-events:none;touch-action:none;
    transition:opacity .2s ease,visibility 0s linear .2s}
  .nav.mobile-open::after{opacity:1;visibility:visible;pointer-events:auto;transition:opacity .2s ease,visibility 0s}
  /* the page competed under a .55 scrim and the drawer had no bottom edge; .72 and a hairline close it */
  .nav.mobile-open .nav__links{border-bottom:1px solid var(--ink-line-2)}
}
@media(max-width:960px) and (prefers-reduced-motion:reduce){.nav::after{transition:none}}
/* iPad landscape gets the desktop bar + mega menus: touch-size the triggers, rail rows and "View / All" links */
@media(min-width:961px) and (max-width:1199px) and (pointer:coarse){.nav__links .navlink{min-height:44px}.rail-item{min-height:44px}.detail .all,.panel__sfoot .all{min-height:44px}}
/* mega panels at tablet widths: 11px labels up to the 12px floor, wrapped detail titles get heading leading */
@media(min-width:961px) and (max-width:1199px){.menu__feat .cap .k,.panel__offices{font-size:12px}.detail h3{line-height:1.2;text-wrap:balance}}
/* /contact/ header CTA now jumps to the form card (it reloaded the page at every width); land it clear of the bar */
#cform{scroll-margin-top:calc(var(--navh,66px) + 16px)}
/* footer "Process" (/#process) landed flush under the fixed bar */
@media(max-width:1199px){#process{scroll-margin-top:var(--navh,66px)}}
/* logo <img> now carries width/height for a stable load; CSS keeps setting the size (the nav logo already has width:auto) */
.foot__logo img{width:auto}
/* footer touch targets: 37px link rows, 36px office phones, 31px phone, 21px Back to top, 36px socials */
@media(max-width:1199px){
  .foot__col a{padding:10px 0;min-height:44px}
  .foff a{display:inline-flex;align-items:center;min-height:44px;margin-top:0}
  .foot__tel{display:inline-flex;align-items:center;min-height:44px}
  .foot__totop{min-height:44px}
  .foot__social a{width:44px;height:44px}
}
/* footer layout below 1024: offices become one full-width 2x2 block so no column is a cramped strip.
   Tablet: Brand | Services | Company, offices below. Phone: Brand, Services | Company, offices, then the meta row. */
@media(max-width:1023px){
  .foot__top{grid-auto-flow:row dense}
  .foot__col--off{grid-column:1/-1;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 24px;align-items:start}
  .foot__col--off .eyebrow{grid-column:1/-1;margin-bottom:-6px}
  .foff{margin:0}
  /* no lone "400" / "600" on its own line: the suite number stays with "Suite" */
  .foff span{text-wrap:pretty}
}
/* 320px: a 128px office column split "Sacramento, CA | 95864"; the offices stack instead */
@media(max-width:359px){.foot__col--off{grid-template-columns:1fr}}
/* Company is a short list, so it takes only its own width: the brand column then holds the three badges on one row */
@media(min-width:700px) and (max-width:1023px){.foot__top{grid-template-columns:minmax(0,1.3fr) minmax(0,1fr) auto;gap:44px 30px}
  /* the offices 2x2 ran on its own 50/50 split, so Pasadena / Bakersfield started 8-11px right of Services above;
     it now takes the columns of the row above (second office column = Services + Company) */
  .foot__col--off{grid-template-columns:subgrid;column-gap:30px}
  .foot__col--off>.foff:nth-child(even){grid-column:1}
  .foot__col--off>.foff:nth-child(odd){grid-column:2 / -1}}
@media(max-width:699px){
  .foot__top{grid-template-columns:1fr 1fr;gap:40px 24px}
  .foot__brand{grid-column:1/-1}
  .foot__bottom{display:grid;grid-template-columns:1fr auto;row-gap:12px}
  .foot__bottom>span{grid-column:1/-1}
}
/* 320-359: "Biological Resources" and "Hazardous Materials" (136px) wrapped in a 128px column, so the Services and Company
   rows drifted out of register. Company's longest label is 53px: Services takes the wider share
   (after the <=699 rule it overrides) */
@media(max-width:359px){.foot__top{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr)}}
/* home closing CTA on phones: the two buttons stack at one width instead of a ragged 220/148 pair, and the band is less tall */
@media(max-width:600px){.cta__in{padding-block:72px}.cta .actions{flex-direction:column;align-items:stretch;max-width:320px;margin-inline:auto;gap:12px}.cta .actions .btn{justify-content:center;min-height:52px}}
/* home closing CTA below 1200: the phone ghost button's .14 border vanished on the lake photo; same .5 as the hero ghost.
   The lede broke "...takes to clear / it, and how we'd run it." at tablet widths; balanced lines instead */
@media(max-width:1199px){.home .cta .btn--ghost{border-color:rgba(255,255,255,.5)}.cta p{text-wrap:balance}}
/* 961-1199 bar: the plain links had no side padding while the caret buttons carry the UA 6px, so the gaps read
   24/18/12/18 ("Projects Insights" ran together) and the button text sat a fraction lower; padding and leading now match */
@media(min-width:961px) and (max-width:1199px){.nav__links>a.navlink{padding-inline:6px}.nav__links>button.navlink{line-height:inherit}}
/* 961-999: the 12px of added padding would overrun the bar, so the gap gives back 2px a side (22px even gaps) */
@media(min-width:961px) and (max-width:999px){.nav__links{gap:10px}}
/* 961-1199 "Who We Are" dropdown: it opens under its trigger and at 961 ran to 3px from the screen edge, so it is held
   16px in; its "Contact" arrow was squeezed to a 3px sliver by the wrapping office list */
@media(min-width:961px) and (max-width:1199px){.panel--simple{left:min(var(--anchor,16px),calc(100% - 360px))}.panel__sfoot .all svg{flex:none}}
/* footer 1024-1199: VETERAN-OWNED wrapped under DVBE / SDVOSB. The badge row stays whole and the brand column is
   floored at its width (280px), offices at their longest address line; Services and Company give up the few px.
   Same four-column rhythm as desktop */
@media(min-width:1024px) and (max-width:1199px){.foot__top{grid-template-columns:minmax(min-content,1.35fr) 1fr minmax(max-content,1.15fr) 1fr}.foot__badges{flex-wrap:nowrap}.foot__badge{flex:none;white-space:nowrap}}
/* footer "About" (39px) and "FAQ" (29px) were narrower than a 44px target; an invisible pad, no layout change */
@media(max-width:1199px){.foot__col a{position:relative}.foot__col a::after{content:"";position:absolute;inset:0 -8px}}
/* inner-page heroes: the Scroll cue was hidden at 1024-1199 on the service pages, /environmental-consulting/ and /faq/
   but still showed on careers, projects, industries and insights; off below desktop on every inner page */
@media(max-width:1199px){body.svcpage .hero__scroll{display:none}}
/* breadcrumb "Home" was a 20px target on projects, industries and insights (44px elsewhere); the service pages' invisible
   pad now covers every inner hero crumb, with no layout change */
@media(max-width:1199px){body.svcpage .crumb a{position:relative}body.svcpage .crumb a::after{content:"";position:absolute;inset:-12px -5px}}
/* phone H2s: one size on every inner page (28px at 390, as on the service pages), instead of 28 / 29.2 / 30px by page
   family. Pages whose authored lines need less at 320 keep their own smaller floor in their <style> */
@media(max-width:430px){body.svcpage .h2{font-size:clamp(26px,7.2vw,30px)}}
/* 431-900: the contact H2 set at 28px under 30px section H2s on the same page; it takes the section size */
@media(min-width:431px) and (max-width:900px){body.svcpage .cwrap__h{font-size:clamp(30px,3.6vw,44px)}}
/* stacked contact block (<=900): the intro was held at 42ch (385px) on who-we-are and the service pages, half the band,
   while /environmental-consulting/ and /industries/ ran 60ch; one measure everywhere, offices rule to the form width */
@media(max-width:900px){body.svcpage .cwrap__aside p{max-width:60ch}body.svcpage .cwrap__aside .cwrap__off{max-width:none}}
/* contact eyebrow: page-local `.cwrap__aside p` margins beat .eyebrow, so it sat flush on its H2 on some pages (0px)
   and 18px clear on others; 18px on every page */
@media(max-width:1199px){body.svcpage .cwrap__aside .eyebrow{margin:0 0 18px}}
/* emphasis: four <b> runs (contact intro on who-we-are and industries, the CEQA overview lead, the biology timing note)
   set at 700 in the paragraph's grey; the house idiom is 500 + ink. Below 1200 for now (desktop pending Victor's OK) */
@media(max-width:1199px){body.svcpage .cwrap__aside p b,body.svcpage #overview p b,body.svcpage #timing p b{font-weight:500;color:var(--c-ink)}}
/* == /QA:global == */
/* == QA:home == */
/* hero: below 1200 the copy runs wider than the 100deg scrim covers, so bright video frames
   washed out the eyebrow and H1. An even wash to 900, a deeper left wash 901-1199 */
@media(max-width:900px){
  .home .hero__scrim{background:linear-gradient(to top,rgba(10,10,11,.84) 0%,rgba(10,10,11,.68) 40%,rgba(10,10,11,.6) 72%,rgba(10,10,11,.5) 100%)}
}
@media(min-width:901px) and (max-width:1199px){
  /* the right half carries the paragraph, so the wash holds at .4 there instead of fading to .22 */
  .home .hero__scrim{background:
    linear-gradient(100deg,rgba(12,12,13,.8) 0%,rgba(14,14,15,.6) 40%,rgba(16,16,17,.44) 62%,rgba(16,16,17,.4) 100%),
    linear-gradient(to top,rgba(10,10,11,.7) 0%,rgba(10,10,11,0) 30%)}
  /* where the pair wraps (901-1167) "See the work" grows to the primary's 220px instead of a ragged 220/143 stack;
     where it fits on one row the 1-4px of slack is all it takes */
  .home .hero .actions .btn--ghost{flex:1 1 auto;max-width:220px;justify-content:center}
}
@media(max-width:1199px){
  /* the eyebrow's .82 white is an inline style, so only !important reaches it */
  .home .hero__lede .eyebrow{color:#fff!important}
  .home .hero__lede .eyebrow,.home .hero h1{text-shadow:0 1px 2px rgba(4,10,22,.6),0 0 22px rgba(4,10,22,.45)}
  .home .hero__certno{color:rgba(255,255,255,.78)}
  /* the 14% ghost border measured 1.3:1 on the video */
  .home .hero .btn--ghost{border-color:rgba(255,255,255,.5)}
}
/* H1 holds the approved four lines (Every Project / Begins // With What You / Can't See.) from 320
   to 639; 640-900 sets two lines at a size that fills a tablet hero */
@media(max-width:639px){.home .hero h1{font-size:clamp(34px,11.2vw,44px);max-width:8.6em}}
@media(min-width:640px) and (max-width:900px){.home .hero h1{font-size:clamp(44px,min(6.6vw,12vh),56px)}}
/* phones: both hero buttons one full width, like the service pages */
@media(max-width:560px){.home .hero .actions .btn{flex:1 1 100%;justify-content:center}}
/* trust: bigger marks on phones; the proof line keeps "See the work" whole */
@media(max-width:760px){.home .logorail img{height:44px}.home .trust__meta{font-size:14px;line-height:1.6}}
/* the eyebrow over the logos balances its two lines instead of leaving "AND PRIMES" alone */
@media(max-width:760px){.home .trust__lab{text-wrap:balance}}
@media(max-width:1199px){.home .trust__meta{text-wrap:balance}.home .trust__meta a{white-space:nowrap}}
/* statement: once stacked, the paragraph keeps a reading measure */
@media(min-width:561px) and (max-width:900px){.home .stmt__side .lead{max-width:60ch}}
/* the underlined "defend it." never splits (at 414-430 and 560 it left "it." alone with half the underline) */
@media(max-width:1199px){.home .pos__big .em{white-space:nowrap}}
/* growth: the unit label never drops under 12px; the title balances instead of orphaning "measure." */
@media(max-width:1199px){.home .growth__unit{font-size:max(12px,.16em)}.home .growth__title{text-wrap:balance}}
/* chart ticks are SVG user units, scaled down with the 640-wide viewBox (5px on a phone). The unit size
   shrinks as the chart widens, so the rendered labels hold 12-14px at every width in each layout band */
@media(max-width:560px){.home .growth{padding:30px 20px 32px}.home .chart__tick text{font-size:calc(50.2px - 5.81vw)}}
/* at that size "1000" overhung the chart's left edge by up to 10px; the chart steps in by the overhang (10px at 320,
   0 by 518) so the y labels start on the panel title's edge */
@media(max-width:560px){.home .chart{--chart-in:clamp(0px,32.4px - 6.25vw,10px)}.home .chart__svg{margin-left:var(--chart-in);width:calc(100% - var(--chart-in))}}
@media(min-width:561px) and (max-width:900px){.home .chart__tick text{font-size:calc(30.8px - 2.21vw)}}
@media(min-width:901px) and (max-width:1199px){.home .chart__tick text{font-size:calc(32.4px - 1.553vw)}}
/* services: 761-900 stacks the header like the statement above it */
@media(min-width:761px) and (max-width:900px){.home #services .shead{grid-template-columns:1fr;gap:14px}.home #services .shead__t{max-width:62ch}}
/* tile titles wrap on balance, not on a 22ch cap that orphaned "materials" */
@media(max-width:1199px){.home .mtile__n{text-wrap:balance}.home .mtile:not(.mtile--hero) .mtile__n{max-width:none}}
/* phones: tiles and their closing rule sit on the 20px text spine; titles sized for a full-width tile */
@media(max-width:640px){
  .home .mosaic,.home #services .secfoot{max-width:calc(100% - 2*var(--pad))}
  .home #services .secfoot{padding-inline:0}
  .home .mtile:not(.mtile--hero) .mtile__n{font-size:19px}
}
/* 641-1000: the sixth tile closes the grid wide, mirroring the wide CEQA tile, instead of sitting beside an empty cell */
@media(min-width:641px) and (max-width:1000px){.home .mosaic .mtile:last-child{grid-column:span 12;aspect-ratio:21/9}}
/* touch tablets in landscape have no hover, so the descriptions show as they do at <=1000 */
@media(max-width:1199px) and (hover:none){.home .mtile__d{opacity:1;transform:none}}
/* partners: the lead quote already closes on a rule, so the first short quote drops its own; short quotes keep a measure */
@media(max-width:860px){
  .home .qsub .q:first-child{border-top:0;margin-top:0;padding-top:0}
  .home .qsub blockquote{max-width:62ch}
}
/* process: the drawn orange line kept its 4-across geometry after the steps reflowed. 481-820 (2x2) hides it
   like the grey base line; phones carry it as a vertical connector from node to node */
@media(min-width:481px) and (max-width:820px){.home .proc__track--draw::after{display:none}}
@media(max-width:480px){
  .home .proc__track{gap:30px}
  .home .proc__track--draw::after{display:none}
  .home .proc__track .step{display:grid;grid-template-columns:60px 1fr;column-gap:18px;align-items:start}
  .home .proc__track .step__node{grid-row:1 / span 2;margin-bottom:0}
  .home .proc__track .step h3{margin:17px 0 6px}
  .home .proc__track .step:not(:last-child)::before{content:"";position:absolute;left:29px;top:30px;bottom:-60px;width:2px;background:var(--gold);z-index:-1}
}
/* the heading balances instead of orphaning "permit." */
@media(max-width:560px){.home #process h2{text-wrap:balance}}
/* step and value copy read at 15-15.5px on every phone and tablet, not 14/14.5px */
@media(max-width:1199px){.home .step p{font-size:15px}.home .vrow p{font-size:15.5px}}
/* about: the panel's own inset replaced by the page gutter, so it sits on the same left edge as every section */
@media(max-width:1199px){.home .oplap{padding-inline:var(--pad)}}
/* insights: tablet portrait sets the featured story image + text side by side instead of a half-screen image.
   Titles wrap "pretty" (no one-word last line); balance split "SB / 71" on phones */
@media(max-width:1199px){.home .ins__t{text-wrap:pretty}}
@media(max-width:900px){.home .ins{row-gap:36px}}
@media(min-width:641px) and (max-width:900px){
  .home .ins__lead{display:grid;grid-template-columns:1fr 1fr;gap:28px;align-items:center}
  .home .ins__body{padding-top:0}
}
/* FAQ: the first item is marked open in the markup, but the script only sizes an answer on click, so it showed
   the open badge over an empty answer. Fixed below 1200 only; desktop (approved) still shows it, pending a decision */
.home .faq__item.open .faq__a{max-height:none}
/* the closing link was double-indented (.secfoot padding inside a padded .wrap) under a second hairline;
   question rows, answers and value rows drop their 4px inset to sit on the heading edge */
@media(max-width:1199px){
  .home .faq + .secfoot{padding-inline:0;border-top:0;margin-top:8px}
  .home .faq__q,.home .vrow{padding-inline:0}
  .home .faq__a p{margin-inline:0}
}
/* section exit links reach a 44px touch target with no layout change */
@media(max-width:1199px){
  .home .tlink,.home .secfoot__a{position:relative}
  .home .tlink::after,.home .secfoot__a::after{content:"";position:absolute;inset:-11px -6px}
}
/* hero eyebrow at 360-430: "Permitting" wrapped alone under a dangling bullet on the page's first line. It needs 371px
   at 14px; 3.3vw sets it whole on one line (12.9px at 390, the 12px label floor at 360) */
@media(max-width:430px){.home .hero .eyebrow{font-size:clamp(12px,3.3vw,14px)}}
/* 415-560: the stacked hero buttons stopped at 377px, short of the right gutter */
@media(max-width:560px){.home .hero .actions{width:100%}}
/* veteran badge: the caption was inline in a taller line box, its two lines 27px apart; a block at 1.5 leading.
   Values: "...real survey / data." stranded "data." at tablet widths */
@media(max-width:1199px){.home .found__badge span{display:block;line-height:1.5}.home .vrow p{text-wrap:pretty}}
/* growth label: "...in our first / year" at 360 */
@media(max-width:560px){.home .growth__lab{text-wrap:pretty}}
/* process note on phones: the dash moves under the step nodes (centred on the connector) and the text starts on the
   step-copy column, instead of sitting on neither the heading edge nor the copy edge */
@media(max-width:480px){
  .home .proc__note{display:grid;grid-template-columns:60px 1fr;column-gap:18px;align-items:center}
  .home .proc__note::before{justify-self:center}
}
/* == /QA:home == */
/* == QA:about == */
/* /who-we-are/ hero ghost: a .14 white border on near-black barely registered; .5, as on the home hero and CTA band */
@media(max-width:1199px){body.svcpage .ahero .btn--ghost{border-color:rgba(255,255,255,.5)}}
/* /careers/ crew captions at tablet widths: balance left a stub first line ("And we / celebrate our / successes
   together."); pretty keeps full first lines and still avoids a lone last word. Phones keep balance */
@media(min-width:701px) and (max-width:1199px){body.svcpage .crew b{text-wrap:pretty}}
/* /who-we-are/ "Come soar with us." 761-900: heading left, paragraph right, and the button alone under the heading
   ~150px below its rule. Stacked like the service-page heads: heading, paragraph, button */
@media(min-width:761px) and (max-width:900px){
  body.svcpage:has(.ahero) #careers .shead,
  body.svcpage:has(.ahero) #careers .shead:not(.shead--wide):has(> div > .h2 .hl + .hl){grid-template-columns:1fr;gap:14px}
  body.svcpage:has(.ahero) #careers .shead__t{max-width:62ch}
}
/* == /QA:about == */
/* == QA:projects-industries == */
/* /projects/ media dialog: the close x was a 27x30 target. 44x44 now; the negative margin keeps the glyph where it was */
@media(max-width:1199px){.lbx .lbx__x{width:44px;height:44px;display:grid;place-items:center;padding:0;margin:-7px -8.5px -7px 0}}
/* landscape phones: the stage capped at 80vh but the dialog stayed 94vw, so the photo sat left with the arrows and x
   floating off it. The dialog now takes its width from the height that is left (16:10 photo, 16:9 video) */
@media(max-width:1199px){
  dialog.lbx{width:min(94vw,calc((100svh - 96px) * 1.6))}
  dialog.lbx.vid{width:min(94vw,calc((100svh - 96px) * 1.7778))}
  .lbx .lbx__stage{max-height:calc(100svh - 96px)}
}
/* project cards: the date lost to the page's `.pj p` (0,1,1) and set at body size; this restores the authored label.
   Actions become 44px targets (text stays put) and sit on the card floor, so 2-up rows line up */
@media(max-width:1199px){
  .pj .pj__d{font-size:12.5px;line-height:1.3;margin-top:16px}
  .pj .pj__acts{margin-top:auto;padding-top:4px;row-gap:0}
  .pj .pj__act{min-height:44px;text-wrap:balance}
  /* video titles read as captions of the card below at a 16px row gap; balanced so no word hangs alone */
  #videos .vgrid{row-gap:32px}
  #videos .vcard__t{text-wrap:balance}
}
/* (the /industries/ phone H2 sizes moved to that page's own <style>: #field, #related and #contact recur on other pages) */
/* /projects/ 2-up rows: a 44px box centred a one-line action while a two-line one filled its box, so the last lines
   sat ~11px apart and the CDFW excerpt gap closed to 4px. The box now hugs the text (bottoms already align on the
   card floor), the 44px target moves to ::after, and the desktop 14px excerpt gap returns */
@media(min-width:761px) and (max-width:1199px){
  .pj .pj__acts{padding-top:14px}
  .pj .pj__act{min-height:0;position:relative}
  .pj .pj__act::after{content:"";position:absolute;inset:-11px -6px}
}
/* /projects/: three images carry baked-in frames (the gavel card's defect). Below 1200 each is scaled inside its
   overflow:hidden frame to its live area: Willow-Shepherd (green border + bevel), Del Monte (dark bands above and below
   the photo strip), and the "Full-service consultancy" thumbnail (pillarbox + letterbox). `scale` composes with the hover
   zoom. Desktop keeps the files as they are (recropping them is Victor's call) */
@media(max-width:1199px){
  .pj__ph img[src*="n-willow-e-shepherd"]{scale:1.4;transform-origin:48% 45.6%}
  .pj__ph img[src*="del-monte-p"]{scale:2.04;transform-origin:50% 48.7%}
  .vcard__ph img[src*="zQvxQH2_CfE"]{scale:1.8;transform-origin:50% 50%}
}
/* /industries/ 701-900: the 4:5 crops were centred on every photo, halving the leftmost worker (Education &
   Institutional) and leaving "ED" of the MERCED sign (Government); those two are framed on their subjects */
@media(min-width:701px) and (max-width:900px){
  .sector__img img[src*="ind-institutional"]{object-position:15% 50%}
  .sector__img img[src*="ind-government"]{object-position:22% 50%}
}
/* landscape phones (844x390): each 4:5 photo was 330x412, taller than the screen; it keeps its column width and takes
   the screen height left under the bar */
@media(min-width:701px) and (max-width:900px) and (max-height:500px){.svcpage .sector__img{aspect-ratio:auto;height:calc(100svh - 96px)}}
/* == /QA:projects-industries == */
/* == QA:consulting-contact-faq == */
/* /environmental-consulting/ project titles: balance moved breaks onto the space after the em dash ("Foundation / —
   Sierra") and split "High- / Speed". The dash is glued to the word before it and High-Speed is one unit (span.nw) */
@media(max-width:1199px){.svcpage .pcard__t .nw{white-space:nowrap}}
/* /contact/ "Send message" was 47px tall; every other form submit and hero button is 54px */
@media(max-width:1199px){.cpage .cform__actions .btn{min-height:54px}}
/* /contact/ 921-1199: the copy column ended ~350px above the form card, leaving empty navy under the links. It holds
   in view while the form scrolls past. overflow:clip (not hidden) so the hero is not a scroll container */
@media(min-width:921px) and (max-width:1199px){
  .cpage .chero{overflow:clip}
  .cpage .chero__lede{position:sticky;top:calc(var(--navh,64px) + 32px);align-self:start}
}
/* phones: the hours line was 13px beside 15.5px copy */
@media(max-width:760px){.cpage .ofoot .hours{font-size:14px}}
/* == /QA:consulting-contact-faq == */
/* == QA:services == */
/* The six service detail pages only: theirs are the only breadcrumbs that link to /environmental-consulting/, so the
   other .svcpage pages (careers, faq, industries, insights, projects, who-we-are) do not move. Desktop (>=1200) untouched. */
/* hero 701-1199: the 116deg desktop grade fades out on the right, so the crumb, H1, sub and ghost CTA sat on bright sky
   (sub 2.0-3.4:1). An even .64-.92 wash fixed that but flattened the photo (hawk, hand barely readable). Now the
   wash is held under the text column (it ends near 600-650px at every width here) and eases past it, with a short
   band under the nav: crumb 4.8:1+, sub 5.8:1+, nav links 5.2:1+, ghost CTA 6.2:1+ at 701-1180, measured on the
   brightest 5% of pixels behind each */
@media(min-width:701px) and (max-width:1199px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .hero__scrim{background:linear-gradient(180deg,rgba(7,16,30,.38) 0,rgba(7,16,30,0) 120px),linear-gradient(90deg,rgba(7,16,30,.5) 0,rgba(7,16,30,.42) 540px,rgba(7,16,30,0) 840px),linear-gradient(0deg,rgba(6,13,26,.86) 0%,rgba(7,16,30,.55) 45%,rgba(7,16,30,.4) 100%)}
}
@media(max-width:1199px){
  /* a nowrap "Waste Consulting" (305px) grew the 1fr hero track past a 280px column at 320 and was clipped */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .hero__in{grid-template-columns:minmax(0,1fr)}
  /* the scroll cue was removed from phones; at 901-1199 it was a 47x18 target with an 11px label */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .hero__scroll{display:none}
  /* when the credential strip wraps (CEQA at 901-1199) its rows sat 41-47px apart; same 14px row gap as phones */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .hero__creds .wrap{row-gap:14px}
  /* uppercase micro-labels up to the 12px floor */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .flow__step .ph,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wcard__k{font-size:12px}
  /* 15px fields made iOS zoom the page on focus */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .field input,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .field textarea{font-size:16px}
  /* hero ghost CTAs and in-page links landed under the fixed bar. #documents is a mid-section paragraph that is still
     a .rev (18px below its place) when the jump is measured, so it gets 20px of air plus those 18px */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) section[id]{scroll-margin-top:var(--navh,66px)}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) #documents{scroll-margin-top:calc(var(--navh,66px) + 38px)}
  /* touch targets reach 44px with no layout change: crumb links (20px), "All projects" (23px), the contact phone (36px).
     The phone also stretched across the whole column, so a tap on the empty space beside it dialled */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .crumb a,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead>.tlink{position:relative}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .crumb a::after{content:"";position:absolute;inset:-12px -5px}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead>.tlink::after{content:"";position:absolute;inset:-11px -6px}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .cwrap__go{position:relative;align-self:flex-start}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .cwrap__go::after{content:"";position:absolute;inset:-4px -6px}
  /* the page-local .cwrap__aside p margin beat .eyebrow, so the contact eyebrow sat flush on its H2 (0px, 18px elsewhere) */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .cwrap__aside .eyebrow{margin:0 0 18px}
  /* Valued Partners has its own navy ground now, so it and the band after it each open a new ground: full top padding,
     not the halved "same chapter" padding (32px against 58px on every other section at 390) */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) #partners,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) #partners + .band-dark{padding-top:clamp(58px,5.6vw,86px)}
}
/* 320-369: the H1 steps down from 34px so the bound last pair fits the column */
@media(max-width:400px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .hero h1{font-size:clamp(28px,9.2vw,34px)}}
/* breadcrumb on phones: the current page takes its own line, so no "/" hangs at a line end and every hero starts alike */
@media(max-width:600px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .crumb .sep:nth-last-child(2){display:none}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .crumb .here{flex-basis:100%}
}
/* phones: authored two-line H2s held at two lines (30px broke "strategy." and "practice." onto a third) */
@media(max-width:430px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .h2{font-size:clamp(26px,7.2vw,30px)}}
/* 761-900: section heads stack (the lead was a 276px column, ~30 characters a line). The :has(.hl + .hl) form is
   repeated because the two-line-heading split above (min-width:761px) outranks a plain .shead */
@media(min-width:761px) and (max-width:900px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead:not(.shead--wide):has(> div > .h2 .hl + .hl){grid-template-columns:1fr;gap:14px}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead .h2{max-width:none}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead__t{max-width:62ch}
  /* photo + text splits keep their desktop two columns (stacked, the 4:3 photos ran 738px wide, upscaled 1.7-2x) */
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .depth{grid-template-columns:1fr 1fr;gap:clamp(28px,4.4vw,48px)}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .region{grid-template-columns:.82fr 1.18fr;gap:clamp(28px,4.4vw,48px)}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .obs{grid-template-columns:1.06fr .94fr;gap:clamp(28px,4.4vw,48px)}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .pled{grid-template-columns:.92fr 1.08fr;gap:clamp(28px,4.4vw,48px)}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .season__head,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .season__cap{grid-template-columns:.92fr 1.08fr;gap:clamp(28px,4.4vw,48px)}
}
/* short testimonials keep a reading measure once they run full width (96-113 characters a line at 768-820) */
@media(max-width:900px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .qsub blockquote{max-width:62ch}}
/* related services (5 cards): 2 wide + 3 at 561-900 instead of 2+2+1 with a lone card; on phones 1 wide + 2x2
   instead of five 200px cards stacked (~1,100px). 901-1199 stays 3+2 like desktop */
@media(min-width:561px) and (max-width:900px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wgrid--rel{grid-template-columns:repeat(6,minmax(0,1fr))}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wgrid--rel .wcard{grid-column:span 3}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wgrid--rel .wcard:nth-child(n+3){grid-column:span 2}
}
/* (the phone layout is shared with /who-we-are/ and /industries/, the only other pages with this grid: see the end of
   this block) */
/* phones: the form's submit runs the full card width, like the hero buttons */
@media(max-width:560px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .cform .btn{width:100%;justify-content:center}}
/* 761-900 photo splits: a 300-330px 4:3 photo beside a 600-650px text column read as a stray thumbnail (permitting
   had ~180px of empty paper above and below it). A portrait 4:5 crop gives it the column's weight */
@media(min-width:761px) and (max-width:900px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .pled__fig .duo,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .obs__fig .duo{aspect-ratio:4/5}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .obs__fig .duo img{object-position:32% 50%}
}
/* related services: one title wrapping to two lines beside one-line neighbours left the kicker labels at different
   heights across a row; every title in a shared row holds a two-line block so the labels line up */
@media(min-width:561px) and (max-width:900px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wgrid--rel .wcard__t{min-height:2.28em}}
/* == final pass (Oct 21) == */
/* related-service kickers (RESOURCES / APPROVALS / SUBSURFACE) sat at the top of the card scrim, where it was still
   nearly clear: 2.2-3.7:1 on the white ruler, the substation and the pale rock. The scrim now reaches .72 navy 22px into
   the caption band and the kicker starts below that point, so it always sits on .72-.95 navy */
@media(max-width:1199px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .wgrid--rel .wcard__b{padding-top:calc(var(--wcb-p,clamp(18px,1.8vw,24px)) + 22px);
    background:linear-gradient(rgba(8,20,38,0),rgba(8,20,38,.72) 22px,rgba(8,20,38,.95))}
}
/* phones: one wide card + 2x2 instead of five 200px cards stacked (~1,100px). The same component on /who-we-are/ and
   /industries/ was still five stacked cards, so it now shares this layout and the kicker scrim */
@media(min-width:360px) and (max-width:560px){
  body.svcpage .wgrid--rel{grid-template-columns:repeat(2,minmax(0,1fr))}
  body.svcpage .wgrid--rel .wcard{min-height:168px}
  body.svcpage .wgrid--rel .wcard:first-child{grid-column:1/-1}
  body.svcpage .wgrid--rel .wcard__b{--wcb-p:14px;padding:36px 14px 14px;
    background:linear-gradient(rgba(8,20,38,0),rgba(8,20,38,.72) 22px,rgba(8,20,38,.95))}
  body.svcpage .wgrid--rel .wcard__k{font-size:12px}
  body.svcpage .wgrid--rel .wcard__t{font-size:16px}
  body.svcpage .wgrid--rel .wcard:nth-child(n+2) .wcard__t{min-height:2.28em}
}
/* geology "Above the line / Below the line": the tabs centred their content, so a two-line hint lifted "Below the line"
   10px above "Above the line" (721-1023). Top-aligned; the hint balances instead of leaving "look." alone */
@media(max-width:1199px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tab{display:flex;flex-direction:column;justify-content:flex-start;align-items:stretch}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tab .ps{text-wrap:balance}
}
/* <=720: the tabs stacked, so the selected "Above" list sat under the grey "Below" tab and read as its contents. The pair
   stays side by side, as at 721 and up, and the open panel always sits under both */
@media(max-width:720px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tabs{grid-template-columns:1fr 1fr}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tab + .probe__tab{border-left:0}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tab{padding:16px 14px 18px}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .probe__tab .ps{font-size:13px}
}
/* 761-900, CEQA: the overview and the dark Coordination / Timing / Agencies rows stacked a 250px heading over a 447px
   paragraph with ~290px of empty navy beside every row. Two columns on the shared spine, as at 1024 */
@media(min-width:761px) and (max-width:900px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .flow__row,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .ov{grid-template-columns:1fr 1fr;gap:0 clamp(28px,4.4vw,48px)}
}
/* biology / archaeology middot lists: lines opened on a "·" and items split ("Protocol- / Level Surveys", "San / Joaquin
   Valley Kit Fox"). Each item and its trailing dot is one unit (span.li in the markup); below 1200 a unit only wraps
   inside itself when it is wider than the whole line. Desktop wrapping is unchanged (the spans are inert there) */
@media(max-width:1199px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .li{display:inline-block}}
/* permitting agency roster on phones: four short roles hugged the right edge while CDFW's wrapped into a ragged block;
   every row is now name over role */
@media(max-width:560px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .proster li{display:block}body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .proster span{margin-top:4px}}
/* biology species cards: the two-line name floor (2.4em) left an empty line under every one-line name. 521-900 all four
   names fit on one line; on phones only the first row holds the long "California Tiger Salamander" */
@media(min-width:521px) and (max-width:900px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .spec__card figcaption b{min-height:0}}
@media(max-width:520px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .spec__card:nth-child(n+3) figcaption b{min-height:0}}
/* species notes left one word alone ("season.", "year.", "at night.") */
@media(max-width:1199px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .spec__card figcaption span{text-wrap:pretty}}
/* phone heroes: the middle crumb link measured 4.2:1 on the bright geology rubble; .82 white (5:1+) on every phone hero */
@media(max-width:700px){body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .crumb a{color:rgba(255,255,255,.82)}}
/* 901-1199: beside a two-line heading the lead ran 345px wide and 8-11 lines (237px against a 143px heading), leaving a
   void under the heading. Widening the lead breaks the authored heading lines, so the heads stack as they do at 761-900
   and on /environmental-consulting/: heading, then the lead at a reading measure */
@media(min-width:901px) and (max-width:1199px){
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead,
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead:not(.shead--wide):has(> div > .h2 .hl + .hl){grid-template-columns:1fr;gap:14px}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead .h2{max-width:none}
  body.svcpage:has(.crumb a[href="/environmental-consulting/"]) .shead__t{max-width:62ch}
}
/* == /QA:services == */
/* == QA:insights == */
/* touch targets reach 44px with no visual change: Share (58x22) and the back link (23px) get an invisible hit pad;
   share icons and pager buttons were 42px because all:unset left them content-box */
@media(max-width:1199px){
  .svcpage .ishare::before{content:"";position:absolute;inset:-11px -8px}
  .svcpage .art__back{position:relative}.svcpage .art__back::before{content:"";position:absolute;inset:-11px -6px}
  .svcpage .ashare{gap:8px}.svcpage .ashare a,.svcpage .ashare button{box-sizing:border-box;width:44px;height:44px}
  .svcpage .ipager button{box-sizing:border-box;min-width:44px;height:44px}
  /* short filter labels ("All 27" is 38px wide) get 8px of invisible hit area each side; the gaps stay clear */
  .svcpage .ifilt button::before{content:"";position:absolute;inset:0 -8px}
  /* "Read the latest" (#posts) landed with the heading under the fixed bar; same idiom as #process in QA:global.
     Remove if a global html{scroll-padding-top} is added, or the offset doubles. */
  .svcpage #posts{scroll-margin-top:var(--navh,66px)}
  /* a date or reading time never splits across two lines in card and article meta */
  .svcpage .icard__m .d,.svcpage .art__m .d{white-space:nowrap}
}
/* 320-359: the Share label takes its own line so the five icons stay on one row */
@media(max-width:359px){.svcpage .ashare span{flex-basis:100%;margin:0 0 2px}}
/* topic filter: one swipeable row instead of 2-3 loose wrapped rows; underline sits on the hairline */
@media(max-width:899px){
  .svcpage .ifilt{flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
    margin-inline:calc(-1 * var(--pad));padding-inline:var(--pad);scroll-padding-inline:var(--pad);column-gap:26px}
  .svcpage .ifilt::-webkit-scrollbar{display:none}
  .svcpage .ifilt button{flex:none;white-space:nowrap}
  .svcpage .ifilt button::after{bottom:0}
  /* the rail clips anything outside it, so the focus ring draws inside the label */
  .svcpage .ifilt button:focus-visible{outline-offset:-3px}
}
/* 2-column card grid: a lone last card spans the row as image + text instead of leaving an empty half-row */
@media(min-width:621px) and (max-width:980px){
  .svcpage #more .igrid>.icard:last-child:nth-child(odd),.svcpage .igrid>.icard.icard--solo{grid-column:1/-1;display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:auto auto 1fr auto;column-gap:clamp(20px,2.4vw,34px);align-items:start}
  .svcpage #more .igrid>.icard:last-child:nth-child(odd)>.icard__ph,.svcpage .igrid>.icard.icard--solo>.icard__ph{grid-column:1;grid-row:1/-1}
  .svcpage #more .igrid>.icard:last-child:nth-child(odd)>:not(.icard__ph),.svcpage .igrid>.icard.icard--solo>:not(.icard__ph){grid-column:2}
  .svcpage #more .igrid>.icard:last-child:nth-child(odd)>.icard__m,.svcpage .igrid>.icard.icard--solo>.icard__m{margin-top:0}
}
/* tablet article: a 600px column keeps body copy near 70 characters a line (it ran 90-106 at 760px);
   18px reads better than desktop's 17px at tablet distance. Desktop keeps 760px/17px from 1200px. */
@media(min-width:600px) and (max-width:1199px){.svcpage .art{max-width:600px}.svcpage .art__b{font-size:18px}}
/* phone article: the one data table fits the column, pull quotes keep a smaller set-off than the UA 40px */
@media(max-width:600px){
  .svcpage .art__b table{width:100%;font-size:15px;line-height:1.45;border-collapse:collapse}
  .svcpage .art__b th,.svcpage .art__b td{padding:8px 10px 8px 0;vertical-align:top}
  .svcpage .art__b th:empty{padding:0}
  .svcpage .art__b tbody tr{border-bottom:1px solid var(--c-border)}
  .svcpage .art__b blockquote{margin-inline:20px}
}
/* 320-359: at 15px the table is still 306px in a 280px column, so it steps down to 14px with tighter cells */
@media(max-width:359px){.svcpage .art__b table{font-size:14px}.svcpage .art__b th,.svcpage .art__b td{padding-right:6px}}
/* phone article title: scales down from 30px so long titles stop filling the first screen (size is inline, hence !important) */
@media(max-width:430px){.svcpage .art h1{font-size:clamp(25px,4.4vw + 10px,30px)!important}}
/* touch: a tapped card's image zoom does not stay stuck */
@media(hover:none){.svcpage .icard:hover .icard__ph img{transform:none}}
/* card meta: category over date on every card, so a long category ("Permitting & Compliance") no longer leaves the "·"
   dangling at a line end or pushes its title below its row-mates. The dot is a bare text node: the grid parks it in a
   0-height third row and visibility hides it. Desktop keeps the one-line "Category · Date". */
@media(max-width:1199px){
  .svcpage .icard__m{display:grid;grid-template-rows:auto auto;grid-auto-rows:0;visibility:hidden}
  .svcpage .icard__m b{grid-row:1;visibility:visible}.svcpage .icard__m .d{grid-row:2;visibility:visible}
}
/* lone related card (no excerpt): "Read more" sits under the title instead of at the foot of the image row */
@media(min-width:621px) and (max-width:980px){.svcpage #more .igrid>.icard:last-child:nth-child(odd)>.icard__f{margin-top:0}}
/* landscape tablets (1024-1199): the 600px tablet column ran the title to 4 lines with ~290px empty each side. The
   article keeps desktop's 760px for the title and lead image; only running text holds the ~70-character measure */
@media(min-width:1024px) and (max-width:1199px){
  .svcpage .art{max-width:760px}
  .svcpage .art__b>p,.svcpage .art__b>ul,.svcpage .art__b>ol{max-width:600px}
}
/* topic rail 800-899: at 820 the last label was cut by its count ("Soar News 9"), which read as accidental. The gap
   closes just enough for all six labels to end on the right gutter (15px at 820, back to 26px by 899); at 768-799 the
   cut stays clearly mid-label. 662px is the six labels' width */
@media(min-width:800px) and (max-width:899px){.svcpage .ifilt{column-gap:clamp(14px,calc((90vw - 662px) / 5),26px)}}
/* article pages have no dark hero, so the transparent bar put its .9 white links and burger on pale paper (1.4-1.9:1);
   below 1200 the bar is solid ink there */
@media(max-width:1199px){body.svcpage:has(.art) .nav{background:var(--ink);border-bottom-color:var(--ink-line-2)}body.svcpage:has(.art) .nav::before{opacity:0}}
/* /insights/ "Or call 559.547.8884" was a 94x16 target (44px on /faq/); inline padding, the line does not move */
@media(max-width:1199px){.svcpage #contact .intro__mail a{padding-block:14px}}
/* "On a Mission For Threatened Species" card: the still carries the Soar bird watermark at its lower left. Below 1200
   the crop is anchored right so the mark falls outside the card (the file itself is Victor's / Mariah's call) */
@media(max-width:1199px){.svcpage .icard__ph img[src*="31Gq-QvRRxE"]{object-position:100% 50%;scale:1.04;transform-origin:100% 50%}}
/* == /QA:insights == */

/* == Oct 29 2026: client notes == */
/* contact bands: "Discuss your project" points to the form; the arrow turns down where the form sits below (<=900) */
.cwrap__go{display:inline-flex;align-items:center;gap:10px;min-height:44px;align-self:flex-start;max-width:100%}
.cwrap__go .ar{color:var(--gold);display:inline-block;transition:transform .3s var(--ease-out)}
.cwrap__go:hover .ar{transform:translateX(5px)}
@media(max-width:900px){.cwrap__go .ar{transform:rotate(90deg)}.cwrap__go:hover .ar{transform:rotate(90deg) translateX(4px)}}
/* homepage services: the capabilities statement link under the text beside "Six disciplines, one field team" */
.home #services .shead__t .tlink{margin-top:20px}

/* == Oct 29 2026: polish pass == */
/* basic pages (/our-team/, /capabilities-statement/): the intro line under the title in the ink head band */
.svcpage .ahead .ahead__lead{margin:clamp(16px,1.6vw,22px) 0 0;max-width:62ch;color:rgba(255,255,255,.9);
  font-size:clamp(17px,1.35vw,20px);line-height:1.55;text-wrap:pretty}
/* homepage FAQ: "All questions" sits on the list's left edge, under the list's own last rule; the second,
   full-width rule (and the gutter padding that pushed the link 64px in) only made a stray line */
.home .faq + .secfoot{padding-inline:0;border-top:0;margin-top:clamp(22px,2.2vw,30px);padding-top:0}
/* …and the questions and answers on that same edge at every width (a 4px inset above 1199px put them off the
   heading and the link) */
.home .faq__q{padding-inline:0}.home .faq__a p{margin-inline:0}
/* FAQ toggles: a plus that becomes a minus, drawn as two bars so the vertical one folds flat when the
   question opens. Same ring and colour open or closed: an x in a circle (filled or outlined, orange) read
   as a close / alert button */
.faq .faq__q .pm{position:relative;font-size:0}
.faq .faq__q .pm::before,.faq .faq__q .pm::after{content:"";position:absolute;left:50%;top:50%;width:10px;height:2px;
  margin:-1px 0 0 -5px;border-radius:1px;background:currentColor;transition:transform .28s var(--ease-out)}
.faq .faq__q .pm::after{transform:rotate(90deg)}
body .faq .faq__item.open .faq__q .pm{transform:none;background:transparent;border-color:var(--c-border);color:var(--c-muted)}
body .faqp .faq__item.open .faq__q .pm{color:var(--c-accent-text,#A4520A)}
body .faq .faq__item.open .faq__q .pm::after{transform:rotate(0deg)}
/* contact bands: page CSS sets the link inline-block, which dropped the 10px gap before the arrow */
.cwrap__aside .cwrap__go{display:inline-flex}
/* basic pages: the body on the head band's 960px measure, so the title, the text and the photos share one
   left edge; running text keeps a readable line */
.svcpage .art--page{max-width:960px}
.svcpage .art--page .art__b>p{max-width:68ch}
.svcpage .art--page .art__b{margin-top:0}
/* group headings in two columns: 20px keeps the longest ("Hazardous Materials & Professional Services") on one
   line in a 462px column at 1440, so the lists under them start level */
.art--page .art__b .wp-block-column>h3{font-size:clamp(18px,1.4vw,20px);margin-bottom:10px}
/* team rows on phones: a stacked photo the full column wide was a screen and a half per person */
@media(max-width:759px){
  .art--page .art__b .wp-block-media-text.is-stacked-on-mobile>.wp-block-media-text__media{max-width:168px}
  .art--page .art__b .wp-block-media-text.is-stacked-on-mobile>.wp-block-media-text__content{padding:18px 0 0}
}
/* AI-tell pass (Oct 29 2026; agency playbook anti-ai-slop-design + the impeccable.style catalog) */
/* card titles never end on one stranded word ("…Tulare, / California") */
.pcard__t,.wcard__t,.rcard__t{text-wrap:balance}
/* a 12px floor for the small uppercase labels, which ran 9.5-11px on some pages */
body .hero__scroll,body .wcard__k,body .flow__step .ph,body .dir__row .l,body .office .region,body .office .hq{font-size:12px}
/* "What we catch in the field" (#about): the contour texture behind it was hidden by the panel's solid
   paper fill, so on wide screens it showed only in the side margins and a strip below, ending on hard
   vertical and horizontal edges. The panel no longer overlaps anything, so it is see-through; the
   texture now fades out at the bottom as well as the top instead of stopping where Insights begins */
.home #about .oplap{background:transparent;padding-inline:var(--pad)}
/* (the panel's own 3.4vw side padding put the photo 15px outside the site's 64px gutter at 1440 and the
   right column 15px past it; on the gutter it lines up with every other section at every width) */
#about::after{
  /* one copy of the tile, sized to cover: the tile does not repeat cleanly, and at 58% it showed
     vertical seams (x=500 and x=1613 at 1920) where the contour lines broke */
  background-repeat:no-repeat;background-size:cover;background-position:62% 100%;
  /* hairlines: a 1px non-scaling stroke, so "cover" no longer fattens the lines to 3-4px across the copy */
  background-image:url("../assets/topo-tile-fine.svg");
  -webkit-mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 26%,#000 55%,#000 72%,transparent 100%);
          mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 26%,#000 55%,#000 72%,transparent 100%)}
/* the Veteran-owned plate sits on the photo: flat, no shadow smudging below the photo's edge */
.found__badge{box-shadow:none}
/* two-column layout: keep the copy column on clean paper. The contours stay around the photo, in the
   side margins and below, and fade out before the text (vertical fade x horizontal clear zone) */
@media(min-width:901px){
  #about::after{
    -webkit-mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 26%,#000 55%,#000 72%,transparent 100%),
      linear-gradient(to right,#000 0,#000 calc(50% - 40px),transparent calc(50% + 30px),transparent calc(50% + 600px),#000 calc(50% + 760px));
    -webkit-mask-composite:source-in;
            mask-image:linear-gradient(to bottom,transparent 0%,rgba(0,0,0,.5) 26%,#000 55%,#000 72%,transparent 100%),
      linear-gradient(to right,#000 0,#000 calc(50% - 40px),transparent calc(50% + 30px),transparent calc(50% + 600px),#000 calc(50% + 760px));
            mask-composite:intersect}
}
/* stacked (<=900px): the copy runs the full width, there is no empty margin to fill, so no texture */
@media(max-width:900px){#about::after{content:none}}


/* == Oct 29 2026: composition pass (looked at against apple.com, saildrone.com, anduril.com, geckorobotics.com) == */
/* inner-page heroes: the block rode the top edge (crumb 25px under the logo) with ~100px of empty photo under the
   buttons. It now sits low, as on the homepage, with the crumb clear of the logo */
@media(min-width:901px){
  body.svcpage .hero__in{padding-top:clamp(120px,14vh,140px)}
  body.svcpage .hero__creds{margin-top:clamp(24px,2.4vw,32px)}
}
/* section-head links ("All projects →", "All insights →") floated mid-row at the left of an empty column; they now
   close the heading's row on the right edge, level with its rule */
@media(min-width:761px){
  .home .shead>.tlink,.svcpage .shead>.tlink{align-self:end;justify-self:end;margin-bottom:-6px}
}
/* related services (5 cards) left an empty slot after 3+2 from 901px up: 2 wide + 3, as at 561-900 */
@media(min-width:901px){
  body.svcpage .wgrid--rel:has(> .wcard:nth-child(5):last-child){grid-template-columns:repeat(6,minmax(0,1fr))}
  body.svcpage .wgrid--rel:has(> .wcard:nth-child(5):last-child) .wcard{grid-column:span 3}
  body.svcpage .wgrid--rel:has(> .wcard:nth-child(5):last-child) .wcard:nth-child(n+3){grid-column:span 2}
}
/* homepage statement: the headline was bottom-aligned, so the eye landed on the grey paragraph first */
@media(min-width:901px){.home .stmt{align-items:start}}
/* homepage services: the closing hairline ran 80-1360 at 1440, matching neither the mosaic nor the text; it now
   runs on the text edges */
.home #services .secfoot{max-width:calc(min(100%,var(--maxw)) - 2*var(--pad));padding-inline:0}
/* closing line: no single word left on its second line */
.secfoot__t{text-wrap:pretty}
/* titles in steps and rows wrapped on the body's 1.62 leading, so a two-line title read as two separate lines
   (homepage process, the service-page "how we work" rows, permitting cells, biology rows, contact offices) */
body .step h3,body .flow__k h3,body .brow__r h3,body .pcell h3,body .city h3,body .vrow h4{line-height:1.2}
body .step h3{margin-bottom:12px}
/* CEQA document list: the rows were open at the bottom (a rule over each row, none under the last pair), and the
   lead-in and closing line ran past the first column to stop part-way across the second. Both now sit on column one */
.doclist li:last-child{border-bottom:1px solid var(--c-border)}
@media(min-width:721px){.doclist li:nth-last-child(2):nth-child(odd){border-bottom:1px solid var(--c-border)}}
@media(min-width:1100px){.svcpage .ovlead,.svcpage .doclist + .secfoot__t{max-width:calc((100% - clamp(36px,5.8vw,84px))/2)}}
/* homepage FAQ: an 840px list under its heading left the right third of the band empty. Heading and "All questions"
   take the left column, the questions the right, the way Stripe and Saildrone set a short FAQ */
@media(min-width:1100px){
  .home section:has(> .wrap > .faq) > .wrap{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,6fr);
    grid-template-rows:auto 1fr;column-gap:clamp(48px,4.4vw,72px)}
  .home section:has(> .wrap > .faq) .shead{grid-column:1;grid-row:1;grid-template-columns:1fr;margin-bottom:0}
  .home section:has(> .wrap > .faq) .faq{grid-column:2;grid-row:1 / span 2;max-width:none;margin-top:-18px}
  .home section:has(> .wrap > .faq) .faq__q{text-wrap:balance}
  .home section:has(> .wrap > .faq) .faq + .secfoot{grid-column:1;grid-row:2;align-self:start;max-width:none;margin:clamp(26px,2.4vw,34px) 0 0}
}
/* Environmental Consulting service index: title, description and photo were each centred in the row, so the three
   started at three different heights. They share the row's top edge */
body .svx__r{align-items:start}
/* GIS "under one roof" heading: at the related band's 16ch it broke into four lines, "environmental" alone on one. It
   takes the wide head the other long headings use, and balances to two lines */
@media(min-width:901px){
  body.svcpage:has(.ovband--map) #related .shead{grid-template-columns:minmax(0,1.7fr) minmax(0,1fr)}
  body.svcpage:has(.ovband--map) #related .shead .h2{max-width:none!important;text-wrap:balance}
}
/* Who We Are photo block: the tall photo kept 2:3 above 1199px, so it ended 18px above the two photos beside it. It
   fills both rows at every width, as it already did below 1200 */
body.svcpage .fam__stack .tall .duo{aspect-ratio:auto;height:100%}
/* Careers "We work hard. / We learn from one another. / ..." photos stepped down by 0, 72, 23 and 101px in two shapes,
   which read as scattered. One shape, one even step down the row, so the four lines read in order to the last */
@media(min-width:761px){
  body.svcpage .crew li:nth-child(n){margin-top:0}
  body.svcpage .crew li:nth-child(2){margin-top:clamp(18px,2.4vw,36px)}
  body.svcpage .crew li:nth-child(3){margin-top:clamp(36px,4.8vw,72px)}
  body.svcpage .crew li:nth-child(4){margin-top:clamp(54px,7.2vw,108px)}
  body.svcpage .crew li:nth-child(n) figure{aspect-ratio:4/5}
}
/* article titles: at 24ch the long ones balanced into four short lines beside an empty right half, over a 960px photo.
   At 30ch they take three */
@media(min-width:901px){body.svcpage .ahead--lead h1{max-width:30ch}}

/* Autoplay refused (iPhone Low Power Mode, data savers): no native play button over the hero;
   the poster stands in, and the first tap starts the video (home.php) */
video:not([controls])::-webkit-media-controls,
video:not([controls])::-webkit-media-controls-start-playback-button{display:none!important;-webkit-appearance:none}

/* == Nov 2 2026: project pages (/projects/<slug>/) == */
/* breadcrumb in the head band: a long project title stays on one line and ends in an ellipsis */
.ahead .crumb{max-width:100%}
.ahead .crumb a,.ahead .crumb .sep{flex:none}
.ahead .crumb .here{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the lead: the first photo (opens the photo viewer) or the video's still (plays it); the .art__lead frame is the article's */
.pjd__lead .pjd__open,.pjd__lead .pjd__play{all:unset;box-sizing:border-box;display:block;position:relative;width:100%;height:100%;cursor:zoom-in}
.pjd__lead .pjd__play{cursor:pointer}
.pjd__lead .pjd__open:focus-visible,.pjd__lead .pjd__play:focus-visible{outline:2px solid var(--c-accent);outline-offset:-4px}
.pjd__lead img{transition:transform .8s var(--ease-out)}
.pjd__lead:hover img{transform:scale(1.02)}
.pjd__badge,.pjd__tag{position:absolute;bottom:12px;display:inline-flex;align-items:center;gap:7px;max-width:calc(100% - 24px);padding:6px 10px;
  border-radius:2px;background:rgba(9,17,30,.78);color:#fff;font-size:12.5px;font-weight:600;letter-spacing:.02em;line-height:1.3}
.pjd__badge{right:12px}
.pjd__tag{left:12px}
.pjd__badge svg{width:15px;height:15px;flex:none}
.pjd__ic{position:absolute;left:50%;top:50%;width:58px;height:58px;margin:-29px 0 0 -29px;border-radius:50%;
  display:grid;place-items:center;background:rgba(9,17,30,.72);color:#fff;transition:background .2s}
.pjd__play:hover .pjd__ic{background:var(--c-accent)}
.pjd__ic svg{width:22px;height:22px;margin-left:3px}
/* the other photos: a row of thumbnails, each opening the viewer on its own photo */
.pjd__gal{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(10px,1.4vw,18px);margin-top:clamp(10px,1.4vw,18px)}
.pjd__thumb{all:unset;box-sizing:border-box;display:block;aspect-ratio:16/10;border-radius:var(--radius);overflow:hidden;background:var(--c-surface-2);cursor:zoom-in}
.pjd__thumb img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .8s var(--ease-out)}
.pjd__thumb:hover img{transform:scale(1.03)}
.pjd__thumb:focus-visible{outline:2px solid var(--c-accent);outline-offset:3px}
@media(hover:none){.pjd__lead:hover img,.pjd__thumb:hover img{transform:none}}
@media(max-width:600px){.pjd__gal{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* the write-up: a label on the left, the text on the right, a hairline above each section */
.pjd__sections{margin-top:clamp(36px,4.4vw,64px)}
.art > .pjd__sections:first-child{margin-top:0}
.pjd__sec{display:grid;grid-template-columns:minmax(0,.3fr) minmax(0,.7fr);gap:clamp(14px,3vw,48px);padding:clamp(26px,3vw,40px) 0;border-top:1px solid var(--c-border)}
.pjd__sec:last-child{padding-bottom:0}
.pjd__sec h2{margin:0;font-size:clamp(20px,1.8vw,24px);font-weight:600;line-height:1.25;letter-spacing:var(--tracking-display);color:var(--c-ink)}
.pjd__sec .art__b{margin-top:0}
.pjd__sec .art__b>:last-child{margin-bottom:0}
@media(max-width:760px){.pjd__sec{grid-template-columns:1fr;gap:12px}}
/* the photo viewer, as the Projects page has it (its rules are inline there) */
.lbx{padding:0;border:0;background:transparent;max-width:min(1200px,94vw);width:94vw;overflow:visible}
.lbx::backdrop{background:rgba(5,10,20,.88)}
.lbx__bar{display:flex;align-items:center;gap:14px;color:#fff;padding:0 0 10px}
.lbx__t{flex:1;font-size:15px;font-weight:600}
.lbx__n{font-size:13px;color:rgba(255,255,255,.7)}
.lbx__x{all:unset;cursor:pointer;font-size:30px;line-height:1;color:#fff;padding:0 4px}
.lbx__stage{position:relative;aspect-ratio:16/10;max-height:80vh;background:#05090f;border-radius:var(--radius);overflow:hidden}
.lbx__stage img{width:100%;height:100%;object-fit:contain;display:block}
.lbx.vid .lbx__stage{aspect-ratio:16/9}
.lbx__stage iframe{width:100%;height:100%;border:0;display:block}
.lbx__nav{all:unset;cursor:pointer;position:absolute;top:50%;margin-top:-26px;width:52px;height:52px;display:grid;place-items:center;
  font-size:40px;color:#fff;background:rgba(9,17,30,.6);border-radius:50%}
.lbx__prev{left:-8px}.lbx__next{right:-8px}
.lbx.one .lbx__nav{display:none}
@media(max-width:700px){.lbx__prev{left:6px}.lbx__next{right:6px}}
/* thumbnail rows close without a hole: two fill two columns, four fill four; on phones an odd last one runs full width */
.pjd__gal:has(> :nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}
.pjd__gal:has(> :nth-child(4):last-child){grid-template-columns:repeat(4,minmax(0,1fr))}
@media(max-width:600px){
  .pjd__gal,.pjd__gal:has(> :nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}
  .pjd__gal > :last-child:nth-child(odd){grid-column:1 / -1;aspect-ratio:16/8}
}
