/* ============================================================
   GroveDeck WordPress theme — shared stylesheet
   Organic "Grove" direction. Light warm canvas, deep forest
   anchor, wave transitions between color bands.
   No mid-greens. Palette (WCAG AA verified):
     canvas #F7F6F1 | canvas-2 #ECEFE6 | ink #1E2A23 (13.8:1)
     ink-2 #3F5249 (7.7:1) | leaf-deep #2C6149 (links/buttons)
     leaf-pale #E3EFE5 (on-dark) | forest #1B3325 (bands)
     focus #1B5FB0
   ============================================================ */
:root{
  --canvas:#F7F6F1;
  --canvas-2:#ECEFE6;
  --ink:#1E2A23;
  --ink-2:#3F5249;
  --forest:#1B3325;
  --forest-2:#264534;
  --leaf-deep:#2C6149;
  --leaf-deeper:#234E3B; /* leaf hover (on white = 9.4:1) */
  --leaf-pale:#E3EFE5;  /* on-dark text */
  --honey:#E8B45C;      /* primary CTA accent (honey + ink = 7.9:1) */
  --honey-dark:#D9A648; /* honey hover */
  --band:#E8EDDE;       /* light band background */
  --focus:#1B5FB0;
  --error:#9A2C2C;      /* form error text (on #fff = 7.6:1) — 2026-08-06 */
  --hairline:#CBD5CB;
  --radius:14px;
  --measure:70ch;

  /* Nav dropdown, as a concentric pair. Nested-radius rule (Apple HIG / Material 3 /
     Figma's nested-corner guidance): an inner corner is only cohesive with its
     container when outer = inner + gap. Anything else leaves the two arcs
     non-parallel and the gap between them pinching at the corners — which is what a
     999px stadium pill inside a 16px rounded panel was doing here.
     The item keeps the 999px pill so it matches the "Services" pill directly above
     it and the rest of the site's button language, so the PANEL is the side that
     moves. A stadium's effective corner radius is half its height, so the pad is the
     only free variable — and it's tuned so the panel lands on 32px, the radius this
     site already uses for its other large surfaces (.cta-panel, .lane-wrap), instead
     of the orphan 29.5px that a .4rem pad produced. Concentricity is derived rather
     than hardcoded so the pair can't drift; if the item's padding, font-size, or
     line-height changes, only --submenu-item-h needs re-measuring. */
  --submenu-pad:.55rem;               /* 8.8px */
  --submenu-item-h:46.2px;            /* measured: .62rem×2 padding + 26.4px line box */
  --submenu-item-radius:999px;        /* stadium → effective radius = item-h / 2 */
  --submenu-radius:calc(var(--submenu-item-h) / 2 + var(--submenu-pad)); /* 31.9px */
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition:none!important}
}
body{
  margin:0;
  background:var(--canvas);
  color:var(--ink);
  font-family:"Epilogue",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:1.0625rem;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
h1,h2,h3{
  font-family:"Fraunces",Georgia,serif;
  font-weight:600;
  line-height:1.15;
  margin:0 0 .6em;
  letter-spacing:-.01em;
}
h1{font-size:clamp(2.375rem,5.2vw,3.875rem)}
h2{font-size:clamp(1.625rem,3vw,2.375rem);color:var(--leaf-deep)}  /* 6.7:1 on canvas */
h3{font-size:1.25rem;line-height:1.3}
p{margin:0 0 1.1em;max-width:var(--measure)}
a{color:var(--leaf-deep);text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
a:hover{font-weight:600}
:focus-visible{outline:3px solid var(--focus);outline-offset:2px;border-radius:4px}
em{font-style:italic}

/* Skip link */
.skip-link{
  position:absolute;left:1rem;top:-3.5rem;z-index:100;
  background:var(--ink);color:var(--canvas);
  padding:.65rem 1.1rem;border-radius:0 0 10px 10px;
  text-decoration:none;font-weight:600;transition:top .15s ease-out;
}
.skip-link:focus{top:0}

/* Layout helpers */
.wrap{max-width:72rem;margin-inline:auto;padding-inline:clamp(1.25rem,4vw,2.5rem)}
section{padding-block:clamp(3rem,6vw,5rem);scroll-margin-top:2rem}
/* homepage sections: ~15% tighter vertical rhythm (v1.1.51) */
.home section{padding-block:clamp(2.55rem,5.1vw,4.25rem)}
/* Tighter top padding where a section follows a wave or a related block.
   7 call sites: home.html ×6 + the contact band build.js emits. (2026-08-06)
   Written as `section.section-tight` (0,1,1), NOT a bare `.section-tight`
   (0,1,0): `.home section` above is (0,1,1) and sets padding-block, so on the
   homepage its padding-top would outrank a bare class no matter where the class
   sits in source order. Source order alone does not fix this — specificity is
   compared first. Every call site is on a <section>, so the type selector is
   free. */
section.section-tight{padding-top:clamp(2rem,4vw,3.5rem)}
.band-2{background:var(--band)}

/* ===== Wave transitions between bands (organic, varied) ===== */
/* -2px bleed + 4px overscan kills hairline seams where bands meet */
.wave{line-height:0;display:block;margin-top:-2px;margin-bottom:-2px;position:relative}
.wave svg{display:block;width:100%;height:calc(clamp(52px,9vw,116px) + 4px)}
/* taller layered "treeline horizon" waves for hero → content and content → footer */
.wave-hero svg,.wave-foot svg{height:clamp(110px,17vw,200px)}

/* Buttons — 44px minimum target.
   ONE interaction model across every button surface (v1.1.61): hover and press
   shift TONE only. No transform, no shadow change — nothing moves or grows under
   the cursor. Matches the treatment .chip and .pagination a already used, and is
   the same for hand-coded .btn and Gutenberg .wp-block-button__link. */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:48px;padding:.7rem 1.6rem;border-radius:999px;
  font-weight:600;font-size:1rem;text-decoration:none;
  border:1px solid transparent;cursor:pointer;
  /* Explicit property list, not `all` — the shadow must never animate. */
  transition:background-color .18s ease-out,border-color .18s ease-out,
             color .18s ease-out,filter .18s ease-out;
  box-shadow:0 5px 24px -9px rgba(6,14,10,.36); /* static soft drop */
}
/* Press feedback for touch, where :hover never fires. */
.btn:active:not(:disabled){filter:brightness(.94)}
/* theme.js disables the contact submit during send; without this the dead
   button still answered :hover. */
.btn:disabled,.btn[aria-disabled="true"]{opacity:.8;cursor:not-allowed}

.btn-primary{background:var(--honey);color:var(--ink)}                      /* 7.9:1 */
.btn-primary:hover:not(:disabled){background:var(--honey-dark)}             /* 6.7:1 */
/* Secondary CTA — solid leaf-green, pairs with the honey primary (See how we work). */
.btn-lead{background:var(--leaf-deep);color:#fff}                           /* 7.2:1 */
.btn-lead:hover:not(:disabled){background:var(--leaf-deeper);color:#fff}    /* 9.4:1 */
/* Ghost/outline buttons stay flat — a drop shadow under a transparent fill reads wrong.
   2026-08-06: kept, not deleted. No page in src/pages/ uses .btn-outline any more
   (404.html's second CTA became .btn-lead), but the consent banner's Decline
   button does — src/partials/consent.html:9 — so the light pair below is live on
   every page. The .on-dark pair currently matches nothing (.gd-consent is a
   body-level sibling, not inside an .on-dark band); it stays because .on-dark is
   the site-wide dark-band contract and an outline button dropped into one would
   otherwise render leaf-deep-on-forest at 1.9:1 with no warning. 4 lines. */
.btn-outline{background:transparent;color:var(--leaf-deep);border-color:var(--leaf-deep);box-shadow:none} /* 6.7:1 */
.btn-outline:hover:not(:disabled){background:var(--band)}                   /* 6.0:1 */
.on-dark .btn-outline{color:var(--leaf-pale);border-color:var(--leaf-pale)} /* 11.5:1 */
.on-dark .btn-outline:hover:not(:disabled){background:var(--forest-2)}      /* 9.0:1 */
.on-dark :focus-visible{outline-color:#9CC4F0}
/* Honey primary keeps its colour on dark too — the warm fill carries the emphasis. */

/* ===== Header (forest anchor) — shared chrome ===== */
.hero-shell{background:var(--forest);color:var(--canvas);position:relative;overflow:hidden}
.site-header{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding-block:1.25rem;position:relative;z-index:2}
.wordmark{
  font-family:"Fraunces",Georgia,serif;font-size:1.45rem;font-weight:600;
  color:var(--canvas);text-decoration:none;display:inline-flex;align-items:center;gap:.5rem;
}
.wordmark svg{flex:none}
/* ===== Primary navigation — WordPress-ready accessible disclosure menu ===== */
/* (screen-reader-only utility now lives with .sr-only, see "Visually hidden") */

/* Mobile menu controls — hidden on desktop, revealed in the hamburger media query */
.nav-toggle{
  display:none;appearance:none;-webkit-appearance:none;background:transparent;border:0;cursor:pointer;
  width:44px;height:44px;align-items:center;justify-content:center;
  color:var(--canvas);border-radius:12px;flex:none;
  transition:background .18s ease-out;
}
.nav-toggle:hover{background:rgba(227,239,229,.12)}
.nav-toggle-bars{position:relative;display:block;width:22px;height:14px}
.nav-toggle-bars span{position:absolute;left:0;right:0;height:2px;background:currentColor;border-radius:2px}
.nav-toggle-bars span:nth-child(1){top:0}
.nav-toggle-bars span:nth-child(2){top:6px}
.nav-toggle-bars span:nth-child(3){top:12px}
.nav-close{
  display:none;position:absolute;top:1.4rem;right:clamp(1.25rem,6vw,2.25rem);
  width:44px;height:44px;align-items:center;justify-content:center;
  appearance:none;-webkit-appearance:none;background:transparent;border:0;color:var(--canvas);cursor:pointer;border-radius:12px;
  transition:background .18s ease-out;
}
.nav-close:hover{background:rgba(227,239,229,.12)}
body.nav-open{overflow:hidden}
/* Backdrop behind the right-side drawer (shown only in the mobile media query) */
.nav-scrim{display:none;position:fixed;inset:0;z-index:55;background:rgba(13,26,18,.5);opacity:0;visibility:hidden;border:0;margin:0;padding:0;cursor:pointer;transition:opacity .26s ease-out,visibility .26s}

.site-nav{display:flex;align-items:center}
.site-nav .menu,
.site-nav .sub-menu{list-style:none;margin:0;padding:0}
.site-nav .menu{display:flex;align-items:center;gap:clamp(.15rem,1vw,.5rem);flex-wrap:wrap}
.site-nav .menu-item{position:relative;display:flex;align-items:center}

/* Top-level links and submenu toggles share one pill affordance — NO underline on hover */
.site-nav .menu-item > a,
.site-nav .submenu-toggle{
  color:var(--leaf-pale);text-decoration:none;font-weight:500;font-size:1rem;
  min-height:44px;display:inline-flex;align-items:center;gap:.3rem;
  padding:.5rem .8rem;border-radius:999px;
  transition:background .18s ease-out,color .18s ease-out;
}
/* Reflow-free hover bold: a hidden 600-weight ghost reserves the bold width on
   the horizontal top-level labels, so bolding one never nudges the row. */
.site-nav > .menu > .menu-item > a:not(.btn){
  flex-direction:column;justify-content:center;align-items:flex-start;gap:0;
}
.site-nav > .menu > .menu-item > a:not(.btn)::after{
  content:attr(data-text);font-weight:600;height:0;line-height:0;
  overflow:hidden;visibility:hidden;pointer-events:none;
}
/* Childless items: hover/focus brightens + bolds the label — buttons excluded.
   MUST stay scoped to `> .menu >` (top level). Unscoped, this rule also matched
   .sub-menu links and — being one :not() more specific than the sub-menu hover rule
   below — overrode it with font-weight:600, so hovering a dropdown item re-measured
   its nowrap label at bold and widened the whole panel a couple of px mid-hover. */
.site-nav > .menu > .menu-item:not(.menu-item-has-children):not(.menu-cta) > a:hover,
.site-nav > .menu > .menu-item:not(.menu-item-has-children):not(.menu-cta) > a:focus-visible{
  color:#fff;font-weight:600;
}

/* Parent items: the link and its caret act as ONE target — hovering, focusing, or
   opening either underlines the label and brightens the caret together. */
.site-nav .menu-item-has-children{border-radius:999px}
.site-nav .menu-item-has-children:hover > a,
.site-nav .menu-item-has-children:focus-within > a,
.site-nav .menu-item-has-children.is-open > a{
  color:#fff;font-weight:600;
}
.site-nav .menu-item-has-children:hover > .submenu-toggle,
.site-nav .menu-item-has-children:focus-within > .submenu-toggle,
.site-nav .menu-item-has-children.is-open > .submenu-toggle{
  color:#fff;
}

/* Current page / current section indicator — "Soft puff" raised glass: a pillowy
   translucent fill, a top sheen, a fine rim, and a wide soft drop so the pill
   floats above the bar. Scoped to TOP-LEVEL items. Bold white label is the
   primary cue (WCAG 1.4.1); the glass is the supporting shape signal. */

/* (a) Childless current item: the link is the glass pill. */
.site-nav > .menu > .menu-item:not(.menu-item-has-children).current-menu-item > a:not(.btn),
.site-nav > .menu > .menu-item:not(.menu-item-has-children) > a[aria-current="page"]:not(.btn){
  color:#fff;font-weight:600;
  background:rgba(227,239,229,.15);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.32),
    0 0 0 1px rgba(227,239,229,.12),
    0 12px 26px -8px rgba(0,0,0,.45);
}

/* (b) Parent item that is the current section (ancestor) or current page:
   the glass wraps the whole pill — label AND caret together (applied to the li). */
.site-nav > .menu > .menu-item-has-children.current-menu-ancestor,
.site-nav > .menu > .menu-item-has-children.current-menu-item{
  background:rgba(227,239,229,.15);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.32),
    0 0 0 1px rgba(227,239,229,.12),
    0 12px 26px -8px rgba(0,0,0,.45);
}
.site-nav > .menu > .menu-item-has-children.current-menu-ancestor > a,
.site-nav > .menu > .menu-item-has-children.current-menu-item > a,
.site-nav > .menu > .menu-item-has-children.current-menu-ancestor > .submenu-toggle,
.site-nav > .menu > .menu-item-has-children.current-menu-item > .submenu-toggle{
  color:#fff;font-weight:600;
}

/* Current item inside an open dropdown — same "Soft puff" glass, concentric with the
   panel. `.menu-item` is restated so this outranks the base `.sub-menu .menu-item > a`
   rule further down; tied on specificity it lost, and the current item silently
   rendered at leaf-pale/500 with only the glass showing — no weight cue (WCAG 1.4.1)
   even though the optical padding nudge below was already compensating for a bold
   that never arrived. */
.site-nav .sub-menu .menu-item.current-menu-item > a,
.site-nav .sub-menu .menu-item > a[aria-current="page"]{
  color:#fff;font-weight:600;
  background:rgba(227,239,229,.15);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.32),
    0 0 0 1px rgba(227,239,229,.12),
    0 10px 20px -10px rgba(0,0,0,.5);
}

/* Parent items: anchor + injected toggle button sit together. Extra left
   padding balances the room the caret takes on the right so the pill looks even. */
.site-nav .menu-item-has-children > a{padding-left:1.15rem;padding-right:.45rem}
.site-nav .submenu-toggle{
  background:transparent;border:0;cursor:pointer;
  margin-left:-.5rem;padding:.5rem .5rem;min-width:44px;justify-content:center;
}
.site-nav .submenu-toggle svg{transition:transform .22s ease-out}
.site-nav .menu-item-has-children.is-open > .submenu-toggle svg{transform:rotate(180deg)}

/* Dropdown panel */
.site-nav .sub-menu{
  position:absolute;top:calc(100% + .5rem);left:0;z-index:40;min-width:248px;
  background:var(--forest-2);
  border:1px solid rgba(227,239,229,.16);
  border-radius:var(--submenu-radius);padding:var(--submenu-pad);
  box-shadow:0 20px 44px rgba(0,0,0,.4);
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity .18s ease-out,transform .18s ease-out,visibility .18s;
}
.site-nav .menu-item-has-children.is-open > .sub-menu{
  opacity:1;visibility:visible;transform:translateY(0);
}
.site-nav .sub-menu .menu-item{display:block}
.site-nav .sub-menu .menu-item > a{
  display:block;width:100%;border-radius:var(--submenu-item-radius);padding:.62rem 1.05rem;
  min-height:44px;font-weight:500;color:var(--leaf-pale);white-space:nowrap;
}
/* Reflow-free width: the panel is shrink-to-fit over nowrap labels, so its width is
   set by the widest label AT ITS CURRENT WEIGHT — which means the panel measured a
   different width on the page whose item is the bold current one. Same hidden
   600-weight ghost trick as the top-level bar: every item reserves its bold width,
   so the panel is one fixed size on every page, open or hovered. (No JS = no
   data-text = empty ghost = harmless.) */
.site-nav .sub-menu .menu-item > a::after{
  /* display:block is load-bearing twice over. Inline, the ghost would sit on the
     same line as the real label and their widths would ADD. And as a flex item
     (the first thing tried here) it picked up min-height:auto, which floors a
     flex item at its min-content height regardless of height:0 — that silently
     grew every row from 46.2px to 51px. As a block child of a block anchor it
     contributes its max-content width and exactly zero height. */
  content:attr(data-text);display:block;font-weight:600;height:0;line-height:0;
  overflow:hidden;visibility:hidden;pointer-events:none;
}
/* Hover shifts colour + a soft fill, never weight — a 500→600 swap reflows the
   label mid-hover and can rewrap it in a narrow column.
   No underline: these are a list of links with no surrounding body copy, so
   WCAG 1.4.1 / F73 (links must be distinguishable from adjacent text without
   colour) never engages here, and F73 explicitly does NOT accept a hover-only
   underline as that cue anyway — it has to be in the resting state to count. The
   fill is a weaker version of the current-item glass, so it reads as "you are
   about to pick this" against "this is the one you're on". Keyboard users still
   get the global 3px :focus-visible outline (2.4.7 / 2.4.13). */
.site-nav .sub-menu .menu-item > a:hover,
.site-nav .sub-menu .menu-item > a:focus-visible{color:#fff;background:rgba(227,239,229,.09)}

/* CTA item keeps the pill button look (honey primary).
   font-weight is restated because `.site-nav .menu-item > a` (0,2,1) outranks
   `.btn` (0,1,0) and would otherwise drop this CTA to 500 while every other
   primary button on the site sits at 600. */
.site-nav .menu-cta{margin-left:.35rem}
.site-nav .menu-cta > a.btn{white-space:nowrap;padding:.7rem 1.6rem;min-height:48px;color:var(--ink);font-weight:600}
.site-nav .menu-cta > a.btn:hover{color:var(--ink);background:var(--honey-dark)}

/* No-JS / fallback: reveal submenu on hover for pointer users */
@media (hover:hover){
  .no-js .site-nav .menu-item-has-children:hover > .sub-menu{opacity:1;visibility:visible;transform:translateY(0)}
}
/* ===== Mobile: hamburger drawer (JS) / wrapping fallback (no-JS) ===== */
@media (max-width:860px){
  /* Submenus stack inline; menu becomes a vertical list. Applies in the drawer
     AND in the no-JS wrapping fallback. */
  .site-nav .sub-menu{position:static;opacity:1;visibility:visible;transform:none;box-shadow:none;border:0;background:transparent;padding:.1rem 0 .3rem .8rem;min-width:0}
  .site-nav .sub-menu .menu-item > a{white-space:normal}
  .site-nav .menu-item-has-children:not(.is-open) > .sub-menu{display:none}
  .site-nav .menu{flex-direction:column;align-items:flex-start;gap:.1rem;width:100%}
  .site-nav .menu-item{width:100%}
  /* Parent items: let the submenu wrap onto its own full-width row so it drops
     DOWN beneath the label + caret instead of sitting to their right. */
  .site-nav .menu-item-has-children{flex-wrap:wrap}
  .site-nav .menu-item-has-children > .sub-menu{flex-basis:100%;width:100%}

  /* Hamburger drawer — only when JS is active (html loses .no-js).
     Right-side slide-in panel (not a full-screen overlay) with a tap-to-close
     backdrop over the rest of the page. */
  html:not(.no-js) .nav-toggle{display:inline-flex}
  html:not(.no-js) .nav-scrim{display:block}
  html:not(.no-js) .nav-scrim.is-open{opacity:1;visibility:visible}
  html:not(.no-js) .site-nav{
    position:fixed;top:0;right:0;bottom:0;left:auto;z-index:60;
    width:min(86vw,340px);
    background:var(--forest);
    box-shadow:-14px 0 40px rgba(0,0,0,.32);
    flex-direction:column;align-items:stretch;justify-content:flex-start;
    padding:clamp(4.5rem,15vw,6rem) clamp(1.25rem,6vw,1.75rem) 2.5rem;
    overflow-y:auto;overscroll-behavior:contain;
    visibility:hidden;transform:translateX(100%);
    transition:transform .26s ease-out,visibility .26s;
  }
  html:not(.no-js) .site-nav.is-open{visibility:visible;transform:translateX(0)}
  html:not(.no-js) .nav-close{display:inline-flex}
  html:not(.no-js) .site-nav .menu{gap:.3rem}
  html:not(.no-js) .site-nav .menu-item > a,
  html:not(.no-js) .site-nav .submenu-toggle{font-size:1.1rem}
  html:not(.no-js) .site-nav .menu-cta{margin:.6rem 0 0}
  html:not(.no-js) .site-nav .menu-cta > a.btn{align-self:flex-start}
}

/* ===== Hero (front page) ===== */
.hero{padding-block:clamp(3.5rem,8vw,7rem) 0;position:relative;z-index:1}
.eyebrow{
  color:var(--leaf-pale);
  font-size:.85rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  margin-bottom:1.4rem;
}
.hero h1{color:var(--canvas);max-width:21ch}
.hero h1 em{color:var(--leaf-pale);font-style:italic}
.hero .lede{color:var(--leaf-pale);font-size:1.1875rem;max-width:58ch;margin-top:1.25rem}
.hero-ctas{display:flex;gap:.9rem;flex-wrap:wrap}
.lead-in{font-size:1.125rem;color:var(--ink-2);max-width:62ch}

/* ===== Compact interior page header ===== */
.page-head{padding-block:clamp(2.5rem,6vw,4.5rem) clamp(2.5rem,6vw,4rem);position:relative;z-index:1}
.page-head .eyebrow{margin-bottom:1rem}
.page-head h1{color:var(--canvas);max-width:20ch}
.page-head .lede{color:var(--leaf-pale);font-size:1.1875rem;max-width:56ch;margin-top:1.1rem}
/* Interior hero CTAs — was six copies of an inline style="margin-top:2rem" on
   every service page's hero_extra. Kept at 2rem (the value they all shipped);
   the homepage .hero keeps its own slightly roomier 2.25rem. (2026-08-06) */
.page-head .hero-ctas{margin-top:2rem}

/* ===== Verticals / card grids ===== */
.vert-grid{display:grid;gap:2rem;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));margin-top:2.25rem}
.vert-grid > div{
  position:relative;
  padding:1.25rem 1rem;margin-inline:-1rem;border-radius:var(--radius);
  transition:transform 0.2s ease, box-shadow 0.2s ease;
}
.vert-grid > div:hover{transform:translateY(-5px);box-shadow:0 14px 32px rgba(30, 42, 35, 0.12)}
/* whole card clickable via stretched link */
.vert-grid h3 a{text-decoration:none}
.vert-grid h3 a:hover{text-decoration:none}
.vert-grid h3 a::after{content:"";position:absolute;inset:0;border-radius:var(--radius)}
.vert-grid h3 a:focus-visible{outline:none}
.vert-grid h3 a:focus-visible::after{outline:3px solid var(--focus);outline-offset:2px}
.vert-note{color:var(--ink-2);font-size:.95rem;margin-top:1rem}
#verticals .vert-note{font-size:1.0625rem} /* homepage: match section body size; .95rem stays for single.php post meta */

/* ===== Services ledger ===== */
.services-wrap{position:relative}
.services{display:grid;grid-template-columns:1fr 1fr;column-gap:clamp(2rem,5vw,4.5rem);margin-top:1rem;position:relative;z-index:1}
.service{
  position:relative;
  display:grid;grid-template-columns:1fr;gap:1rem;align-items:start;
  padding:1.35rem 1rem;margin-inline:-1rem;border-radius:var(--radius);
  transition:transform 0.2s ease, box-shadow 0.2s ease;
}
.service:hover{transform:translateY(-5px);box-shadow:0 14px 32px rgba(30, 42, 35, 0.12)}
/* whole card clickable via stretched link */
.service h3 a{text-decoration:none}
.service h3 a:hover{text-decoration:none}
.service h3 a::after{content:"";position:absolute;inset:0;border-radius:var(--radius)}
.service h3 a:focus-visible{outline:none}
.service h3 a:focus-visible::after{outline:3px solid var(--focus);outline-offset:2px}
.service svg{color:var(--leaf-deep);margin-top:.2rem}
.service h3{margin-bottom:.35rem}
.service p{margin:0;color:var(--ink-2);font-size:.97rem}
@media (max-width:760px){.services{grid-template-columns:1fr}}
/* lone last services card (odd card count only): one-column width, left-justified and
   flush with the column-1 cards (which bleed -1rem); v1.1.52 — was centered in v1.1.48.
   v1.1.55: scoped to :nth-child(odd) so an even grid (6 cards) renders 2×3 untouched */
.services>.service:nth-child(odd):last-child{grid-column:1 / -1}
@media (min-width:761px){
  .services>.service:nth-child(odd):last-child{justify-self:start;margin-inline:-1rem;width:calc((100% - clamp(2rem,5vw,4.5rem))/2 + 2rem)}
}

/* ===== Engine band (forest, organic corners) ===== */
.engine{
  background:var(--forest);color:var(--leaf-pale);
  border-radius:32px;padding:clamp(2rem,5vw,3.5rem);
  position:relative;overflow:hidden;
}
.engine::before{
  content:"";position:absolute;right:-15%;top:-55%;width:70%;height:120%;
  background:var(--forest-2);border-radius:50%;
}
.engine > *{position:relative}
.engine h2{max-width:24ch;color:var(--canvas)}
.engine p{color:var(--leaf-pale)}

/* ===== Comparison table — elevated GroveDeck card ===== */
.table-hint{display:none;font-size:.9rem;color:var(--ink-2);margin:1.5rem 0 -1rem}
@media (max-width:700px){.table-hint{display:block}}
/* overflow-x:clip keeps .lane-card (absolute, outside .table-scroll) from
   escaping the wrap on narrow phones — below ~345px it used to push the whole
   document sideways. clip, not hidden, so vertical position:sticky still works. */
.lane-wrap{position:relative;margin-top:2rem;overflow-x:clip}
.table-scroll{overflow-x:auto}
.table-scroll:focus-visible{outline:3px solid var(--focus);outline-offset:2px;border-radius:var(--radius)}
.table-scroll table{border-collapse:separate;border-spacing:0;width:100%;min-width:640px;font-size:.97rem;position:relative;z-index:1}
.table-scroll caption{text-align:left;padding:1rem}
.table-scroll thead th{font-family:"Fraunces",serif;font-weight:600;font-size:1.02rem;
  padding:1.05rem 1.4rem;text-align:left;vertical-align:middle;color:var(--ink-2)}
.table-scroll tbody th,.table-scroll tbody td{padding:1.15rem 1.4rem;text-align:left;vertical-align:top;
  border-bottom:1px solid color-mix(in srgb,var(--hairline) 60%,transparent)}
.table-scroll tbody th{font-weight:600;color:var(--ink-2);font-size:.95rem}
.table-scroll tbody tr:last-child th,.table-scroll tbody tr:last-child td{border-bottom:none}
/* GroveDeck column = one elevated card. White fill + even shadow are drawn by
   .lane-card sitting behind the column (positioned by theme.js); the cells stay
   transparent so the card shows through, keeping their own row separators. */
.table-scroll tbody td.col-gd{font-weight:600;background:transparent;
  border-bottom-color:color-mix(in srgb,var(--hairline) 70%,transparent)}
.table-scroll thead th.col-gd{position:relative;z-index:2;
  background:var(--leaf-deep);color:#fff;border-radius:18px 18px 0 0}
.lane-card{position:absolute;z-index:0;background:#fff;border-radius:18px;
  box-shadow:0 0 38px -12px rgba(27,51,37,.5)}
/* graceful fallback before JS runs / if JS is disabled: tint the column */
.lane-card:not([style]){display:none}
.lane-wrap:not(:has(.lane-card[style])) tbody td.col-gd{background:color-mix(in srgb,var(--leaf-pale) 60%,#fff)}

/* ===== CTA band ===== */
.cta-shell{background:var(--forest);color:var(--canvas)}
.cta-shell h2{color:var(--canvas);max-width:24ch}
.cta-shell p{color:var(--leaf-pale)}
.trust-line{color:var(--leaf-pale);font-size:.95rem;margin-top:1.4rem}

/* ===== FAQ ===== */
.faq{margin-top:1.5rem;border-top:1px solid var(--hairline)}
.faq details{border-bottom:1px solid var(--hairline)}
.faq summary{
  cursor:pointer;font-weight:600;font-size:1.05rem;
  padding:1.1rem .25rem;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:1rem;
  list-style:none;transition:color 0.15s ease-out;
}
.faq summary:hover{color:var(--leaf-deep)}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"+";font-family:"Fraunces",serif;font-size:1.4rem;color:var(--leaf-deep);
  flex:none;line-height:1;transition:transform .2s ease-out;
}
.faq summary:hover::after{transform:scale(1.15)}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq details p{padding:0 .25rem 1.25rem;color:var(--ink-2);max-width:64ch;margin:0}
/* Ported off the deleted .prose .sl-faq twin (2026-08-06): interior pages that
   close a FAQ group and go straight into the CTA don't need the extra rule —
   the accordion's own last border already draws one. Repointing it at .faq
   makes that consistent on every page instead of only the sl-faq ones. */
.prose .faq + .wp-block-separator{display:none}

/* ===== Prose (page.php / single.php body) ===== */
.prose{color:var(--ink);font-size:1.0625rem}
.prose > *{max-width:var(--measure)}
.prose h2{margin-top:2.4rem}
.prose h3{margin-top:2rem;color:var(--ink)}
.prose p{margin:0 0 1.2em}
.prose ul,.prose ol{margin:0 0 1.3em;padding-left:1.3rem}
.prose li{margin-bottom:.5rem}
.prose li::marker{color:var(--leaf-deep)}
.prose a{font-weight:500}
.prose hr{border:none;border-top:1px solid var(--hairline);margin:2.6rem 0;max-width:var(--measure)}
.prose figure{margin:2.2rem 0;max-width:none}
.prose figure img{display:block;width:100%;border-radius:var(--radius)}
.prose figcaption{font-size:.9rem;color:var(--ink-2);margin-top:.7rem}
.prose blockquote{
  margin:2.2rem 0;padding:.4rem 0 .4rem 1.6rem;
  border-left:3px solid var(--leaf-deep);
  font-family:"Fraunces",Georgia,serif;font-size:1.3rem;line-height:1.4;color:var(--ink);
  font-weight:500;max-width:60ch;
}
.prose blockquote p{margin:0}
.prose blockquote cite{display:block;margin-top:.8rem;font-family:"Epilogue",sans-serif;font-size:.95rem;font-style:normal;color:var(--ink-2);font-weight:600}

/* ===== Chips (404 suggestion links) =====
   2026-08-06: the blog/archive/search subsystem this once belonged to
   (.article-meta .pill .byline .avatar .post-list .post-row .chips .pagination
   .searchform .result .img-ph .breadcrumb, ~96 lines) was deleted — no blog
   exists and the nav link is commented out in header.html. `.chip` survives
   because 404.html's .suggest row uses it. */
.chip{
  display:inline-flex;align-items:center;min-height:40px;padding:.4rem 1rem;
  border-radius:999px;border:1px solid var(--hairline);background:#fff;
  color:var(--ink-2);text-decoration:none;font-size:.92rem;font-weight:600;
  transition:all .15s ease;
}
.chip:hover{border-color:var(--leaf-deep);color:var(--leaf-deep)}
.chip[aria-current="true"]{background:var(--leaf-deep);color:#fff;border-color:var(--leaf-deep)}
/* On the forest hero shell the white pill disappears — same treatment
   .on-dark .btn-outline gets. Replaces three identical inline styles on
   404.html's chips. (2026-08-06) */
.on-dark .chip{background:transparent;color:var(--leaf-pale);border-color:var(--leaf-pale)}  /* 11.5:1 */
.on-dark .chip:hover{background:var(--forest-2);color:#fff;border-color:var(--leaf-pale)}    /* 9.0:1 */

/* ===== 404 ===== */
.notfound{text-align:center;padding-block:clamp(3rem,8vw,6rem)}
.notfound .code{
  font-family:"Fraunces",Georgia,serif;font-weight:600;color:var(--leaf-pale);
  font-size:clamp(5rem,18vw,11rem);line-height:.9;letter-spacing:-.03em;margin:0 0 .4rem;
}
.notfound h1{color:var(--canvas);font-size:clamp(1.8rem,4vw,2.6rem);margin:0 auto .9rem;max-width:18ch}
.notfound p{color:var(--leaf-pale);margin-inline:auto;max-width:48ch}
.notfound .hero-ctas{justify-content:center;margin-top:2rem}
.notfound .suggest{margin-top:2.6rem;display:flex;flex-wrap:wrap;gap:.6rem;justify-content:center}

/* ===== Contact form ===== */
.contact-grid{display:grid;gap:clamp(2rem,5vw,4rem);grid-template-columns:1fr 1.2fr;margin-top:1rem;align-items:start}
@media (max-width:860px){.contact-grid{grid-template-columns:1fr}}
form{display:grid;gap:1.1rem}
.form-card{background:#fff;border:1px solid var(--hairline);border-radius:var(--radius);padding:clamp(1.5rem,3vw,2.2rem);box-shadow:0 6px 22px rgba(30,42,35,.04)}
.field-row{display:grid;gap:1.1rem;grid-template-columns:1fr 1fr}
@media (max-width:560px){.field-row{grid-template-columns:1fr}}
label{font-weight:600;font-size:.95rem;display:block;margin-bottom:.35rem}
/* "(optional)" qualifier inside a <label> — drops back to body weight and the
   secondary ink so it reads as a note, not part of the field name. Replaces four
   identical inline styles (home.html ×2, contact.html ×2). (2026-08-06) */
.label-optional{font-weight:400;color:var(--ink-2)}
input,select,textarea{
  width:100%;font:inherit;color:var(--ink);
  background:#fff;border:1px solid var(--hairline);border-radius:10px;
  padding:.7rem .85rem;min-height:48px;
  transition:border-color 0.2s ease, box-shadow 0.2s ease;
}
textarea{min-height:7rem;resize:vertical}
/* Native select chrome doesn't match the hairline + 10px-radius fields around it,
   so draw our own chevron. background-image, not a pseudo-element: select can't
   host one. */
select{
  appearance:none;-webkit-appearance:none;
  padding-right:2.6rem;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1L7 7L13 1' stroke='%231E2A23' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 1rem center;
  background-size:14px 9px;
}
select::-ms-expand{display:none}
input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:none;border-color:var(--focus);box-shadow:0 0 0 3px rgba(27, 95, 176, 0.2);
}
.form-note{font-size:.92rem;color:var(--ink-2);margin:0}
/* The trust line that closes a form card is a SIBLING after </form>, not the
   form's last child, so `form{gap:1.1rem}` no longer separates it — without this
   it butts flush against the submit button. Restates the same 1.1rem so the
   rhythm is unchanged from when it lived inside the grid. Child combinator: the
   other .form-note on home.html (the "Prefer email?" line in the left column) is
   not in a .form-card and must keep margin:0. (2026-08-06) */
.form-card > .form-note{margin-top:1.1rem}
/* Submit status region (2026-08-06). theme.js writes "Sending your message…"
   here, then swaps in an error string AND sets role="alert" on failure — so the
   error state keys off that attribute rather than a second class. Empty is the
   resting state on every page load, so it must not reserve a grid row. */
.form-status{margin:0;font-size:.92rem;font-weight:600;color:var(--ink-2);max-width:var(--measure)}
.form-status:empty{display:none}
.form-status[role="alert"]{color:var(--error)}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.contact-aside{display:grid;gap:1.6rem}
.contact-aside .item{display:grid;gap:.2rem}
.contact-aside .item .k{font-size:.78rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--leaf-deep)}
.contact-aside .item a{font-weight:500}

/* ===== Footer ===== */
.site-footer{background:var(--forest);color:var(--leaf-pale);padding-block:clamp(2.25rem,4.5vw,3.5rem) 2rem}
.foot-cols{display:grid;gap:2.5rem;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr))}
.foot-cols h3{font-family:"Epilogue",sans-serif;font-size:.85rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--leaf-pale)}
.foot-cols ul{list-style:none;margin:0;padding:0}
.foot-cols li{margin-bottom:.15rem}
.foot-cols a{color:var(--leaf-pale);text-decoration:none;display:inline-flex;align-items:center;min-height:44px}
/* No hover underline — every footer link sits in a headed <ul> with no body copy
   beside it, so WCAG 1.4.1 / F73 doesn't engage (and a hover-only underline was
   never a valid 1.4.1 cue regardless; F73 requires it in the resting state).
   Focus still gets the global 3px :focus-visible outline. */
.foot-cols a:hover{color:#fff}
.legal{border-top:1px solid var(--forest-2);margin-top:3rem;padding-top:1.5rem;font-size:.9rem;color:#B9CBBD}
.legal p{margin:0;max-width:none}
/* Trademark line — smallest type on the site, so it stops at .8rem rather than
   the .72rem it carried as an inline style. #90A698 on forest = 5.2:1. */
.legal .tm{margin-top:.6rem;font-size:.8rem;line-height:1.55;color:#90A698;max-width:78ch}

/* ===== Scroll reveal ===== */
.reveal{opacity:0;transform:translateY(14px);transition:opacity .6s cubic-bezier(.22,1,.36,1),transform .6s cubic-bezier(.22,1,.36,1)}
.reveal.in{opacity:1;transform:none}
/* No-JS fallback: never hide content if the reveal script doesn't run */
.no-js .reveal{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none}}

/* ===== Visually hidden =====
   .sr-only is the site's name (build.js + markup); .screen-reader-text is the
   WP convention theme.js still injects into the nav toggles. The two rules were
   byte-identical duplicates 500 lines apart — merged 2026-08-06. */
.sr-only,
.screen-reader-text{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* ============================================================
   INTERIOR-PAGE BRIDGE
   Interior pages were authored against the legacy "Skill Loop"
   design system (sl- classes) and ship as plain Gutenberg blocks.
   This maps both onto the GroveDeck look so page bodies carry the
   same styling as the homepage. Everything is scoped to .prose, so
   the front page (front-page.php) is never affected.
   ============================================================ */

/* --- Legacy sl- accents (recoloured for the LIGHT body) --------
   2026-08-06: .sl-eyebrow / .sl-hero-tagline / .sl-vertical-note / .sl-compare
   and the whole <details class="sl-faq"> accordion were deleted — the sl-faq
   markup moved to the homepage's `<div class="faq"><details>` pattern (see
   ".faq" above, which now also owns the "hide the separator after a FAQ group"
   rule) and the other three had no markup left anywhere. .sl-trust-line stays:
   it is part of the shared closing-CTA contract below. */
.prose > .sl-trust-line{color:var(--ink-2);font-size:.95rem;margin-top:1.4rem}

/* --- Dark forest band: the shared closing CTA ------------------
   2026-08-06: adopted. Every interior page now closes with
     <div class="sl-cta-band"><h2>…</h2><p>…</p>
       <a class="btn btn-primary" href="/contact/">…</a>
       <p class="sl-trust-line">…</p></div>
   Colours are lifted verbatim from the homepage .cta-shell.on-dark
   (forest fill, canvas headings, leaf-pale body + trust line) so the two read
   as the same band. Geometry can't match: .cta-shell is a full-bleed section
   with waves top and bottom, and this sits inside .prose — so it borrows the
   homepage .engine's 32px rounded-card shape instead.
   `max-width:none` here is a descendant selector, so it frees the band at ANY
   nesting depth (bare in .prose, inside .wp-block-group, or inside
   .wp-block-group__inner-container). The old .wp-block-group > .sl-cta-band
   duplicate below was removed as redundant. */
.prose .sl-cta-band,
.prose .sl-engine-band{
  background:var(--forest);color:var(--canvas);
  border-radius:32px;padding:clamp(2rem,5vw,3.25rem);
  margin:2.5rem 0;max-width:none;
}
/* Element selectors, not :where(h2,h3) — :where() is 0-specificity, so the
   interior-rhythm rule `.prose h2{margin-top:4.5rem}` further down would
   otherwise win and drop 4.5rem of dead space inside the band's padding. */
.prose .sl-cta-band h2,.prose .sl-cta-band h3,
.prose .sl-engine-band h2,.prose .sl-engine-band h3{color:var(--canvas);max-width:24ch;margin-top:0}
.prose .sl-cta-band p,
.prose .sl-engine-band p{color:var(--leaf-pale);max-width:60ch}
/* Plain links go white; the .btn CTA keeps its own fill (below). */
.prose .sl-cta-band a:not(.btn),
.prose .sl-engine-band a:not(.btn){color:#fff}
.prose .sl-cta-band .sl-trust-line,
.prose .sl-engine-band .sl-trust-line{color:var(--leaf-pale);font-size:.95rem;margin-top:1.4rem}
/* Degenerate-band guard (2026-08-06). The band is a shared contract but its
   contents vary: 13 bands in all — 8 ship h2 + p + .btn + trust line, four ship NO h2
   (about, verticals, trust, content-development — the p leads), and
   workforce-readiness ships only .btn + trust line. Two ways that went wrong:
   (a) the closing .sl-trust-line is a <p>, so `p{margin:0 0 1.1em}` added ~19px
   under it that the top of the band had no counterpart for — every band was
   bottom-heavy inside its own padding, degenerate or not;
   (b) with no h2, whatever leads (p, or a bare .btn) must not carry a top margin
   into the padding. `.prose .sl-cta-band h2{margin-top:0}` only covered the h2
   case. Zero-margin on the first and last child makes the padding symmetric in
   all three shapes. The band cannot collapse — its padding is unconditional. */
.prose .sl-cta-band > :first-child,
.prose .sl-engine-band > :first-child{margin-top:0}
.prose .sl-cta-band > :last-child,
.prose .sl-engine-band > :last-child{margin-bottom:0}

/* --- Default Gutenberg blocks → GroveDeck look ----------------- */
/* Hand-coded .btn inside a page body (2026-08-06): `.prose a{font-weight:500}`
   is (0,1,1) and outranks `.btn{font-weight:600}` at (0,1,0), so an interior
   .btn rendered a weight lighter than the identical button on the homepage.
   Same class of bug the .site-nav .menu-cta rule documents further up. */
.prose a.btn{font-weight:600}
/* Buttons mirror .btn / .btn-primary / .btn-outline — including the tonal-only
   interaction model (v1.1.61). These used to lift WITHOUT the shadow bloom that
   .btn:hover applied, so the same button behaved differently depending on whether
   a template or the block editor rendered it. */
.prose .wp-block-button__link{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:48px;padding:.7rem 1.6rem;border-radius:999px;
  font-weight:600;font-size:1rem;text-decoration:none;
  border:1px solid transparent;background:var(--honey);color:var(--ink);
  box-shadow:0 5px 24px -9px rgba(6,14,10,.36);
  transition:background-color .18s ease-out,border-color .18s ease-out,
             color .18s ease-out,filter .18s ease-out;
}
.prose .wp-block-button__link:hover{background:var(--honey-dark)}
.prose .wp-block-button__link:active{filter:brightness(.94)}

/* The dark interior bands carry no .on-dark class, so anything focused inside
   them inherited --focus #1B5FB0 at 2.14:1 on forest — under the 3:1 non-text
   minimum (WCAG 1.4.11). Same override the header/footer already use. */
.prose .sl-cta-band :focus-visible,
.prose .sl-engine-band :focus-visible{outline-color:#9CC4F0}
/* .btn is the shared CTA-band contract (2026-08-06); .wp-block-button__link is
   kept alongside it for any block-editor button still in a body. Both keep the
   honey fill on forest — the warm colour carries the emphasis, same as the
   homepage .cta-shell.on-dark. */
.prose .sl-cta-band .btn-primary,
.prose .sl-engine-band .btn-primary,
.prose .sl-cta-band .wp-block-button__link,
.prose .sl-engine-band .wp-block-button__link{background:var(--honey);color:var(--ink)}
.prose .sl-cta-band .btn-primary:hover:not(:disabled),
.prose .sl-engine-band .btn-primary:hover:not(:disabled){background:var(--honey-dark);color:var(--ink)}
/* Mirror .on-dark .btn-outline (:117) for the secondary slot, since the band
   itself can't carry .on-dark — it lives inside .prose. */
.prose .sl-cta-band .btn-outline,
.prose .sl-engine-band .btn-outline{background:transparent;color:var(--leaf-pale);border-color:var(--leaf-pale)}
.prose .sl-cta-band .btn-outline:hover:not(:disabled),
.prose .sl-engine-band .btn-outline:hover:not(:disabled){background:var(--forest-2);color:var(--leaf-pale)}

/* Column groups → soft cards, echoing the homepage vert/service grids
   ------------------------------------------------------------------
   2026-08-06 (P0): this was three competing `.prose .wp-block-columns` rules in
   three different sections of the file, and NONE of them set `display` — the
   flex came from WordPress core layout CSS that this static build never ships,
   so ~20 grids across 11 pages rendered as stacked full-width blocks. Merged
   into this one rule and given a real responsive grid. Winning values from the
   old cascade are preserved (margin-block 3rem, gap 1.5rem, max-width none).
   `min(15rem,100%)` instead of a bare 15rem track floor: auto-fit already drops
   to a single column once the container is under two tracks wide, and the
   min() stops that last track overflowing on containers narrower than 15rem —
   so it degrades to one column with no breakpoint of its own.
   `.stage-grid` (v1.1.12, further down) still overrides this with its bespoke
   6-track layout — it is one class more specific and comes later. */
.prose .wp-block-columns{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(15rem,100%),1fr));
  align-items:stretch;
  gap:1.5rem;margin:3rem 0;max-width:none;
}
/* Six-or-more-card blocks: pin to 3 tracks (2026-08-06).
   At the design width the container is ~1072px, so auto-fit resolves to FOUR
   240px-floor tracks. Card counts in the wild are 2, 3, 4 and 6 — 2/3/4 all fill
   their rows evenly at four tracks, but 6 lands as 4 + a ragged 2 with two empty
   tracks trailing (integrations, workforce-readiness, first block on each).
   Forcing three tracks gives 3 + 3 and, as a bonus, the same ~341px card width
   as every 3-card block on the vertical pages, so the two shapes agree.
   Below 900px auto-fit's 2-track result is already correct for six cards. */
@media (min-width:900px){
  .prose .wp-block-columns:has(> .wp-block-column:nth-child(6)){
    grid-template-columns:repeat(3,1fr);
  }
}
/* Two column blocks back to back are one visual grid split across two <div>s —
   healthcare / government / higher-education each close with 2 + 2 cards under a
   single h2. Their 3rem margins collapse to a 48px seam against the grid's own
   24px gap, which reads as two unrelated blocks. Match the gap so the four cards
   read as a 2×2. No heading ever sits between these; every other column block on
   the site is preceded by an h2 and keeps the full 3rem. (2026-08-06) */
.prose .wp-block-columns + .wp-block-columns{margin-top:1.5rem}
.prose .wp-block-column{
  padding:1.25rem;border-radius:var(--radius);
  transition:transform .2s ease, box-shadow .2s ease;
}
.prose .wp-block-column:hover{transform:translateY(-4px);box-shadow:0 14px 32px rgba(30,42,35,.10)}
.prose .wp-block-column > :first-child{margin-top:0}
.prose .wp-block-column :where(h3,h4){margin-bottom:.4rem;color:var(--ink)}
.prose .wp-block-column p{color:var(--ink-2);font-size:.97rem}

/* Separators */
.prose .wp-block-separator{
  border:none;border-top:1px solid var(--hairline);
  margin:2.6rem 0;max-width:var(--measure);opacity:1;
}


/* ============================================================
   INTERIOR-PAGE WIDTH + CARD GRIDS (v1.0.6)
   Page bodies ship wrapped in one constrained wp:group. `.prose > *`
   was capping that group at --measure, squeezing card grids, tables
   and bands into the reading column. Free structural blocks to the
   full container while keeping running text at a readable measure so
   interior pages use the page width like the homepage.
   ============================================================ */

/* Let the wrapping group(s) span the full content container */
.prose > .wp-block-group,
.prose > .wp-block-group > .wp-block-group{max-width:none}

/* Keep running text readable even when the group is full width.
   2026-08-06: the __inner-container arm was added because Gutenberg's
   constrained-layout groups wrap their children one level deeper — the lists on
   managed-administration and content-development are children of
   .wp-block-group__inner-container, not of .wp-block-group, so they escaped
   this cap and ran the full 72rem while the paragraphs around them stopped at
   70ch.
   2026-08-06 (re-verified after the page refactor): ALL 15 body pages now open
   with <div class="wp-block-group"><div class="wp-block-group__inner-container">
   and nothing on any page is a text-level direct child of .wp-block-group any
   more — the __inner-container arm is what actually carries the cap site-wide,
   and it covers every page. Nesting is exactly two deep everywhere; no page
   adds a third group. The .wp-block-group arm is kept as the fallback for a body
   authored without the inner wrapper. Structural children (.wp-block-columns,
   .sl-cta-band, figure) are deliberately absent from the :where() list so they
   stay full width; hr/.wp-block-separator are capped by their own rules above. */
.prose .wp-block-group > :where(h1,h2,h3,h4,h5,h6,p,ul,ol,dl,blockquote,pre,.wp-block-heading,.wp-block-list),
.prose .wp-block-group__inner-container > :where(h1,h2,h3,h4,h5,h6,p,ul,ol,dl,blockquote,pre,.wp-block-heading,.wp-block-list){
  max-width:var(--measure);
}

/* (The `.prose .wp-block-columns` duplicate that lived here, and the
   `.wp-block-group > .sl-cta-band` / `.sl-engine-band` full-width duplicates,
   were folded into their canonical rules above on 2026-08-06. The
   `.wp-block-table` arm went with the deleted table block.) */

/* Column groups → real cards so a full-width grid reads as cards
   (echoes the homepage .vert-grid / .service surfaces) */
.prose .wp-block-column{
  background:color-mix(in srgb,var(--canvas-2) 55%,#fff);
  border:1px solid color-mix(in srgb,var(--hairline) 55%,transparent);
  border-radius:var(--radius);
  padding:1.4rem 1.45rem 1.55rem;
}
.prose .wp-block-column > :where(h2,h3,h4,p){max-width:none}
.prose .wp-block-column > :first-child{margin-top:0}

/* Linked column cards → whole card clickable, no title underline
   (echoes the homepage .vert-grid / .service stretched-link pattern, v1.1.37) */
.prose .wp-block-column:has(> :where(h2,h3) > a){
  position:relative;
  transition:transform .18s ease, box-shadow .18s ease;
}
.prose .wp-block-column:has(> :where(h2,h3) > a):hover{
  transform:translateY(-5px);
  box-shadow:0 14px 32px rgba(30, 42, 35, 0.12);
}
.prose .wp-block-column > :where(h2,h3) > a{text-decoration:none}
.prose .wp-block-column > :where(h2,h3) > a:hover{text-decoration:none}
.prose .wp-block-column > :where(h2,h3) > a::after{content:"";position:absolute;inset:0;border-radius:var(--radius)}
.prose .wp-block-column > :where(h2,h3) > a:focus-visible{outline:none}
.prose .wp-block-column > :where(h2,h3) > a:focus-visible::after{outline:3px solid var(--focus);outline-offset:2px}


/* ============================================================
   INTERIOR-PAGE SECTION SPACING (v1.0.9)
   Spacers were removed from page bodies; the theme's native ~2.4rem
   heading margin left sections too tight. Give each section heading
   more room above so sections breathe (homepage uses banded sections
   for this; interior bodies rely on heading rhythm).
   ============================================================ */
.prose h2{margin-top:4.5rem}
/* (the extra air around full-width card grids now lives on the single
   consolidated `.prose .wp-block-columns` rule — 2026-08-06) */
/* don't push the very first block down (hero already provides the gap) */
.prose > .wp-block-group > :first-child,
.prose > .wp-block-group > .wp-block-group__inner-container > :first-child,
.prose > :first-child{margin-top:0}


/* ============================================================
   HOW-WE-WORK STAGE GRID (v1.1.12)
   The five stage cards ship as one .wp-block-columns.stage-grid.
   Render as a uniform 3-up grid: identical width (repeat(3,1fr)) and
   identical height within each row (align-items:stretch). grid-auto-rows
   is auto, not 1fr — 1fr forced row two to match row one's height and left
   cards 4 and 5 carrying ~120px of dead space. Stacks on mobile.
   ============================================================ */
.prose .wp-block-columns.stage-grid{
  display:grid;grid-template-columns:repeat(6,1fr);grid-auto-rows:auto;
  align-items:stretch;gap:1.5rem;max-width:none;
}
/* each card spans two of six tracks = one third wide; the last two are
   shifted in by one track so row two centers under row one (inverted pyramid) */
.prose .wp-block-columns.stage-grid > .wp-block-column{margin:0;flex-basis:auto;grid-column:span 2;}
.prose .wp-block-columns.stage-grid > .wp-block-column:nth-child(4){grid-column:2 / span 2;}
@media (max-width:781px){
  .prose .wp-block-columns.stage-grid{grid-template-columns:1fr;grid-auto-rows:auto;}
  .prose .wp-block-columns.stage-grid > .wp-block-column,
  .prose .wp-block-columns.stage-grid > .wp-block-column:nth-child(4){grid-column:auto;}
}


/* ============================================================
   HIRING-QUESTIONS READING MEASURE (v1.1.63, rescoped 2026-08-06)
   /cornerstone-admin-hiring-questions/ is long-form: its blocks sit
   directly in .prose with no wp:group wrapper and no full-width
   structural blocks (columns/bands), so every element — paragraphs,
   the four long checklists, separators — sits in the same 70ch column
   and the page reads far narrower than sibling pages, whose card grids
   fill the 72rem container. Measured: all running paragraphs site-wide
   are an identical 677px; only composition differs.
   Fix: bump the measure for this page only to the top of the
   documented 70–80ch range via the --measure token, so p/ul/hr widen
   together (677px → ~774px) and stay in proportion. Global .prose
   untouched.
   SCOPE: this shipped as `body.page-id-452`, a WordPress post ID copied
   over during the static port — build.js emits body_class from front
   matter and never emits a numeric page id, so the selector matched
   nothing and the page had been rendering at the wrong measure since
   the port. Now keyed off the slug class the page's front matter sets
   ("body_class": "page hiring-questions"), which is the only body-class
   mechanism this build has.
   ============================================================ */
body.hiring-questions .prose{--measure:80ch}

/* ============================================================
   Static-site additions (not in the WP theme.css)
   1. Inline overrides ported from functions.php wp_add_inline_style()
   2. Consent banner (in-house consent, replaces the old WP plugin)
   ============================================================ */

/* --- 1. Ported WP inline overrides --- */
/* (The `.prose .sl-faq:last-of-type` bottom-border restoration went out with
   the .sl-faq accordion on 2026-08-06 — `.faq details` gives every question a
   bottom border already, including the last, so the behaviour it was patching
   back in is now the default.) */
/* Optical vertical centering for framed nav labels (selected-page pills + CTA). */
.site-nav .menu-cta>a.btn{padding-top:calc(.7rem + 1.5px);padding-bottom:calc(.7rem - 1.5px)}
.site-nav>.menu>.menu-item:not(.menu-item-has-children).current-menu-item>a:not(.btn),
.site-nav>.menu>.menu-item-has-children.current-menu-ancestor>a,
.site-nav>.menu>.menu-item-has-children.current-menu-item>a
{padding-top:calc(.5rem + 1.5px);padding-bottom:calc(.5rem - 1.5px)}
.site-nav .sub-menu .current-menu-item>a{padding-top:calc(.62rem + 1.5px);padding-bottom:calc(.62rem - 1.5px)}
/* Emphasized words in H1 headings render upright (no italic). */
h1 em{font-style:normal}

/* --- 2. Consent banner --- */
.gd-consent{
  position:fixed;left:1rem;right:1rem;bottom:1rem;z-index:200;
  max-width:34rem;margin-inline:auto;
  background:var(--canvas);color:var(--ink);
  border:1px solid var(--hairline);border-radius:var(--radius);
  box-shadow:0 14px 44px -14px rgba(6,14,10,.4);
  padding:1.15rem 1.35rem;
}
.gd-consent[hidden]{display:none}
.gd-consent p{margin:0 0 .9rem;font-size:.95rem;line-height:1.55;max-width:none}
.gd-consent a{color:var(--leaf-deep)}
.gd-consent-actions{display:flex;gap:.6rem;flex-wrap:wrap}
.gd-consent .btn{min-height:44px;padding:.55rem 1.35rem;font-size:.95rem;box-shadow:none}
@media (max-width:480px){
  .gd-consent{left:.6rem;right:.6rem;bottom:.6rem;padding:1rem 1.1rem}
  .gd-consent .btn{flex:1}
}

/* ---- 404 full-height band (static port, rescoped 2026-08-06) ---------------
   WP's header.php printed these as inline styles when is_404(), and the static
   port kept them pointed at .hero-shell:
     body.error404 .hero-shell{min-height:100vh;display:flex;flex-direction:column}
     body.error404 .hero-shell>.wrap{flex:1;display:flex;flex-direction:column}
   That was correct only while 404.html nested <main> inside .hero-shell > .wrap.
   The page refactor made <main> a sibling (matching inner.html and home.html),
   so .hero-shell holds nothing but the site header — 100vh on it inflated the
   header into a full viewport of empty forest and pushed the 404 message below
   the fold. Deleted.
   Re-pointed at the container the 404 body actually lives in
   (main > .cta-shell.on-dark > section > .wrap.reveal > .notfound) so the intent
   — the message occupies the viewport rather than floating in a short page —
   survives. 12rem is the header plus the top of the footer wave. On desktop the
   .notfound content already exceeds this and the rule is inert; on phones, where
   the clamps bottom out, it is what stops the band ending halfway up the screen.
   Everything on this page is forest, so growing the band adds no visible seam. */
body.error404 .cta-shell{
  min-height:calc(100vh - 12rem);
  display:flex;flex-direction:column;justify-content:center;
}
