/* ============================================================
   Forging Tomorrow — ft-build.css
   Native block theme (FSE) build, replacing GeneratePress.

   Brand-signature styling on top of theme.json's palette/font tokens.
   Values below are pixel-measured against production
   (https://forgingtomorrow.com/, headless Chrome + getComputedStyle,
   2026-07-06) so this file is the exact production match, not an
   approximation: section padding 96px (--ft-space-9), hero 120/96,
   card padding 32px/radius 14px/shadow-sm, button 15px 26px/radius 8px.
   ============================================================ */

:root{
  --ft-orange:#F06020; --ft-orange-600:#D9521A; --ft-orange-700:#B8430F; --ft-orange-50:#FEF3EB;
  --ft-teal:#0E4A48;
  --ft-teal-light:#1D6B67;
  --ft-teal-deep:#09302E;
  --ft-teal-muted:#516A68;
  --ft-ink:#0F0F10; --ft-ink-2:#2A2A2C; --ft-ink-3:#5C5C60; --ft-ink-4:#8C8C92;
  --ft-line:#E6E1DA; --ft-line-strong:#C9C2B7;
  --ft-paper:#FFFFFF; --ft-cream:#FBF7F2; --ft-cream-2:#F4EEE5; --ft-charcoal:#1A1A1B; --ft-charcoal-2:#232325;
  --ft-fg-on-dark:#FBF7F2;
  /* "The Storyteller" type pair — Newsreader (display) + Archivo (body/UI),
     locked by Brent 2026-07-12, replacing Fraunces + Inter theme-wide. Newsreader
     has no SOFT/WONK/oversized-opsz axes (Fraunces-only), so --ft-disp is retired;
     display headings instead carry a flat weight/tracking pair (see .disp below). */
  --ft-font-display:'Newsreader','Iowan Old Style','Cambria',Georgia,serif;
  --ft-font-body:'Archivo',system-ui,-apple-system,'Helvetica Neue',Arial,sans-serif;
  --ft-container:1240px;
  --ft-space-9:96px;
  /* ---- Locked hero feather baseline (Iteration 3, Brent, 2026-07-12) ---- */
  --ft-opacity:0.21;
  --ft-drift-amt:1.36;
  --ft-drift-dur:16s;
  --ft-shadow:0.88;
  /* full spacing scale + text/link tokens — page-scoped CSS (e.g. the privacy
     page) consumes these; leaving any undefined collapses that padding to 0 */
  --ft-space-4:16px; --ft-space-5:24px; --ft-space-6:32px; --ft-space-7:48px; --ft-space-8:64px;
  --ft-text-base:16px;
  --ft-link:var(--ft-orange-700);
  --ft-radius-btn:8px;
  --ft-radius-lg:14px;
  --ft-radius-xl:22px;
  --ft-shadow-sm:0 2px 6px rgba(15,15,16,.06),0 1px 2px rgba(15,15,16,.04);
  --ft-shadow-md:0 8px 24px rgba(15,15,16,.08),0 2px 6px rgba(15,15,16,.04);
  --ft-shadow-lg:0 24px 60px rgba(15,15,16,.12),0 6px 14px rgba(15,15,16,.06);
}

/* .wrap.wrap: specificity workaround — WP's global-styles-inline-css prints
   `.has-global-padding{padding-left/right:var(--wp--style--root--padding-*)}`
   (an unset custom property, resolving to 0) after this stylesheet at the
   same 0,1,0 specificity as the bare .wrap rule, which wins the horizontal
   padding by source order and left every section flush to the viewport edge
   at mobile (no max-width to fall back on there, unlike desktop where
   margin:auto + max-width masked the loss). Same fix pattern as .band.band. */
.wrap.wrap{max-width:var(--ft-container);margin:0 auto;padding:0 32px}

/* type helpers */
/* eyebrow color by ground (WCAG AA for 12px text needs 4.5:1). The SAFE color
   (orange-700) is the DEFAULT so any NEW light-ground section is AA out of the
   box; dark sections opt in explicitly:
   - charcoal grounds (process): full --ft-orange — #F06020 on #1A1A1B ≈ 5.6:1, passes.
   - TEAL grounds (hero, testimonial): CREAM — #F06020 on #0E4A48 is only ≈3.06:1, fails
     (Brent's call 2026-07-10 post-launch: cream wherever orange conflicts with its ground). */
.eyebrow{font-family:var(--ft-font-body);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.18em;color:var(--ft-orange-700);display:inline-block;margin:0 0 18px}
.process .eyebrow{color:var(--ft-orange)}
.hero .eyebrow{color:var(--ft-cream)}
.lede{font-size:20px;line-height:1.55;color:var(--ft-ink-2);font-weight:400}
.disp{font-family:var(--ft-font-display);font-weight:500;letter-spacing:-.005em;color:var(--ft-ink)}
.disp em{font-style:italic}
.dot{color:var(--ft-orange)}
/* Services/Process/Contact/Hybrid H2 periods are ink, not orange (only the
   hero and Team-section headline periods stay orange) */
.dot.ink{color:var(--ft-ink)}

/* buttons — 8px radius, matching production.
   Two different markup shapes carry the .btn box (padding/radius/fill):
   1. Gutenberg buttons: outer <div class="wp-block-button btn btn-primary"> is the
      shape-carrying wrapper; inner <a class="wp-block-button__link"> must fully
      reset and inherit the shape from it.
   2. The contact form's native <button class="btn btn-primary">: there is no
      separate wrapper, the button element itself IS the shape-carrying box, so it
      keeps .btn's padding/radius and only needs GP's grey fill/color neutralized.

   GeneratePress prints `button, ..., a.wp-block-button__link:not(.has-background)
   {color:#fff;background:#55555e}` (specificity 0,2,1 for the anchor branch, 0,0,1
   for the bare `button` branch) after this stylesheet, and Gutenberg core prints
   `:where(.wp-block-button__link){border-radius:9999px}` (0 specificity, easy to
   beat). `.wp-block-button.btn > .wp-block-button__link` is 3 classes (0,3,0),
   beating GP's 0,2,1 on class-count alone. `.btn-primary`/`.btn-plain` (0,1,0)
   already beats the bare `button` branch's 0,0,1, so no !important needed anywhere. */
.btn{display:inline-flex;align-items:center;gap:10px;font-family:var(--ft-font-body);font-weight:600;font-size:17px;padding:15px 26px;border-radius:var(--ft-radius-btn);cursor:pointer;text-decoration:none;transition:all .2s ease;border:1.5px solid transparent;background:transparent;color:inherit;line-height:1}
.btn svg{width:18px;height:18px}
/* CLICK-AREA FIX (2026-07-10, was the "KNOWN ISSUE" tap-target gap): for
   Gutenberg buttons the padding now lives on the ANCHOR, not the outer div —
   the div's padding is zeroed so it shrink-wraps the anchor, making the
   clickable element exactly the visible box (was: 13-18px text-height anchor
   floating in a 31-42px padded div; clicks on the padding hit the div = dead).
   The div keeps background/border/radius/shadow/hover-transform (parent :hover
   fires while hovering the child, so all interaction states still work), so
   rendering is pixel-identical. The form's native <button class="btn"> is
   unaffected: it has no inner anchor, keeps .btn's own padding, and was never
   broken. EVERY padding override for Gutenberg buttons must target the anchor
   (.wp-block-button.X > .wp-block-button__link), never the outer div — the
   nav/mobile overrides below follow this. */
.wp-block-button.btn{padding:0}
.wp-block-button.btn > .wp-block-button__link{display:inline-flex;align-items:center;gap:9px;color:inherit;text-decoration:none;background:transparent;padding:15px 26px;border:0;border-radius:inherit;box-shadow:none;font:inherit;line-height:1}
.wp-block-button.btn > .wp-block-button__link:focus-visible{outline:2px solid var(--ft-orange);outline-offset:2px}
/* GP's hover/focus/active rule for a.wp-block-button__link:not(.has-background) ties this
   selector on specificity (0,3,1); pin transparent explicitly so a cascade-order flip can't
   bring the grey back on interaction states. */
.wp-block-button.btn > .wp-block-button__link:hover,
.wp-block-button.btn > .wp-block-button__link:focus,
.wp-block-button.btn > .wp-block-button__link:active{background:transparent}
/* accessibility fix: white-on-orange (#F06020) fails WCAG AA; --ft-orange-700
   (#B8430F) gives ~5.5:1. Every filled primary button (header/hero/hybrid/
   contact submit) picks this up via the shared .btn-primary class. Hover/active
   motion matches production's .ft-btn-primary exactly (lift + growing shadow). */
.btn-primary{background:var(--ft-orange-700);color:#fff;box-shadow:0 6px 18px rgba(240,96,32,.22)}
.btn-primary:hover{background:var(--ft-orange-600);color:#fff;transform:translateY(-1px);box-shadow:0 12px 30px rgba(240,96,32,.32)}
.btn-primary:active{transform:translateY(0) scale(.98);background:var(--ft-orange-700)}
.btn:focus-visible{outline:2px solid var(--ft-orange);outline-offset:2px}
.btn-ghost{background:transparent;color:var(--ft-ink);border-color:var(--ft-line-strong)}
.btn-ghost:hover{border-color:var(--ft-ink)}
/* hero secondary CTA — bordered ghost button on the teal field (cream text/
   border), matching production; replaces the old plain-text-link treatment */
.btn-ghost-hero{background:transparent;color:var(--ft-cream);border-color:rgba(251,247,242,.45)}
.btn-ghost-hero:hover{border-color:var(--ft-cream);background:rgba(251,247,242,.08)}
.link-arrow{display:inline-flex;align-items:center;gap:8px;font-weight:600;color:var(--ft-orange-700);text-decoration:none}
.link-arrow:hover{gap:12px}
.link-arrow svg{width:17px;height:17px}
.link-arrow.ink{color:var(--ft-ink)}

/* header — muted teal bar, white wordmark (native FSE template part:
   wp:site-logo + wp:navigation, block markup differs from the old
   hand-authored header, selectors below target the block-rendered DOM) */
header.site{position:sticky;top:0;z-index:50;background:var(--ft-teal-muted);border-bottom:1px solid rgba(255,255,255,.10);transition:box-shadow .22s ease}
header.site.is-scrolled{box-shadow:0 6px 20px rgba(9,48,46,.18)}
.nav{display:flex;align-items:center;justify-content:space-between;height:76px;max-width:var(--ft-container);margin:0 auto;padding:0 32px}
/* height-only sizing, width intentionally omitted so the browser derives it
   from the image's true aspect ratio (1441x238, ~6.06:1); a fixed width here
   or on the block's inline style squishes the wordmark. wp:site-logo renders
   a figure.wp-block-site-logo > a > img (no .nav-logo on the inner img, the
   className lands on the figure). */
.nav-logo,.nav-logo img{height:42px;width:auto;display:block}
.nav-logo{flex-shrink:0}
.nav-logo a{display:block;line-height:0}
.nav-right{display:flex;align-items:center;gap:26px;flex-shrink:0}
/* wp:navigation renders <nav class="wp-block-navigation nav-links"><ul
   class="wp-block-navigation__container">, one <li
   class="wp-block-navigation-item"> per link */
.nav-links{flex-shrink:0}
.nav-links ul{display:flex;align-items:center;gap:28px;list-style:none;margin:0;padding:0}
.nav-links li{margin:0}
.nav-links a{color:#EFEAE3;font-weight:500;font-size:15px;text-decoration:none;transition:color .2s;white-space:nowrap}
.nav-links a:hover{color:#fff}
.nav .btn{font-size:15px}
.nav .btn > .wp-block-button__link{padding:11px 20px} /* anchor owns padding (click-area fix) */
/* mobile nav overlay (wp:navigation's built-in responsive menu, opened via
   the hamburger toggle below 1080px — see the @media(max-width:1080px) block
   near the bottom) */
/* !important: WP core prints `.wp-block-navigation:not(.has-background)
   .wp-block-navigation__responsive-container.is-menu-open
   :not(.disable-default-overlay){background-color:#fff}` — 5 class-level
   selectors (0,5,0), our block never sets a background color at the block-
   attribute level so it always matches `:not(.has-background)`. Found via
   the mobile-launch QA sweep: the overlay was opening white with near-
   invisible cream text on every page (2026-07-09). */
.wp-block-navigation__responsive-container.is-menu-open{background:var(--ft-teal-deep) !important}
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item a{color:var(--ft-cream);font-size:20px}
/* tap-target fix (mobile QA sweep, 2026-07-09): the icon rendered at its
   native 24x24px with zero padding, well under the 44px touch-target
   minimum on the single control that opens the entire mobile nav. Negative
   margin keeps the header's logo/CTA layout math unchanged (net footprint
   is still the icon's 24px, just with a bigger, centered hit area). */
.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close{color:#EFEAE3;padding:10px;margin:-10px}

/* hero — deep-vignette teal field, feather medallion, cream type.
   Padding 120px/96px is production's exact measured value (headless Chrome,
   1440 viewport, 2026-07-06), not the earlier 116/128 estimate.
   Background = the workbench's "deep vignette" recipe (Brent, 2026-07-12):
   brighter center behind the headline, edges darkened toward teal-deep. */
.hero{position:relative;overflow:hidden;text-align:center;padding:120px 0 96px;
  background:
    radial-gradient(60% 50% at 50% 42%, rgba(220,240,236,.16) 0%, rgba(220,240,236,0) 55%),
    radial-gradient(100% 90% at 50% 42%, var(--ft-teal-light) 0%, var(--ft-teal) 42%, var(--ft-teal-deep) 100%),
    radial-gradient(130% 120% at 50% 50%, transparent 52%, rgba(9,48,46,.55) 100%);
}
/* Feather — locked Drift treatment (Brent, 2026-07-12): promoted from a static
   ::before to a real, animatable element so it can drift + take restrained
   scroll parallax (see ft-build.js). Outer .hero-feather is the parallax
   target (translateY only); .feather-drift-wrap carries the rotate/translate/
   scale drift so the two motions never fight. Glow was tried and rejected
   (--ft-glow:0, see hero-directions/RECIPE.md) — teal-deep drop-shadow does
   the lift instead. */
.hero-feather{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:min(720px,92%);aspect-ratio:1/1;z-index:0;pointer-events:none}
.feather-drift-wrap{position:relative;width:100%;height:100%;animation:featherDrift var(--ft-drift-dur) ease-in-out infinite;transform-origin:50% 50%}
@keyframes featherDrift{
  0%   { transform:rotate(0deg) translate(0,0) scale(1); }
  22%  { transform:rotate(calc(var(--ft-drift-amt) * 1.4deg)) translate(calc(var(--ft-drift-amt) * 4px), calc(var(--ft-drift-amt) * -6px)) scale(calc(1 + var(--ft-drift-amt) * .012)); }
  50%  { transform:rotate(calc(var(--ft-drift-amt) * -1deg)) translate(calc(var(--ft-drift-amt) * -5px), calc(var(--ft-drift-amt) * 3px)) scale(calc(1 - var(--ft-drift-amt) * .01)); }
  76%  { transform:rotate(calc(var(--ft-drift-amt) * .8deg)) translate(calc(var(--ft-drift-amt) * 3px), calc(var(--ft-drift-amt) * 5px)) scale(calc(1 + var(--ft-drift-amt) * .008)); }
  100% { transform:rotate(0deg) translate(0,0) scale(1); }
}
.feather-img{position:relative;display:block;width:100%;height:100%;object-fit:contain;opacity:var(--ft-opacity);filter:drop-shadow(0 calc(var(--ft-shadow) * 10px) calc(var(--ft-shadow) * 34px) rgba(9,48,46, calc(var(--ft-shadow) * 0.65)))}
@media (prefers-reduced-motion: reduce){
  /* static feather, same opacity/shadow — drift + JS parallax both stop */
  .feather-drift-wrap{animation:none;transform:none}
}
/* .hero-inner is the full section field (matches --ft-container, production
   measures 1240 here); the headline/lede keep their own narrower reading
   width (max-width/ch) so the centered hero composition is unchanged. */
.hero-inner.hero-inner{position:relative;z-index:2;padding:0 32px;max-width:var(--ft-container);margin:0 auto} /* double class: core .has-global-padding ties + wins on order otherwise (same trap as .wrap.wrap) */
/* h1 120px / letter-spacing -0.005em / line-height 1.02 is the "Storyteller"
   Newsreader tuning (Brent, 2026-07-12) — was -.025em/.96 under Fraunces,
   whose tighter optical proportions don't carry over. */
.hero h1{color:var(--ft-cream);font-size:clamp(56px,9vw,120px);line-height:1.02;letter-spacing:-0.005em;margin:18px auto 24px;max-width:16ch}
.hero .lede{color:rgba(251,247,242,.78);max-width:600px;margin:0 auto 36px;font-size:21px}
.hero-ctas{display:flex;gap:14px;flex-wrap:wrap;justify-content:center;align-items:center}

/* generic section — 96px top/bottom is production's exact value (--ft-space-9)
   across every section (services/why-us/process/testimonial/contact/about) */
.section{padding:var(--ft-space-9) 0}
.section-head.section-head{max-width:660px;margin:0 0 52px} /* double class vs core constrained-layout max-width tie */
.section-head h2{font-size:clamp(36px,5vw,58px);line-height:1.02;margin:0 0 18px}
/* !important: WP core prints `.is-layout-constrained > :where(...){margin-left:
   auto !important;margin-right:auto !important}` for any direct child of a
   constrained-layout group that isn't an align* block — no specificity fix
   can beat an !important rule, so this is a legitimate, narrowly-scoped
   exception. Left-aligns the intro under the eyebrow/h2 (not .center). */
.section-head .lede{max-width:60ch;margin-left:0 !important;margin-right:0 !important}
/* About's section-head is the only bare (non-.center) consumer of the 660px
   cap above — it isn't a standalone header band like services/process, it
   sits directly over .bios in the same .wrap and needs to share its content
   width + left edge. Bare .section-head is still a constrained-layout child
   of .wrap (constrained too), so core's forced is-layout-constrained
   auto-margin (!important, same rule as the .lede fix above) centered the
   660px box inside .wrap, offsetting it right of .bios's flush-left edge
   (bug reported 2026-07-15). Scoped to .about, not the shared rule, so
   any future bare .section-head elsewhere keeps the narrow default. */
.about .section-head.section-head{max-width:none;margin-left:0 !important;margin-right:0 !important}
/* centered section-head (services/process): H2 spans the full column, only
   the intro paragraph gets a readable measure + is centered under it.
   !important to win the tie against the left-aligned .section-head .lede
   rule above (both are !important now, so this one must also carry it to
   win via source order) */
.section-head.center{max-width:none;text-align:center}
.section-head.center .lede{max-width:640px;margin-left:auto !important;margin-right:auto !important}

/* services — white */
.services{background:var(--ft-paper)}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:28px}
/* card padding 32px all around + shadow-sm is production's exact measured
   value (headless Chrome, 2026-07-06) — replaces an earlier 68px/44px estimate. */
.card{background:var(--ft-paper);border:1px solid var(--ft-line);border-radius:14px;padding:32px;box-shadow:var(--ft-shadow-sm);transition:box-shadow .22s ease,transform .22s ease}
.card:hover{border-color:var(--ft-line-strong);box-shadow:var(--ft-shadow-md);transform:translateY(-2px)}
.card-icon{width:52px;height:52px;border-radius:12px;background:var(--ft-orange-50);display:flex;align-items:center;justify-content:center;margin:0 0 26px}
.card-icon svg,.card-icon i{width:26px;height:26px;color:var(--ft-orange-700)}
.card h3{font-family:var(--ft-font-display);font-weight:500;font-size:27px;line-height:1.15;letter-spacing:-.012em;margin:6px 0 14px}
.card p{color:var(--ft-ink-2);line-height:1.65;margin:0 0 30px}
/* plain hanging-indent list, no checkmarks/dividers (markup no longer has icons) */
.bullets{list-style:none;padding:0;margin:0 0 34px}
/* Pilot/prod treatment: flex row with a 17px orange check + hairline top border.
   Orange at this size is fine here — the checks are decorative accents, not text
   (see CLAUDE.md non-negotiable #1). */
.bullets li{display:flex;align-items:center;gap:10px;padding:8px 0;color:var(--ft-ink-2);border-top:1px solid var(--ft-line)}
.bullets li svg{width:17px;height:17px;color:var(--ft-orange);flex:none}

/* why us — cream, single column editorial */
.whyus{background:var(--ft-cream);border-top:1px solid var(--ft-line);border-bottom:1px solid var(--ft-line)}
/* Center the why-us as a ~900px column (matches production) instead of the old
   left-pinned narrow block. The section has no .inner wrapper — the content lives
   directly in .wrap — so constrain .wrap itself; it keeps .wrap's margin:0 auto so
   the column centers with balanced margins. Content stays left-aligned within it. */
.whyus .wrap{max-width:900px}
/* !important: WP core prints `.is-layout-constrained > :where(...){margin-left:
   auto !important;margin-right:auto !important}` for any direct child of a
   constrained-layout group that isn't an align* block (here: h2 and .body,
   both direct children of .wrap.inner) — no specificity fix can beat an
   !important rule. Shares one left edge with the eyebrow (was hanging the
   eyebrow further left than the auto-centered heading/body). */
.whyus .eyebrow,.whyus h2,.whyus .body,.whyus .body p,.whyus .link-arrow{margin-left:0 !important;margin-right:0 !important}
.whyus h2{font-size:clamp(38px,5.6vw,72px);line-height:1.0;margin:0 0 30px;max-width:none}
.whyus .body{max-width:none}
.whyus .body p{color:var(--ft-ink-2);font-size:18px;line-height:1.7;margin:0 0 22px}

/* process — charcoal */
.process{background:var(--ft-charcoal);color:var(--ft-fg-on-dark)}
.process .section-head h2,.process .disp{color:var(--ft-fg-on-dark)}
.process .lede{color:#CFC9C1}
.process .section-head{margin-bottom:136px}
.process .section-head h2{font-size:64px;line-height:1.05}
/* bare columns, no card border/bg/radius, no divider rules — production
   uses whitespace only between the columns (batch 4 reconciliation).
   Vertical padding matches production's measured 32px top/bottom. */
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:0}
.step{border:0;border-radius:0;background:transparent;padding:32px}
.step .num{font-family:var(--ft-font-display);font-weight:700;font-size:96px;color:var(--ft-orange);line-height:1;margin:0 0 18px}
.step h3{font-family:var(--ft-font-display);font-weight:500;font-size:22px;margin:0 0 10px;color:var(--ft-fg-on-dark)}
.step p{color:#B7B2AB;font-size:15px;line-height:1.6;margin:0}

/* testimonial — solid teal band, cream type, single-slide crossfade carousel
   (custom vanilla-JS carousel, ft-build.js initTestimonialCarousel; replaced
   the non-functional GB Pro carousel block 2026-07-12, see STATUS.md) */
.quote{background:var(--ft-teal);text-align:center}
.quote .wrap{max-width:900px}
/* Gutenberg core's .wp-block-quote ships a default left border + left
   padding (the banned accent-stripe pattern); reset it explicitly */
/* italic display serif, sized generously — the quotes are short, let them
   breathe (Brent, 2026-07-12 carousel spec) */
.quote blockquote{border:0;padding:0;margin:0 auto 28px;max-width:26ch;font-family:var(--ft-font-display);font-weight:400;font-style:italic;font-size:clamp(30px,4.6vw,52px);line-height:1.26;letter-spacing:-.018em;color:var(--ft-cream);text-wrap:pretty}
/* centered cream circular monogram badge above the eyebrow (SVG already
   bakes in the cream circle + hairline stroke, see brand-graphics) */
.quote .mark{margin:0 auto 20px;display:block;width:60px;height:60px}
.quote .eyebrow{color:var(--ft-cream);display:block;margin:0 0 24px} /* cream on teal — orange fails AA here (see eyebrow note above) */
.quote cite{font-style:normal;color:rgba(251,247,242,.72);font-weight:500}
.quote cite b{color:var(--ft-cream);font-weight:600;display:block;font-size:17px;margin-bottom:2px}

/* carousel chrome — all slides share one grid cell so the section reserves
   height for the tallest quote with no JS measurement + no reflow on swap */
.t-carousel{position:relative}
.t-viewport{display:grid;touch-action:pan-y}
.t-slide{grid-area:1/1;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .6s ease,visibility 0s linear .6s;z-index:0}
/* no-JS fallback: first quote stays visible until .has-js lands (inline in
   <head>, pre-paint — see functions.php). MUST be :not(.has-js)-scoped: an
   unscoped .t-slide:first-child has the same specificity as .t-slide.is-active
   and, coming later in source order, permanently pinned slide 1 visible on
   top of whichever slide JS activated — two opaque slides at once in any
   real browser, not a screenshot artifact (caught live 2026-07-14) */
html:not(.has-js) .t-slide:first-child{opacity:1;visibility:visible;pointer-events:auto;z-index:1}
.t-slide.is-active{opacity:1;visibility:visible;transition:opacity .6s ease;pointer-events:auto;z-index:1}
.t-slide blockquote{margin-bottom:24px}
.t-controls{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:28px}
.t-arrow{display:flex;align-items:center;justify-content:center;width:44px;height:44px;flex:0 0 44px;border-radius:50%;border:1px solid rgba(251,247,242,.28);background:transparent;color:var(--ft-cream);cursor:pointer;transition:background-color .2s ease,border-color .2s ease}
.t-arrow:hover{background:rgba(251,247,242,.12);border-color:rgba(251,247,242,.5)}
.t-arrow:focus-visible{outline:2px solid var(--ft-cream);outline-offset:2px}
.t-arrow svg{width:18px;height:18px}
.t-dots{display:flex;align-items:center;gap:2px}
.t-dot{width:44px;height:44px;display:flex;align-items:center;justify-content:center;border:0;background:transparent;cursor:pointer;padding:0}
.t-dot::before{content:"";width:8px;height:8px;border-radius:50%;background:rgba(251,247,242,.46);transition:background-color .2s ease,transform .2s ease}
.t-dot:hover::before{background:rgba(251,247,242,.55)}
.t-dot:focus-visible{outline:2px solid var(--ft-cream);outline-offset:2px}
.t-dot[aria-current="true"]::before{background:var(--ft-orange);transform:scale(1.25)}

@media (prefers-reduced-motion: reduce){
  .t-slide,.t-slide.is-active{transition:none}
}

/* contact — cream */
.contact{background:var(--ft-cream);border-top:1px solid var(--ft-line)}
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:start}
/* checkmarks restored, ink-colored (production's are ink, not orange) */
.trust{list-style:none;padding:0;margin:26px 0 0}
.trust li{display:flex;align-items:center;gap:10px;padding:8px 0;color:var(--ft-ink-2);font-weight:500}
.trust li svg,.trust li i{width:17px;height:17px;color:var(--ft-ink);flex:none}
/* 501(c)(3) note: small plain gray text, no peach tint box */
.discount{margin-top:22px;font-size:13.5px;color:var(--ft-ink-3);line-height:1.55}
.form{background:var(--ft-paper);border:1px solid var(--ft-line);border-radius:16px;padding:40px;box-shadow:var(--ft-shadow-md)}
/* (native-form .field/.field-row rules removed 2026-07-10 — the contact form is
   the Forminator shortcode, styled by the Forminator block below) */
/* contact lede line-length (was an inline style inside an invalid nested <p>) */
.contact .lede{max-width:44ch}
.form .btn{width:100%;justify-content:center;margin-top:6px}

/* ---- Forminator "Start a project" form, restyled to the brand ---- */
/* The .form div is the single card; strip Forminator's own container chrome. */
.contact .forminator-ui.forminator-custom-form{background:none;border:0;box-shadow:none;padding:0;margin:0;font-family:var(--ft-font-body)}
.contact .forminator-row{margin:0 0 22px}
.contact .forminator-row.forminator-row-last{margin-bottom:0}
.contact .forminator-col{padding:0}
.contact .forminator-custom-form .forminator-label{display:block;font-family:var(--ft-font-body);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.18em;line-height:1.3;margin:0 0 8px;color:var(--ft-ink-3)}
.contact .forminator-required{display:none}
.contact .forminator-custom-form .forminator-input,
.contact .forminator-custom-form .forminator-textarea{width:100%;border:1px solid var(--ft-line-strong);border-radius:10px;padding:12px 14px;font-family:var(--ft-font-body);font-size:15px;color:var(--ft-ink);background:#fff;box-shadow:none}
.contact .forminator-custom-form .forminator-textarea{min-height:110px;resize:vertical}
.contact .forminator-custom-form .forminator-input:focus,
.contact .forminator-custom-form .forminator-textarea:focus{outline:none;border-color:var(--ft-orange);box-shadow:0 0 0 3px rgba(240,96,32,.12)}
.contact .forminator-row-last,.contact .forminator-row-last .forminator-col{width:100%}
/* .forminator-button-submit.forminator-button-submit: specificity workaround —
   Forminator's own `.forminator-ui.forminator-custom-form[data-design="default"]
   .forminator-button{border-radius:2px}` is 4 class-level selectors (0,4,0), same
   as this rule's 3 (0,3,0) would lose to it. Doubling the class matches that
   count on class-count without !important; ft-build.css also loads after every
   forminator-*.css enqueue, so the tie resolves in our favor by source order. */
.contact .forminator-custom-form .forminator-button-submit.forminator-button-submit{width:100% !important;display:flex;align-items:center;justify-content:center;gap:8px;background:var(--ft-orange-700);color:#fff;border:0;border-radius:var(--ft-radius-btn);padding:14px 24px;margin-top:6px;font-family:var(--ft-font-body);font-weight:600;font-size:15px;line-height:1;cursor:pointer;transition:background .18s ease}
.contact .forminator-custom-form .forminator-button-submit:hover{background:var(--ft-orange-600)} /* orange-600 keeps white text AA-ish on hover, matches .btn-primary */

/* about — white. Full-width alternating photo+bio rows (Brent: photo left /
   bio right; Kate: bio left / photo right via row-reverse), plain white,
   no card bg/border/shadow. Replaces the old side-by-side cream-card +
   72px circular avatar pattern. */
.about{background:var(--ft-paper);border-top:1px solid var(--ft-line)}
.bios{display:flex;flex-direction:column;gap:64px;margin-top:52px}
.bio-row{display:flex;align-items:flex-start;gap:48px}
.bio-row--reverse{flex-direction:row-reverse}
.bio-photo{width:320px;height:320px;aspect-ratio:1/1;object-fit:cover;border-radius:var(--ft-radius-lg);flex:none}
.bio-text{flex:1}
.bio-name{font-family:var(--ft-font-display);font-weight:500;font-size:32px;margin:0 0 8px;color:var(--ft-ink)}
.bio-role{font-family:var(--ft-font-body);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.18em;color:var(--ft-ink-3);margin:0 0 16px}
.bio-copy{color:var(--ft-ink-2);line-height:1.65;font-size:17px;margin:0}

/* hybrid advantage — cream rounded band, left-aligned column; founded note
   + CTA live outside/below the band */
.hybrid{background:var(--ft-paper)}
/* .band.band: specificity workaround — WP's global-styles-inline-css prints
   `.has-global-padding{padding-left/right:var(--wp--style--root--padding-*)}`
   (an unset custom property, resolving to 0) after this stylesheet at the
   same 0,1,0 specificity, which wins the horizontal padding by source order */
.band.band{background:var(--ft-cream);border-radius:var(--ft-radius-xl);padding:72px}
.hybrid .inner{max-width:680px;margin-top:0;margin-bottom:0;margin-left:0 !important;margin-right:0 !important;text-align:left} /* !important vs core's centering !important (same as .whyus fix) */
.hybrid p.big{font-size:19px;line-height:1.7;color:var(--ft-ink-2);margin:0 0 22px}
.hybrid p.big:last-child{margin-bottom:0}
.hybrid .founded{font-size:14px;color:var(--ft-ink-3);margin:32px 0 18px;text-align:left} /* was --ft-ink-4 (3.13:1, axe color-contrast fail); --ft-ink-3 passes AA at 6.24:1 on cream */
/* CTA is a bold text-link outside the band, not a filled button */
.hybrid-cta{font-size:16px}

/* footer — charcoal (GP Elements footer) */
footer.site{background:var(--ft-charcoal);color:var(--ft-fg-on-dark);padding:72px 0 40px}
/* .foot-grid.foot-grid: specificity workaround (same trap as .wrap.wrap, see
   wp-block-theme-css-specificity memory) — core's .wp-block-columns tied this
   selector at (0,1,0) and won on source order, so the grid NEVER applied: the
   footer silently fell back to core's plain flex/wrap columns (equal-width
   columns + 24px gap on desktop, a single flex-stacked column on mobile)
   instead of the intended 1.6fr-weighted grid. Found live on prod 2026-07-14. */
.foot-grid.foot-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px;padding-bottom:44px;border-bottom:1px solid #34343a}
.foot-grid img,.foot-logo img{height:36px;width:auto;margin:0 0 18px}
.foot-brand p{color:#B7B2AB;font-size:14.5px;line-height:1.6;max-width:34ch;margin:0}
/* footer column titles are h3 (heading-order fix); restyled to the same small-caps
   label look as before, not the default h3 display-type size */
.foot-col h3{font-family:var(--ft-font-body);font-size:12px;text-transform:uppercase;letter-spacing:.16em;color:var(--ft-ink-4);margin:0 0 16px;font-weight:600}
.foot-col a{display:block;color:#D8D3CC;text-decoration:none;font-size:15px;padding:5px 0;transition:color .2s}
.foot-col a:hover{color:var(--ft-orange)}
/* .foot-col p{margin:0}: the <p> wrapping each footer link had no explicit
   margin rule (only the .foot-col a padding was meant to space rows), so it
   fell back to the global p{margin:0 0 16px} (0,0,1) — weaker than core's
   .is-layout-flow block-gap rule, which was adding a 24px top margin on every
   link after the first. Result: ~24px between rows instead of the intended
   10px (.foot-col a's own 5px+5px padding). Matches .foot-brand p's existing
   margin:0, which already won at (0,1,1). */
.foot-col p{margin:0}
/* margin-top:0: same block-gap bleed as .foot-col p above — the copyright line
   is a plain <p>, so core added a 24px top margin on top of its own intended
   26px padding-top, doubling the gap above the copyright line. */
.copyright{padding-top:26px;margin-top:0;color:#8C8C92;font-size:13.5px}

/* scroll-reveal — IntersectionObserver-driven entrance (see ft-build.js).
   Opt-in gating (only hidden once JS confirms it can reveal, via .has-js on
   <html>) rather than opt-out, so content stays visible if JS fails to load —
   matches production's exact 640ms duration + translateY(24px) distance. */
.ft-reveal{opacity:1;transform:none}
.has-js .ft-reveal{opacity:0;transform:translateY(24px);transition:opacity 640ms var(--ft-ease,cubic-bezier(.16,1,.3,1)),transform 640ms var(--ft-ease,cubic-bezier(.16,1,.3,1))}
.has-js .ft-reveal.is-visible{opacity:1;transform:translateY(0)}
.has-js .ft-reveal[data-delay="1"]{transition-delay:80ms}
.has-js .ft-reveal[data-delay="2"]{transition-delay:160ms}
.has-js .ft-reveal[data-delay="3"]{transition-delay:250ms}
.has-js .ft-reveal[data-delay="4"]{transition-delay:340ms}
/* failsafe: if ft-build.js ever fails to load (CDN/cache hiccup), .ft-reveal
   content must not stay invisible — reveal everything at 2.5s regardless. The
   JS path adds .is-visible long before this fires, so normal behavior is
   unchanged (the animation's end state equals the revealed state). */
@keyframes ft-reveal-failsafe{to{opacity:1;transform:none}}
.has-js .ft-reveal{animation:ft-reveal-failsafe .6s ease 2.5s forwards}
@media (prefers-reduced-motion: reduce){
  .has-js .ft-reveal{opacity:1;transform:none;transition:none;animation:none}
}

@media(max-width:1080px){
  /* nav: collapse to the hamburger below 1080px (was 960, raised 2026-07-10
     when the "Free resources" item was added — logo + 6 links + CTA overflowed
     by 30px at 1024; measured fit threshold ≈1070). Original finding: the full
     desktop nav didn't fit between ~681-950px either (QA, 2026-07-09).
     !important: WP core's Navigation block only auto-hides its own toggle
     button above a hardcoded 600px breakpoint —
     `.wp-block-navigation__responsive-container-open:not(.always-shown)
     {display:none}` inside `@media(min-width:600px)` in wp-includes/blocks/
     navigation/style.css, specificity (0,2,0) — so it has to be forced back
     open through 1080px; no specificity fix beats a :not()-qualified core
     rule cleanly here. */
  .nav-links .wp-block-navigation__container{display:none}
  .wp-block-navigation__responsive-container-open{display:flex !important}

  /* MOBILE-HEADER FIX (2026-07-12): the hamburger toggle floated mid-bar
     instead of pinning to the far right. Root cause: .nav-right holds TWO
     flex children (the nav-links toggle, then the CTA .wp-block-buttons);
     .nav's justify-content:space-between only pins the *whole* .nav-right
     box to the right edge, and within that box the toggle (first child)
     landed wherever the box's own left edge fell — nowhere near the true
     right edge. The CTA (last child) claimed the real right edge instead.
     Fix: the CTA moves into the nav drawer (ft-build.js relocates the DOM
     node; see initNavCtaDrawer), so .nav-right ends up holding ONLY the
     toggle, which .nav's existing space-between then pins flush right —
     no toggle/order hacks needed. justify-content:flex-end is a defensive
     add so any future nav-right sibling still hugs the right edge.
     NOTE: .has-js is set by an INLINE <head> snippet (functions.php),
     independent of this external ft-build.js file loading — so gating on
     .has-js alone would NOT protect against ft-build.js itself failing to
     load (unlike .ft-reveal, hiding here has no reveal path without JS
     actually relocating the node). The animation failsafe below is the
     real safety net: if initNavCtaDrawer never runs, the CTA reappears in
     the bar after a generous buffer instead of staying stranded hidden. */
  .nav-right{justify-content:flex-end}
  .has-js .nav-right > .wp-block-buttons{display:none}
  /* CSS Animations win over a normal (non-!important) author declaration
     regardless of specificity/source order, so this reliably restores
     display once the failsafe fires — no-op if ft-build.js already moved
     the node elsewhere (this selector requires it to still be a direct
     child of .nav-right). */
  @keyframes nav-cta-bar-failsafe{to{display:flex}}
  .has-js .nav-right > .wp-block-buttons{animation:nav-cta-bar-failsafe 1ms linear 3s forwards}
}
@media(max-width:900px){
  /* production's tablet/large-mobile section padding drops to 64px
     (--ft-space-8) at this breakpoint; hero to 80px/64px */
  .hero{padding:80px 0 64px}
  .section{padding:64px 0}
  .grid-4{grid-template-columns:1fr 1fr;row-gap:40px}
  .step{padding:0 24px}
  .contact-grid{grid-template-columns:1fr;gap:36px}
  .foot-grid.foot-grid{grid-template-columns:1fr 1fr;gap:32px}
  .bio-photo{width:260px;height:260px}
  .band.band{padding:48px 40px}
}
@media(max-width:680px){
  /* nav-links stays hidden here too, via the @media(max-width:1080px) block
     above (1080 fully covers this range) — see that block's comment. */
  .nav{padding:0 16px;gap:8px}
  /* logo height 22px is production's exact measured mobile value (134px
     wide at that height) — 30px was too wide and pushed the hamburger +
     button off the right edge of a 375px viewport, causing horizontal
     page overflow (verified via headless Chrome, 2026-07-06).
     !important: the image block sets height as an inline style (higher
     specificity than any class selector). */
  .nav-logo img{height:22px !important;width:auto}
  .nav .btn.btn.btn-primary{font-size:12px}
  .nav .btn.btn-primary > .wp-block-button__link{padding:8px 10px;gap:4px} /* anchor owns padding (click-area fix) */
  .nav .btn.btn.btn-primary svg,.nav .btn.btn.btn-primary i{width:13px;height:13px}
  .grid-2{grid-template-columns:1fr}
  .grid-4{grid-template-columns:1fr;row-gap:36px}
  .step{padding:0}
  .step .num{font-size:64px}
  /* stack photo above bio on mobile, both bio rows (incl. the reversed one) */
  .bio-row,.bio-row--reverse{flex-direction:column;gap:24px}
  .bio-photo{width:100%;height:auto}
  .band.band{padding:36px 24px}
  .wrap.wrap{padding:0 22px}
  /* production's small-phone hero padding is 64px/52px (--ft-space-8-ish);
     hero-inner itself no longer carries vertical padding (moved to .hero
     above), so only the horizontal gutter is set here. */
  .hero{padding:64px 0 52px}
  .hero-inner.hero-inner{padding:0 22px}
  .section{padding:64px 0}
}

/* ============ production-match fixes (2026-07-06) ============ */
/* 1. Header flush against the hero — kill WP's default block-gap between the
   top-level template blocks (header / main / footer). */
.wp-site-blocks > * { margin-block-start: 0 !important; margin-block-end: 0 !important; }
main.wp-block-post-content { margin-top: 0; }

/* 1b. Sections flush against each other. The page's <section> blocks are
   children of <main class="wp-block-post-content">, NOT of .wp-site-blocks, so
   the rule above never reaches them. WP's flow/constrained layout puts a
   block-gap top-margin on every child after the first, and that margin shows
   the white body background as a band between full-bleed colored sections
   (glaring between the charcoal Process and teal testimonial sections).
   Sections own their vertical rhythm via padding, so they must sit flush. */
/* scope the margin-kill to SECTION-SHAPED children only (wp:group sections and
   wp:html style blocks are margin-irrelevant): a plain editor-authored page's
   paragraphs/headings/lists keep their natural flow spacing instead of
   collapsing into a wall of text. All 4 built pages' top-level children are
   .wp-block-group sections, so their rendering is unchanged. */
.wp-block-post-content > section, .wp-block-post-content > .wp-block-group { margin-block-start: 0 !important; margin-block-end: 0 !important; }

/* "Content page" template (templates/page-content.html) — plain editor-authored
   prose pages: centered 760px column, real gutters, styled title. Pick it in
   the editor under Page > Template > "Content page". */
.content-page.content-page{padding:72px 24px 96px}
.content-page .wp-block-post-title{font-family:var(--ft-font-display);font-weight:500;letter-spacing:-.005em;color:var(--ft-ink);font-size:clamp(34px,4.6vw,54px);line-height:1.05;margin:0 0 28px}
.content-page .wp-block-post-content > *{max-width:100%} /* long tables/code scroll inside, never overflow */

/* 2. Nav links stay cream on the teal header — the native Navigation block was
   rendering them in the dark body text color, beating the .nav-links a rule. */
header.site .wp-block-navigation a,
header.site .wp-block-navigation .wp-block-navigation-item__content { color: #EFEAE3 !important; }
header.site .wp-block-navigation a:hover,
header.site .wp-block-navigation .wp-block-navigation-item__content:hover { color: #fff !important; }

/* 3. Hero secondary CTA = plain cream text like production (not a bordered ghost). */
.btn-ghost-hero, .btn-ghost-hero:hover { border-color: transparent !important; background: transparent !important; }

/* 4. Hero CTAs carry no arrow glyphs in production (header CTA keeps its arrow). */
.hero-ctas .wp-block-button__link svg { display: none !important; }

/* 5. Nav drawer CTA (2026-07-12) — the "Start a project" button relocated from
   the mobile bar into the nav drawer (see the .nav-right fix above + ft-build.js's
   initNavCtaDrawer). Un-scoped by viewport on purpose: the
   .wp-block-navigation__responsive-container-content ancestor only exists inside
   the offcanvas menu, which core CSS only ever lets a visitor open at <=1080px
   (the toggle that opens it is hidden above that width), so the context selector
   is already an effective width guard. Placed at the end of the file so its
   specificity ties against the .nav .btn breakpoint rules above resolve in its
   favor by source order (same tie-break convention as sections 1-4 above). */
.wp-block-navigation__responsive-container-content .wp-block-buttons{width:100%;margin:32px 0 0}
.wp-block-navigation__responsive-container-content .wp-block-buttons .btn.btn-primary{width:100%}
.wp-block-navigation__responsive-container-content .wp-block-buttons .btn.btn-primary > .wp-block-button__link{width:100%;justify-content:center;padding:16px 26px;font-size:17px;gap:10px}
.wp-block-navigation__responsive-container-content .wp-block-buttons .btn.btn-primary svg{width:18px;height:18px}
