/*
 * Bebas Neue for all display type: headings, buttons, menu (QA round,
 * Oct 2026). Replaces the Adobe-hosted Veneer: every Veneer cut was
 * distressed and the client wanted clean lettering. OFL, self-hosted. One
 * weight, declared across the whole range so the browser never fakes a bold.
 */
@font-face {
  font-family: "Bebas Neue";
  src: url("../fonts/bebas-neue-400.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   Goodwell Painting — stylesheet transcribed from the approved final designs
   in /mockups/*.dc.html.

   Values here are lifted from those files, not re-invented. Where the design
   used an inline style, it becomes a class with the same values.
   ========================================================================== */

:root {
  /* Colour — from the mockups' --gw-* fallbacks (dark theme is final) */
  --gw-bg: #0d0d0d;
  --gw-bg2: #131313;
  --gw-areas-bg: var(--gw-bg2);
  --gw-reviews-bg: #1a1a1a;
  --gw-card: #141414;
  --gw-card-alt: #111111;
  --gw-heading: #ffffff;
  --gw-text: #f5f2ec;
  --gw-mid: rgba(245, 242, 236, 0.84);
  --gw-dim: rgba(245, 242, 236, 0.8);
  --gw-faint: rgba(245, 242, 236, 0.3);
  --gw-line: rgba(255, 255, 255, 0.12);
  --gw-line-soft: rgba(255, 255, 255, 0.09);
  --gw-line-input: rgba(255, 255, 255, 0.18);
  --gw-faint-line: rgba(245, 242, 236, 0.35);
  --gw-accent: #f0a31a;
  --gw-accent-hover: #ffc04d;
  --gw-accent-deep: #d98e1c;
  --gw-accent-tint: rgba(240, 163, 26, 0.06);
  --gw-accent-line: rgba(240, 163, 26, 0.18);
  --gw-accent-line-strong: rgba(240, 163, 26, 0.25);
  --gw-badge-bg: rgba(12, 12, 12, 0.82);
  --gw-badge-line: rgba(255, 255, 255, 0.09);
  --gw-header-bg: rgba(13, 13, 13, 0.96);
  --gw-ink: #131007;

  /* Type — Bebas Neue (display, labels, buttons), Barlow (body). */
  --gw-display: "Bebas Neue", "Arial Narrow", Impact, sans-serif;
  --gw-label: "Bebas Neue", "Arial Narrow", sans-serif;
  --gw-body: Barlow, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Sizes as written in the designs */
  --fs-h1: clamp(50px, 6.2vw, 106px);
  --fs-h2: clamp(28px, 4.5vw, 45px);
  --fs-h3: clamp(22px, 2.2vw, 28px);
  --fs-body: 17px;
  --fs-meta: 16px;
  --fs-small: 13px;
  --fs-btn: 21px;

  --gw-max: 1440px;
  --gw-gutter: 24px;
  --gw-section-y: clamp(56px, 8vw, 104px);

  /* Motion. A long duration over a short distance is what reads as weight
     rather than as a UI toy — hence 620ms for only 18px. The easing
     decelerates without overshoot: no bounce, no elastic, nothing playful.
     No blur anywhere; it costs a GPU pass every frame and shows badly on the
     mid-range Android hardware most of this site's traffic arrives on. */
  --gw-reveal-dur: 620ms;
  --gw-reveal-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --gw-reveal-dist: 18px;
  --gw-reveal-step: 70ms;  /* stagger between siblings */
  --gw-micro-dur: 220ms;   /* hover / state changes */
  --gw-micro-ease: cubic-bezier(0.4, 0, 0.2, 1);

  /* Aliases for the design-system names used in some templates. The palette
     above was written with short names (--gw-mid) while several templates use
     the plan's longer ones (--gw-text-mid); referencing an undefined custom
     property silently drops the declaration, so inline styles using these were
     rendering with no colour at all. Defined once here rather than rewritten
     across the templates, so both spellings stay valid. */
  --gw-text-mid: var(--gw-mid);
  --gw-text-dim: var(--gw-dim);
  --gw-line-strong: rgba(255, 255, 255, 0.35);
  --gw-surface: var(--gw-bg2);
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  padding: 0;
  background: var(--gw-bg);
  color: var(--gw-text);
  font-family: var(--gw-body);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--gw-display);
  font-weight: 400;
  text-transform: uppercase;
  color: var(--gw-heading);
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); line-height: 1.02; letter-spacing: 0.01em; }
h2 { font-size: var(--fs-h2); line-height: 1.02; }
h3 { font-size: var(--fs-h3); line-height: 1.06; }

p { margin: 0 0 16px; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: var(--gw-accent); text-decoration: none; }
a:hover { color: var(--gw-accent-hover); }
/* Body-copy links keep an underline so colour is never the only signal. */
main p a, .gw-deflist a { text-decoration: underline; text-underline-offset: 0.18em; }

:focus-visible { outline: 3px solid var(--gw-accent); outline-offset: 3px; border-radius: 2px; }

.gw-skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--gw-accent); color: var(--gw-ink);
  padding: 12px 20px; font-family: var(--gw-label);
  font-size: 18px; text-transform: uppercase;
}
.gw-skip-link:focus { left: 16px; top: 16px; color: var(--gw-ink); }

.gw-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important; scroll-behavior: auto !important;
  }
}

/* ---------- Layout ---------- */
.gw-wrap { max-width: var(--gw-max); margin: 0 auto; padding-inline: var(--gw-gutter); }
.gw-section { padding-block: var(--gw-section-y); }
/* The alternating shade. This modifier is used in 8 places across the service,
   location, hub and project templates but was never actually defined, so every
   "surface" section rendered the same flat black as its neighbour and the
   alternation the designs rely on was invisible. */
.gw-section--surface { background: var(--gw-bg2); --gw-section-bg: var(--gw-bg2); }
/* Hairline between two same-shade sections so they do not run together. */
.gw-section--bordered { border-top: 1px solid var(--gw-accent-line); }

/* Generic card grid. Used by the location page's Recent Work and Why Local
   Matters blocks and by the hub's card rows — all four were referenced in
   markup but never defined, so the items stacked as plain blocks. */
/* Fixed track counts, not auto-fit: auto-fit created four tracks at 1400px and
   stretched two project cards to 664px each with two empty columns beside them.
   The modifier names the column count, so --3 means three. */
.gw-grid { display: grid; gap: var(--gw-gutter); }
.gw-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.gw-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) {
  .gw-grid--3, .gw-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .gw-grid--3, .gw-grid--4 { grid-template-columns: 1fr; }
}

.gw-card {
  display: flex; flex-direction: column;
  background: var(--gw-card); border: 1px solid var(--gw-line);
  border-radius: var(--r-md, 8px); padding: var(--gw-gutter);
}
.gw-card > a { text-decoration: none; color: inherit; }
/* Bare links in a grid (the location page's Recent Work items) — no card chrome,
   just a rounded image with the title beneath. */
.gw-grid > a { display: block; text-decoration: none; color: inherit; }
.gw-grid > a .gw-frame { border-radius: var(--r-md, 8px); }
.gw-grid > a img { transition: transform 0.4s ease; }
.gw-grid > a:hover img { transform: scale(1.04); }
.gw-grid > a:hover h3 { color: var(--gw-accent); }

/* Torn brush edge between sections — a real design element from the mockups.
 *
 * The mask cuts the torn shape out of the brush's own colour, so whatever the
 * gaps expose has to match the section ABOVE it. That colour cannot be read
 * from a sibling in CSS, so each section publishes its own background as
 * --gw-section-bg and the brush reads it through inheritance. Sections that set
 * a background must set the variable alongside it — see .gw-section--surface
 * and friends below. Default is the page black, which is correct for any
 * section that does not paint its own background.
 */
:root { --gw-section-bg: var(--gw-bg); }

.gw-brush {
  height: 28px;
  margin-top: -27px;
  position: relative;
  z-index: 2;
  -webkit-mask-image: var(--brush);
  mask-image: var(--brush);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
/* The torn gaps expose whatever sits behind the brush — normally the page
   background. When the section above is a different shade (.gw-areas is
   --gw-bg2) that shows as a hard seam across the tear.
 *
 * The brush is masked, and a mask clips descendants too, so a pseudo-element
 * backdrop is not an option — it flattens the torn edge into a straight line.
 * The wrapper below is unmasked and paints the colour instead. Sections that
 * paint their own background set --gw-section-bg so the wrapper matches. */
.gw-brushwrap { background: var(--gw-section-bg); position: relative; }

.gw-brush--reviews { background: var(--gw-reviews-bg); }
.gw-brush--bg { background: var(--gw-bg); }
.gw-brush--bg2 { background: var(--gw-bg2); }
.gw-brush--cta { background: var(--gw-accent-deep); margin-top: 0; margin-bottom: -4px; z-index: 1; }
/* Bottom edge of the gold band. NOT a flipped gold brush — flipping put the
   mask's anti-aliased edge row against the band and it rendered as a hairline.
   Instead this is the pattern the homepage always used: a brush in the NEXT
   section's colour (painted by the brush-matcher JS), pulled up by the base
   -27px margin so its ragged top tears the gold. Band-to-brush is then
   gold-on-gold and cannot seam. The wrapper stays transparent — a painted
   wrapper here would sit over the band as a solid strip. */
.gw-brush--ctabottom {
  background: var(--gw-bg);
  z-index: 1;
}
.gw-brushwrap--ctabottom { background: transparent; }
/* Torn edge below the inner-page heroes. This one works the opposite way to the
   CTA brush: it paints the NEXT section's colour and tears it upward over the
   hero photo, so the gaps reveal the image rather than a flat colour. The
   wrapper stays transparent (the JS skips it) or the tear would be invisible.
   Sits above the hero via the negative margin and a raised z-index. */
.gw-brush--hero {
  background: var(--gw-section-bg, var(--gw-bg2));
  margin-top: -28px;
  z-index: 3;
}
.gw-brushwrap--hero { background: transparent; position: relative; z-index: 3; }

/* ---------- Image frames ---------- */
/* display:block is load-bearing, not cosmetic: .gw-frame is a <span> in several
   templates, and aspect-ratio / height:100% are both ignored on an inline box —
   the image then falls back to its own ratio and frames drift out of step
   (a 3:2 photo rendered 300x204 next to a 4:3 one at 300x225). */
.gw-frame { position: relative; overflow: hidden; display: block; }
.gw-frame > img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
.gw-frame--4x3 { aspect-ratio: 4 / 3; }
.gw-frame--4x5 { aspect-ratio: 4 / 5; }
.gw-frame--16x10 { aspect-ratio: 16 / 10; border-radius: 12px; }
.gw-frame--thumb { width: 112px; height: 76px; flex-shrink: 0; border-radius: 8px; }
.gw-img-placeholder {
  display: block; width: 100%; height: 100%;
  background: linear-gradient(135deg, var(--gw-card), var(--gw-bg));
  border: 1px dashed var(--gw-line);
}

/* ---------- Buttons ---------- */
.gw-btn, .gw-btn-outline, .gw-btn-ink {
  display: inline-flex; align-items: center; gap: 16px;
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-btn);
  letter-spacing: 0.03em; text-transform: uppercase; text-decoration: none;
  padding: 11px 24px 7px; border-radius: 8px; border: 1.5px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: background-color 0.18s ease, color 0.18s ease;
}
.gw-btn { background: var(--gw-accent); color: var(--gw-ink); }
.gw-btn:hover, .gw-btn:focus-visible { background: var(--gw-accent-hover); color: var(--gw-ink); }
.gw-btn-outline { background: transparent; color: var(--gw-accent); border-color: var(--gw-accent); gap: 8px; padding: 9.5px 20px 5.5px; }
.gw-btn-outline:hover { background: rgba(240, 163, 26, 0.12); color: var(--gw-accent); }
/* Trimmed from 12/8 to 9/6 vertical. It was the tallest of the three button
   styles (20px of vertical padding against .gw-btn's 18 and .gw-btn-outline's
   15) which made the CTA band's button pair visibly mismatched. The asymmetry
   is kept — the display face sits high in its em box, so equal padding looks
   bottom-heavy — just reduced in proportion. */
.gw-btn-ink { background: var(--gw-ink); color: var(--gw-accent-deep); font-size: 23px; padding: 9px 26px 6px; }
.gw-btn-ink:hover { background: #000; color: var(--gw-accent-deep); }

.gw-calllink {
  display: inline-block; font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-body); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--gw-heading); text-decoration: none;
  border-bottom: 3px solid var(--gw-accent); padding-bottom: 4px;
}
.gw-calllink:hover { color: var(--gw-accent); }

.gw-inlinelink {
  display: inline-flex; align-items: center; gap: 6px; width: fit-content;
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; text-decoration: none;
  border-bottom: 2px solid var(--gw-accent); padding-bottom: 3px;
}
.gw-inlinelink svg { flex-shrink: 0; }

/* ---------- Section heading: 8px gold bar + eyebrow + h2 ---------- */
.gw-heading { display: flex; gap: var(--gw-gutter); align-items: stretch; }
.gw-heading__bar { width: 8px; flex-shrink: 0; background: var(--gw-accent); }
.gw-eyebrow {
  display: block; font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-small); letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--gw-accent); margin-bottom: 8px;
}

/* ---------- Header ---------- */
.gw-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--gw-header-bg); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--gw-accent-line-strong);
}
.gw-header__inner {
  max-width: var(--gw-max); margin: 0 auto;
  display: flex; align-items: center; gap: 32px; padding: 16px var(--gw-gutter);
}
.gw-logo { display: flex; align-items: center; flex-shrink: 0; }
.gw-logo img { height: 54px; width: auto; }
.gw-nav { margin-left: auto; }
.gw-nav ul { display: flex; gap: var(--gw-gutter); align-items: center; list-style: none; margin: 0; padding: 0; }
.gw-nav a {
  color: var(--gw-heading); font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-body); letter-spacing: 0.12em; text-transform: uppercase;
}
.gw-nav a:hover { color: var(--gw-accent); }
.gw-header__cta { display: flex; align-items: center; gap: 16px; margin-left: 8px; }
/* Header socials: smaller circles than the footer's 42px so they read as
   secondary next to the phone and estimate buttons. */
.gw-social--header { gap: 8px; }
.gw-social--header a { width: 34px; height: 34px; }
/* In the mobile panel the icons centre under the stacked CTA buttons. */
.gw-social--mnav { justify-content: center; margin-top: 4px; }

/* ---------- Inline estimate form band ---------- */
/* The guides' "stopping point" between content sections: two columns on
   desktop (pitch left, fields right), stacked on mobile, deliberately no
   background image — these pages carry paid traffic. */
/* The band carries its own shade — the site's third black, shared with the
   reviews strip — plus hairline rules, so it reads as a stopping point no
   matter what sections land beside it. Depending on neighbour alternation
   broke on the location pages, where it sat flush against a --surface
   section in the same colour. --gw-section-bg is set alongside per the
   brush-matcher contract. */
.gw-formband {
  scroll-margin-top: 90px;
  background: var(--gw-reviews-bg);
  --gw-section-bg: var(--gw-reviews-bg);
  border-top: 1px solid var(--gw-line);
  border-bottom: 1px solid var(--gw-line);
}
.gw-formband__grid { display: grid; grid-template-columns: 1fr 1.25fr; gap: clamp(32px, 5vw, 72px); align-items: start; }
.gw-formband__intro { display: flex; flex-direction: column; gap: 18px; }
.gw-formband__copy { color: var(--gw-mid); font-size: var(--fs-body); line-height: 1.7; max-width: 460px; }
.gw-formband__form { display: flex; flex-direction: column; gap: 16px; }
.gw-formband__form .gw-btn { align-self: flex-start; }
.gw-formband__note { font-family: var(--gw-body); font-size: 13px; color: var(--gw-mid); }
@media (max-width: 860px) {
  .gw-formband__grid { grid-template-columns: 1fr; }
}

/* ---------- Service detail bullets ---------- */
.gw-svcrow__points { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.gw-svcrow__points li { display: flex; gap: 10px; align-items: flex-start; color: var(--gw-mid); font-size: var(--fs-body); line-height: 1.55; }
.gw-svcrow__points li svg { flex-shrink: 0; margin-top: 4px; color: var(--gw-accent); }

/* ---------- Hero ---------- */
.gw-hero {
  position: relative; display: flex; flex-direction: column;
  justify-content: flex-end; overflow: hidden;
}
.gw-hero:has(.gw-hero__media) { min-height: calc(100vh - 87px); }
/* Inner-page hero. Was 76vh — near enough to the homepage's full-height hero
   that service and location pages read as another landing page instead of a
   page you have already landed on. Compact enough that content starts above
   the fold. */
.gw-hero--short:has(.gw-hero__media) { min-height: 46vh; }
.gw-hero__media { position: absolute; inset: 0; }
.gw-hero__media img { width: 100%; height: 100%; object-fit: cover; }
.gw-hero__scrim { position: absolute; inset: 0; pointer-events: none; }
.gw-hero__scrim--h { background: linear-gradient(90deg, rgba(10,10,10,.94) 0%, rgba(10,10,10,.78) 38%, rgba(10,10,10,.28) 75%, rgba(10,10,10,.15) 100%); }
.gw-hero__scrim--v { background: linear-gradient(180deg, rgba(10,10,10,.55) 0%, rgba(10,10,10,0) 25%, rgba(10,10,10,0) 60%, rgba(10,10,10,.85) 100%); }
.gw-hero__body {
  position: relative; max-width: var(--gw-max); width: 100%; margin: 0 auto;
  padding: 16px var(--gw-gutter) 48px;
}
.gw-hero__flag { display: flex; gap: clamp(16px, 3vw, 32px); align-items: stretch; max-width: 1240px; }
.gw-hero__bar { width: 6px; flex-shrink: 0; background: var(--gw-accent); }
.gw-hero__content { display: flex; flex-direction: column; gap: var(--gw-gutter); flex: 1; }
.gw-hero__h1 { max-width: 64%; }
.gw-hero__h1 .is-accent { color: var(--gw-accent); }
.gw-hero__lead { font-size: 19px; color: var(--gw-mid); max-width: 620px; margin: 0; }
/* Availability line beneath the hero buttons — quiet but present: it carries a
   response-time promise, not a decoration. */
.gw-hero__avail { font-size: 14px; color: var(--gw-text-dim); max-width: 520px; margin: 0; }

/* Inner-page hero type: smaller than the homepage's, matching the project page
   head, so the H1 does not dominate a page the visitor has already chosen.
   The top padding clears the absolutely-positioned breadcrumbs — the hero is
   justify-content:flex-end, so at 46vh the content rose far enough to print
   the eyebrow straight over them. */
.gw-hero--short .gw-hero__body { padding-block: 16px 0; }
.gw-hero--short { justify-content: flex-end; padding-top: 108px; }
/* The badge band carries the hero's bottom spacing on inner pages; the
   homepage's 40/80 is sized for a full-height hero. */
.gw-hero--short .gw-hero__badges .gw-wrap { padding-block: 28px 72px; }
.gw-hero--short .gw-hero__content { gap: 14px; }
.gw-hero--short .gw-hero__h1 { font-size: clamp(38px, 5vw, 76px); max-width: 20ch; }
.gw-hero--short .gw-hero__lead { font-size: var(--fs-meta); max-width: 62ch; }
.gw-hero__actions { display: flex; flex-wrap: wrap; gap: var(--gw-gutter); align-items: center; margin-top: 4px; }
.gw-hero__badges { position: relative; }
.gw-hero__badges .gw-wrap { padding-block: 40px 80px; display: flex; }

/* Breadcrumbs */
.gw-crumbs { position: absolute; top: 0; left: 0; right: 0; z-index: 3; }
.gw-crumbs ol {
  max-width: var(--gw-max); margin: 0 auto; padding: 20px var(--gw-gutter);
  display: flex; flex-wrap: wrap; gap: 8px; list-style: none;
  font-size: 14px; color: var(--gw-dim);
}
.gw-crumbs a { color: rgba(245, 242, 236, 0.88); text-decoration: underline; }
.gw-crumbs [aria-current] { color: var(--gw-accent); }

/* ---------- Trust badges ---------- */
.gw-trust {
  display: inline-flex; align-items: center; gap: var(--gw-gutter); flex-wrap: wrap;
  background: var(--gw-badge-bg); backdrop-filter: blur(6px);
  border: 1px solid var(--gw-badge-line); border-radius: 14px;
  padding: 16px var(--gw-gutter); list-style: none; margin: 0;
}
.gw-trust__item { display: flex; align-items: center; gap: 16px; }
.gw-trust__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex-shrink: 0;
  border: 2px solid var(--gw-accent); border-radius: 10px; color: var(--gw-accent);
}
.gw-trust__title {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: 22px;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--gw-heading); line-height: 1.1;
}
.gw-trust__meta { display: block; font-size: var(--fs-small); color: var(--gw-dim); }
.gw-trust__sep { width: 1px; align-self: stretch; background: var(--gw-line); }

/* ---------- Featured reviews ---------- */
.gw-reviews { background: var(--gw-reviews-bg); }
.gw-reviews__inner {
  max-width: 1360px; margin: 0 auto; padding: clamp(40px, 5vw, 64px) var(--gw-gutter);
  display: flex; flex-direction: column; align-items: center; gap: 32px;
}
.gw-reviews__top { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%; flex-wrap: wrap; }
.gw-googlemark { display: flex; align-items: center; gap: 12px; }
.gw-googlemark__stars { display: flex; gap: 2px; color: var(--gw-accent); }
.gw-googlemark__label {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-dim);
}
.gw-reviewgrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--gw-gutter); width: 100%; align-items: stretch; list-style: none; margin: 0; padding: 0;
}
.gw-reviewcard {
  display: flex; flex-direction: column; gap: 16px;
  background: var(--gw-card); border: 1px solid var(--gw-line);
  border-radius: 12px; padding: var(--gw-gutter); text-align: left;
}
.gw-reviewcard__who { display: flex; align-items: center; gap: 12px; }
.gw-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
  color: var(--gw-ink); font-family: var(--gw-label); font-weight: 600; font-size: 19px;
}
.gw-reviewcard__name {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body);
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-reviewcard__meta { display: block; font-size: var(--fs-small); color: var(--gw-dim); }
.gw-reviewcard__quote { margin: 0; font-size: 15px; line-height: 1.6; color: var(--gw-mid); font-family: var(--gw-body); text-transform: none; }
.gw-stars { display: flex; gap: 2px; color: var(--gw-accent); }

/* ---------- Services (sticky panel + numbered rows) ---------- */
.gw-services { background: var(--gw-bg); }
/* The left column carries the heading, the lead and the estimate form; the
   right carries the numbered service rows. The service rows are a short label
   plus an arrow, so they need far less room than the form — hence the weighting
   toward the left. Collapses to one column below 900px via the breakpoint
   further down. */
.gw-services__grid {
  max-width: var(--gw-max); margin: 0 auto; padding: var(--gw-section-y) var(--gw-gutter);
  display: grid; grid-template-columns: minmax(420px, 1.55fr) minmax(260px, 1fr);
  gap: clamp(32px, 5vw, 72px); align-items: start;
}
.gw-services__panel { display: flex; flex-direction: column; gap: var(--gw-gutter); position: sticky; top: 96px; }
.gw-services__lead { margin: 0; color: var(--gw-mid); max-width: 56ch; }

.gw-svclist { display: flex; flex-direction: column; border-top: 1px solid var(--gw-line); list-style: none; margin: 0; padding: 0; }
.gw-svclist__row {
  display: flex; align-items: center; gap: clamp(16px, 2.5vw, 32px);
  padding: 32px 4px; border-bottom: 1px solid var(--gw-line); color: var(--gw-text);
}
.gw-svclist__row:hover { background: var(--gw-accent-tint); }
.gw-svclist__num { font-family: var(--gw-display); font-size: 26px; color: var(--gw-accent); width: 44px; flex-shrink: 0; }
.gw-svclist__text { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.gw-svclist__name { font-family: var(--gw-display); font-size: var(--fs-h3); text-transform: uppercase; color: var(--gw-heading); }
.gw-svclist__blurb { font-size: var(--fs-body); line-height: 1.45; color: var(--gw-dim); }
.gw-svclist__arrow { color: var(--gw-accent); flex-shrink: 0; }

/* ---------- Inline quick form ---------- */
.gw-quickform {
  /* No max-width: it capped the form at 440px inside a 736px column, leaving a
     ~300px dead gap to its right. The column width is the constraint now. */
  display: flex; flex-direction: column; gap: 16px;
  background: var(--gw-card-alt); border: 1px solid var(--gw-line);
  border-radius: 12px; padding: var(--gw-gutter); margin-top: 4px;
}
.gw-quickform__title {
  margin: 0; font-family: var(--gw-label); font-weight: 600; font-size: 22px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-quickform__sub { margin: -8px 0 0; font-size: 14px; color: var(--gw-mid); line-height: 1.5; }
.gw-field { display: flex; flex-direction: column; gap: 6px; }
.gw-field label, .gw-chipset legend {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-dim); padding: 0;
}
.gw-field .req { color: var(--gw-accent); }
.gw-field input, .gw-field select, .gw-field textarea {
  background: transparent; border: 1px solid var(--gw-line-input); border-radius: 8px;
  padding: 14px 16px; color: var(--gw-text); font-family: var(--gw-body);
  font-size: var(--fs-body); width: 100%; min-width: 0;
}
.gw-field textarea { resize: vertical; }
.gw-field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--gw-accent) 50%), linear-gradient(135deg, var(--gw-accent) 50%, transparent 50%);
  background-position: calc(100% - 20px) center, calc(100% - 14px) center;
  background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; padding-right: 40px;
}
.gw-field option { background: var(--gw-card); color: var(--gw-text); }
.gw-field input:focus, .gw-field select:focus, .gw-field textarea:focus {
  border-color: var(--gw-accent); outline: 2px solid var(--gw-accent); outline-offset: 1px;
}
.gw-field--row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.gw-chipset { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.gw-chipset > div { display: flex; flex-wrap: wrap; gap: 8px; }
.gw-chip {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--gw-line-input); border-radius: 999px; padding: 8px 16px;
  font-family: var(--gw-body); font-size: var(--fs-small); letter-spacing: normal;
  text-transform: none; color: var(--gw-mid); cursor: pointer;
}
.gw-chip:hover { border-color: var(--gw-accent); }
.gw-chip input { accent-color: var(--gw-accent); margin: 0; }
.gw-check { display: flex; gap: 12px; align-items: flex-start; font-family: var(--gw-body); font-size: 14px; line-height: 1.5; letter-spacing: normal; text-transform: none; color: var(--gw-mid); cursor: pointer; }
.gw-check input { accent-color: var(--gw-accent); width: 18px; height: 18px; margin-top: 2px; flex-shrink: 0; }

/* ---------- Areas ---------- */
.gw-areas { --gw-section-bg: var(--gw-areas-bg); background: var(--gw-areas-bg); border-top: 1px solid var(--gw-accent-line); border-bottom: 1px solid var(--gw-accent-line); }
.gw-areas__grid {
  max-width: var(--gw-max); margin: 0 auto; padding: 0 var(--gw-gutter);
  display: grid; gap: 48px; align-items: stretch;
  grid-template-columns: minmax(320px, 1fr) minmax(340px, 1.5fr);
}
.gw-areas__text { display: flex; flex-direction: column; gap: var(--gw-gutter); justify-content: center; padding-block: clamp(48px, 6vw, 72px); }
.gw-areas__text p { margin: 0; color: var(--gw-mid); max-width: 480px; }
/* The map sits inside the same box as the text column — same side gutter
   and the same vertical padding — so it reads as a column, not an image
   bleeding off the section's right edge and top/bottom rules. */
.gw-areas__map { position: relative; }
/* Absolutely placed so the TEXT column alone sets the row height; the map
   then fills the same padded box as the text (same clamp as
   .gw-areas__text) and scales down to fit it, right-aligned. */
.gw-areas__map img {
  position: absolute; left: 0; right: 0;
  top: clamp(48px, 6vw, 72px); bottom: clamp(48px, 6vw, 72px);
  width: 100%; height: calc(100% - 2 * clamp(48px, 6vw, 72px));
  object-fit: contain; object-position: right center; display: block;
}
.gw-citygrid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; max-width: 480px; list-style: none; margin: 0; padding: 0; }
.gw-citygrid a {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: var(--gw-bg); border: 1px solid rgba(255, 255, 255, 0.1); padding: 16px;
  color: var(--gw-heading); font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-body); letter-spacing: 0.06em; text-transform: uppercase;
}
.gw-citygrid a:hover { border-color: var(--gw-accent); }
.gw-citygrid__arrow { color: var(--gw-accent); display: inline-flex; }

/* ---------- Who we are / before-after ---------- */
.gw-whoweare { background: var(--gw-bg); }
.gw-whoweare__grid,
.gw-team__grid {
  max-width: var(--gw-max); margin: 0 auto; padding-inline: var(--gw-gutter);
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: clamp(32px, 5vw, 72px); align-items: center;
}
.gw-whoweare__text, .gw-team__text { display: flex; flex-direction: column; gap: 20px; }
.gw-whoweare__text p, .gw-team__text p { margin: 0; color: var(--gw-mid); max-width: 520px; }
.gw-beforeafter { display: flex; flex-direction: column; gap: 12px; }
.gw-beforeafter__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.gw-beforeafter__pair figure { margin: 0; }
.gw-balabel {
  position: absolute; top: 12px; left: 12px;
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; padding: 6px 12px 4px;
}
.gw-balabel--before { background: rgba(10, 10, 10, 0.85); color: var(--gw-text); }
.gw-balabel--after { background: var(--gw-accent); color: var(--gw-ink); }
.gw-beforeafter__caption { font-size: 14px; color: var(--gw-dim); margin: 0; }

/* ---------- Process ---------- */
.gw-process { position: relative; background: var(--gw-areas-bg); border-top: 1px solid var(--gw-accent-line); overflow: hidden; }
.gw-process--bg { background: var(--gw-bg); }
.gw-process__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--gw-gutter); list-style: none; margin: 0; padding: 0; }
.gw-step { position: relative; background: var(--gw-card); border: 1px solid var(--gw-line-soft); padding: 88px var(--gw-gutter) var(--gw-gutter); overflow: hidden; }
.gw-step__num {
  position: absolute; top: -22px; right: -6px; pointer-events: none;
  font-family: var(--gw-display); font-size: 120px; line-height: 1; color: var(--gw-accent-line);
}
.gw-step__title { position: relative; font-family: var(--gw-label); font-weight: 600; font-size: 22px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gw-heading); }
.gw-step p { position: relative; margin: 8px 0 0; font-size: var(--fs-body); line-height: 1.55; color: var(--gw-mid); }

/* ---------- Projects ---------- */
.gw-projects { background: var(--gw-areas-bg); --gw-section-bg: var(--gw-areas-bg); border-top: 1px solid var(--gw-accent-line); }
.gw-projects__top { display: flex; gap: var(--gw-gutter); align-items: flex-end; justify-content: space-between; flex-wrap: wrap; margin-bottom: 40px; }
.gw-projgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--gw-gutter); list-style: none; margin: 0; padding: 0; }
/* auto-fit stretches a lone item to the full row, which turned a single related
   project into a full-bleed photo. Cap the track when there are few items so a
   short row still reads as cards. */
.gw-projgrid:has(> li:last-child:nth-child(1)) { grid-template-columns: minmax(0, 320px); }
.gw-projgrid:has(> li:last-child:nth-child(2)) { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 760px; }
.gw-projgrid a { display: flex; flex-direction: column; gap: 16px; color: var(--gw-text); }
/* Title and meta must stack — they are spans inside one <span> wrapper. */
.gw-projgrid__name { display: block; font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body); letter-spacing: 0.05em; text-transform: uppercase; color: var(--gw-heading); }
.gw-projgrid__meta { display: block; font-size: var(--fs-small); color: var(--gw-dim); margin-top: 4px; }

/* ---------- Value props ---------- */
.gw-values { background: var(--gw-bg); padding-block: clamp(48px, 7vw, 88px); }
.gw-valuegrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--gw-gutter); list-style: none; margin: 0; padding: 0; }
.gw-valuecard { display: flex; gap: 16px; align-items: flex-start; border: 1px solid rgba(255,255,255,.1); padding: var(--gw-gutter); background: var(--gw-card-alt); }
.gw-valuecard__icon {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 40px; height: 40px; border: 2px solid var(--gw-accent); border-radius: 50%; color: var(--gw-accent);
}
.gw-valuecard__name { display: block; font-family: var(--gw-label); font-weight: 600; font-size: 22px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gw-heading); }
.gw-valuecard__blurb { display: block; font-size: var(--fs-body); line-height: 1.55; color: var(--gw-mid); margin-top: 8px; }

/* ---------- Team callout ---------- */
.gw-team { background: var(--gw-areas-bg); border-top: 1px solid var(--gw-accent-line); padding-block: clamp(56px, 7vw, 88px); }

/* ---------- FAQ ---------- */
.gw-faq-section { background: var(--gw-bg); border-top: 1px solid var(--gw-accent-line); }
.gw-faq {
  max-width: var(--gw-max); margin: 0 auto; padding: var(--gw-section-y) var(--gw-gutter);
  display: grid; grid-template-columns: minmax(260px, 1fr) minmax(320px, 1.6fr); gap: clamp(32px, 5vw, 72px);
}
/* .gw-faq is a self-contained section wrapper: it carries its own max-width and
   a full --gw-section-y of padding. Templates that only wanted its two-column
   grid nested it inside .gw-wrap, which stacked both paddings and made those
   sections 208px tall top and bottom against every other section's 104px.
   Inside a .gw-wrap the padding belongs to the parent, so drop it here. */
.gw-wrap.gw-faq { padding-block: 0; }
.gw-faq .gw-heading { align-items: flex-start; }
.gw-faq .gw-heading__bar { align-self: stretch; max-height: 96px; }
.gw-faq__list { display: flex; flex-direction: column; }
.gw-faq__item { border-bottom: 1px solid var(--gw-line); }
.gw-faq__item h3 { margin: 0; font-size: inherit; }
.gw-faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left; padding: 22px 0;
  color: var(--gw-heading); font-family: var(--gw-label); font-weight: 600;
  font-size: clamp(19px, 1.6vw, 23px); letter-spacing: 0.03em; text-transform: uppercase;
}
.gw-faq__q:hover { color: var(--gw-accent); }
.gw-faq__icon { color: var(--gw-accent); flex-shrink: 0; display: inline-flex; transition: transform 0.2s ease; }
.gw-faq__q[aria-expanded="true"] .gw-faq__icon { transform: rotate(180deg); }
/* Animated answer panel.
 *
 * grid-template-rows: 0fr -> 1fr is what makes "collapse to nothing / expand to
 * whatever the content needs" transitionable without measuring heights in JS.
 * The inner element must be min-height:0 and overflow:hidden or the text spills
 * out of the collapsed track instead of being clipped.
 *
 * The button sits inside an <h3> for document outline, so the panel is the
 * h3's next sibling, not the button's — hence :has() on the wrapper. There is
 * an [inert] fallback immediately after for engines without :has() support, so
 * the panel is never left open-but-unstyled.
 */
.gw-faq__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-faq__item:has(.gw-faq__q[aria-expanded="true"]) .gw-faq__panel { grid-template-rows: 1fr; }
/* The clipping element. overflow:hidden is what makes a 0fr track actually
   hide its content, and min-height:0 stops the grid item claiming its
   min-content height and refusing to shrink. */
.gw-faq__panel > * { min-height: 0; overflow: hidden; }
/* The answer's own padding must NOT live on the clipped child: a 0fr track
   still renders its child's padding, which is why collapsed answers stayed
   visible as a sliver of text. The padding moves to the inner <p> only while
   the panel is open, so a collapsed panel truly measures zero. */
.gw-faq__a {
  margin: 0; padding: 0;
  font-size: var(--fs-body); line-height: 1.6; color: var(--gw-mid); max-width: 640px;
}
.gw-faq__item:has(.gw-faq__q[aria-expanded="true"]) .gw-faq__a { padding: 0 0 22px; }

/* No :has() — fall back to the attribute the JS maintains anyway. */
@supports not selector(:has(*)) {
  .gw-faq__panel { grid-template-rows: 1fr; }
  .gw-faq__a { padding: 0 0 22px; }
  .gw-faq__panel[inert] { grid-template-rows: 0fr; }
  .gw-faq__panel[inert] .gw-faq__a { padding: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* Snap, do not slide. The global clamp already kills the duration; this is
     here so the two states stay unambiguous rather than relying on a 0.01ms
     transition landing. */
  .gw-faq__panel { transition: none !important; }
}

/* ---------- Footer (CTA-led — the gold banner lives INSIDE the footer) ---------- */
.gw-footer { position: relative; background: var(--gw-bg); overflow: hidden; }
.gw-footer__glow { position: absolute; inset: 0; pointer-events: none; background: radial-gradient(ellipse 900px 420px at 82% 12%, rgba(240,163,26,.09), transparent 70%); }
.gw-footer__watermark { position: absolute; right: -2%; bottom: 0; height: 55%; width: auto; opacity: 0.05; pointer-events: none; user-select: none; }
.gw-cta { position: relative; background: var(--gw-accent-deep); }
.gw-cta__inner {
  max-width: var(--gw-max); margin: 0 auto; padding: clamp(48px, 6vw, 72px) var(--gw-gutter);
  display: flex; flex-wrap: wrap; gap: 32px; align-items: center; justify-content: space-between;
}
.gw-cta h2 { color: var(--gw-ink); font-size: clamp(28px, 3.4vw, 45px); line-height: 1.05; max-width: 640px; }
.gw-cta__text { display: flex; flex-direction: column; gap: 10px; }
.gw-cta__sub { color: rgba(19, 16, 7, 0.78); font-size: var(--fs-body); line-height: 1.6; max-width: 560px; }
.gw-cta__actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.gw-cta .gw-btn-outline { color: var(--gw-ink); border-color: var(--gw-ink); }
.gw-cta .gw-btn-outline:hover { background: rgba(19, 16, 7, 0.12); color: var(--gw-ink); }
/* Mid-page variant: same band, standing on its own rather than inside the
   footer, so it needs its own top offset to clear the preceding section. */
.gw-cta--mid { margin-top: 0; }

/* Long-form text pages (privacy policy, terms). Narrower than the site max so
   the measure stays readable — legal copy is dense and unbroken by imagery,
   and full-width lines are what make it unreadable. */
/* Editor content on the generic page template. Runs the full wrapper width so
   it lines up with the page head above it and with every other section on the
   site — the rhythm and list rules below carry the readability instead. */
.gw-prose > * + * { margin-top: 1.1em; }
.gw-prose > h2 { margin-top: 1.9em; margin-bottom: 0.4em; font-size: var(--fs-h3); }
.gw-prose > h3 { margin-top: 1.6em; margin-bottom: 0.3em; }
.gw-prose > p, .gw-prose > ul, .gw-prose > ol { line-height: 1.75; color: var(--gw-mid); }
.gw-prose > ul, .gw-prose > ol { padding-left: 1.4em; }
.gw-prose li + li { margin-top: 0.5em; }
.gw-prose a { text-decoration: underline; text-underline-offset: 0.18em; }
/* Block-editor layout. Width here is the editor's choice — a two-column block
   for a half-width column, alignwide to break out of the measure, alignfull to
   reach the viewport edge. The theme provides the mechanism and stays out of
   the decision. alignfull cancels .gw-wrap's gutter with a negative margin,
   which is why it is expressed against the gutter variable rather than vw. */
.gw-prose .alignwide { width: calc(100% + 2 * var(--gw-gutter)); margin-inline: calc(-1 * var(--gw-gutter)); max-width: none; }
.gw-prose .alignfull { width: 100vw; margin-inline: calc(50% - 50vw); max-width: none; }
/* A full-bleed block that paints a background needs its own vertical padding,
   or the colour band hugs the text; and its content has to be pulled back to
   the wrapper, or the text runs to the screen edges. Both only apply when the
   editor actually set a background — a plain alignfull image should still
   reach the edges untouched. */
.gw-prose .alignfull.has-background,
.gw-prose .alignfull.has-background-image { padding-block: clamp(32px, 5vw, 64px); }
.gw-prose .alignfull.has-background > :not(.alignfull),
.gw-prose .alignfull.has-background-image > :not(.alignfull) {
  max-width: var(--gw-max); margin-inline: auto; padding-inline: var(--gw-gutter);
}
/* Wide blocks with a background get breathing room too, but keep their own
   width — they are already inside the wrapper. */
.gw-prose .alignwide.has-background { padding: clamp(24px, 3vw, 40px) var(--gw-gutter); }
.gw-prose .aligncenter { margin-inline: auto; }
.gw-prose .wp-block-columns { display: flex; flex-wrap: wrap; gap: clamp(24px, 4vw, 48px); align-items: flex-start; }
.gw-prose .wp-block-column { flex: 1 1 0; min-width: 0; }
.gw-prose .wp-block-column > * + * { margin-top: 1.1em; }
.gw-prose img, .gw-prose figure { max-width: 100%; height: auto; }
.gw-prose figure { margin-inline: 0; }
.gw-prose figcaption { font-size: var(--fs-meta); color: var(--gw-dim); margin-top: 8px; }
/* Columns stack on narrow screens — flex-wrap alone keeps them side by side
   until they hit zero width, which is unreadable long before it looks broken. */
@media (max-width: 700px) {
  .gw-prose .wp-block-column { flex-basis: 100%; }
}

.gw-footer__main { position: relative; max-width: var(--gw-max); margin: 0 auto; padding: clamp(48px, 6vw, 64px) var(--gw-gutter) 56px; }
.gw-footer__cols { display: grid; grid-template-columns: minmax(240px, 1.2fr) repeat(3, minmax(170px, 1fr)); gap: 40px; }
.gw-footer__brand { display: flex; flex-direction: column; gap: 20px; }
.gw-footer__brand img { height: 88px; width: auto; align-self: flex-start; }
.gw-footer__blurb { font-size: var(--fs-body); line-height: 1.7; color: var(--gw-mid); max-width: 280px; }
/* Google Preferred Sources deeplink — a quieter cousin of .gw-btn-outline so it
   reads as a footer utility, not a page CTA. align-self stops the flex column
   stretching it full width. */
.gw-footer__prefsrc { align-self: flex-start; font-size: 12px; padding: 8px 16px 5px; }
.gw-footer__prefsrc svg { margin-right: 8px; }
.gw-footer__coltitle { font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body); letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-accent); margin: 0; }
.gw-footer__col ul { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.gw-footer__col a, .gw-footer__col span { font-size: var(--fs-meta); color: rgba(245, 242, 236, 0.82); }
.gw-footer__col a:hover { color: var(--gw-accent-hover); }
.gw-footer__col svg { color: var(--gw-accent); width: 22px; display: inline-block; vertical-align: -3px; margin-right: 2px; }
/* Contact column: the phone number keeps the same icon row as the other
   details — it only reads brighter, since it is the one line here worth
   tapping. Tried as a display-size number and as a filled button; both drew
   more attention than the footer wants. */
/* --gw-cream was never defined, so this declaration was dropped and the
   link fell back to the accent colour. --gw-text is the intended cream. */
.gw-footer__meta a[href^="tel:"] { color: var(--gw-text); }
.gw-footer__meta a[href^="tel:"]:hover { color: var(--gw-accent-hover); }
/* Each row is a flex line so the icon-to-text gap is set once here, rather
   than depending on the icon box's own margin — which left phone and mail
   looking glued to their labels. align-items: start keeps the icon on the
   first line of the two-line address rather than centring on the block. */
.gw-footer__meta a, .gw-footer__meta span { display: flex; align-items: flex-start; gap: 10px; }
.gw-footer__meta svg { flex-shrink: 0; margin-right: 0; }
.gw-social { display: flex; gap: 10px; list-style: none; margin: 0; padding: 0; }
.gw-social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border: 1.5px solid rgba(245, 242, 236, 0.25);
  border-radius: 50%; color: rgba(245, 242, 236, 0.85);
}
.gw-social a:hover { border-color: var(--gw-accent); color: var(--gw-accent); }
.gw-footer__bar { position: relative; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.gw-footer__bar > div {
  max-width: var(--gw-max); margin: 0 auto; padding: 20px var(--gw-gutter);
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between;
  font-size: var(--fs-small); color: var(--gw-dim);
}

/* ---------- Projects archive + case study ---------- */
.gw-projfilter { background: var(--gw-bg2); border-block: 1px solid var(--gw-accent-line); padding-block: 24px; }
.gw-projfilter__row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; }
.gw-projfilter__row + .gw-projfilter__row { margin-top: 14px; }
.gw-projfilter__label {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-accent);
  min-width: 84px;
}
.gw-taglist .gw-tag.is-active { background: var(--gw-accent); border-color: var(--gw-accent); color: var(--gw-ink); }
.gw-projgrid--archive { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.gw-projgrid__badge {
  position: absolute; top: 12px; left: 12px;
  background: var(--gw-accent); color: var(--gw-ink);
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; padding: 6px 12px 4px;
}
.gw-projempty { color: var(--gw-mid); font-size: var(--fs-lead, 19px); }

.gw-projfacts { margin: 24px 0 0; border-top: 1px solid var(--gw-line); }
.gw-projfacts > div { display: flex; gap: 20px; align-items: baseline; padding: 14px 0; border-bottom: 1px solid var(--gw-line); }
.gw-projfacts dt {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--gw-text-dim, rgba(245,242,236,.65));
  width: 110px; flex-shrink: 0;
}
.gw-projfacts dd { margin: 0; font-family: var(--gw-label); font-size: 20px; text-transform: uppercase; color: var(--gw-heading); }
.gw-projfacts dd a { color: var(--gw-accent); text-decoration: none; }
.gw-projfacts dd a:hover { text-decoration: underline; }
.gw-projbody { color: var(--gw-mid); font-size: 18px; line-height: 1.7; }

/* WordPress pagination */
.gw-projects .nav-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 40px; }
.gw-projects .page-numbers {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; padding: 10px 14px;
  border: 1px solid var(--gw-line-strong); border-radius: var(--r-md, 8px);
  font-family: var(--gw-label); font-weight: 600; text-transform: uppercase;
  color: var(--gw-text); text-decoration: none;
}
.gw-projects .page-numbers:hover { border-color: var(--gw-accent); color: var(--gw-accent); }
.gw-projects .page-numbers.current { background: var(--gw-accent); border-color: var(--gw-accent); color: var(--gw-ink); }

/* ---------- Service detail rows, stats, materials, testimonial ----------
   These were dropped when the stylesheet was rewritten; the service and
   location templates depend on them.                                        */
.gw-svcrow {
  display: grid; grid-template-columns: 1.05fr 1fr;
  gap: clamp(28px, 4vw, 56px); align-items: center;
  border-top: 1px solid var(--gw-line-soft); padding-top: clamp(32px, 4vw, 56px);
}
.gw-svcrow:nth-of-type(even) { grid-template-columns: 1fr 1.05fr; }
.gw-svcrow:nth-of-type(even) .gw-svcrow__media { order: 2; }
.gw-svcrow__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--r-sm, 5px); }
.gw-svcrow__media img { width: 100%; height: 100%; object-fit: cover; }
.gw-svcrow__body { display: flex; flex-direction: column; gap: 18px; }
.gw-svcrow__body p { margin: 0; color: var(--gw-mid); font-size: 18px; line-height: 1.65; }
.gw-svcrow__body h3 { text-wrap: balance; }

.gw-ticklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.gw-ticklist li { display: flex; gap: 12px; align-items: flex-start; font-size: var(--fs-body); line-height: 1.5; color: var(--gw-dim); }
.gw-ticklist svg { color: var(--gw-accent); flex-shrink: 0; margin-top: 3px; }

.gw-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--gw-gutter); }
.gw-stat { display: flex; flex-direction: column; gap: 6px; }
.gw-stat__value { font-family: var(--gw-display); font-size: clamp(44px, 4.5vw, 64px); line-height: 1; color: var(--gw-accent); }
.gw-stat__label {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-dim);
}

.gw-materials { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--gw-line); }
.gw-materials li {
  display: flex; gap: 16px; align-items: baseline; justify-content: space-between;
  padding: 18px 0; border-bottom: 1px solid var(--gw-line);
}
.gw-materials__name {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: 20px;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-materials__use { display: block; font-size: var(--fs-meta); color: var(--gw-mid); margin-top: 4px; }

.gw-quote { max-width: 1000px; margin-inline: auto; display: flex; flex-direction: column; align-items: center; gap: var(--gw-gutter); text-align: center; }
.gw-quote blockquote {
  margin: 0; font-family: var(--gw-display); font-size: clamp(22px, 2.6vw, 34px);
  line-height: 1.3; color: var(--gw-heading); text-transform: none; text-wrap: balance;
}
.gw-quote__who { font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body); letter-spacing: 0.12em; text-transform: uppercase; color: var(--gw-accent); }
.gw-quote__meta { font-size: var(--fs-meta); color: var(--gw-dim); margin-top: 6px; }

@media (max-width: 860px) {
  .gw-svcrow,
  .gw-svcrow:nth-of-type(even) { grid-template-columns: 1fr; }
  .gw-svcrow:nth-of-type(even) .gw-svcrow__media { order: 0; }
}

/* ---------- Service page ---------- */
.gw-svcintro,
.gw-matgrid {
  display: grid; grid-template-columns: minmax(280px, 1fr) minmax(340px, 1.4fr);
  gap: clamp(32px, 5vw, 72px); align-items: start;
}
.gw-svcintro p { color: var(--gw-mid); font-size: 18px; line-height: 1.65; }
.gw-svcdetails { background: var(--gw-bg); padding-block: var(--gw-section-y); }
.gw-svcrows { display: flex; flex-direction: column; gap: clamp(40px, 5vw, 72px); margin-top: 40px; }
.gw-citygrid--wide { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); max-width: none; margin-top: 40px; }

@media (max-width: 860px) {
  .gw-svcintro, .gw-matgrid { grid-template-columns: 1fr; }
}

/* ---------- Thank-you page ---------- */
.gw-ty {
  position: relative; background: var(--gw-bg); overflow: hidden;
  min-height: calc(100vh - 87px); display: flex; align-items: center;
}
.gw-ty__glow { position: absolute; inset: 0; pointer-events: none; background: radial-gradient(ellipse 900px 480px at 50% 0%, rgba(240,163,26,.12), transparent 70%); }
.gw-ty__inner {
  position: relative; max-width: 840px; margin: 0 auto; width: 100%;
  padding: var(--gw-section-y) var(--gw-gutter);
  display: flex; flex-direction: column; align-items: center; gap: 28px; text-align: center;
}
.gw-ty__check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 76px; height: 76px; border: 3px solid var(--gw-accent);
  border-radius: 50%; color: var(--gw-accent);
}
.gw-ty h1 { font-size: clamp(38px, 5vw, 64px); line-height: 1.05; }
.gw-ty__lead { margin: 0; font-size: 19px; line-height: 1.65; color: var(--gw-mid); max-width: 560px; }
.gw-ty__lead strong { color: var(--gw-heading); }
.gw-ty__card {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--gw-card); border: 1px solid var(--gw-line);
  border-radius: 12px; padding: 24px 32px;
}
.gw-ty__card-label {
  font-family: var(--gw-label); font-weight: 600; font-size: 14px;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--gw-dim);
}
.gw-ty__phone {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--gw-label); font-weight: 600; font-size: clamp(26px, 3vw, 34px);
  letter-spacing: 0.04em; color: var(--gw-accent); text-decoration: none;
}
.gw-ty__phone:hover { color: var(--gw-accent-hover); }
.gw-ty__hours { font-size: var(--fs-small); color: var(--gw-text-dim, rgba(245,242,236,.6)); }
/* Before-we-arrive checklist: the one left-aligned block on a centred page —
   four scannable pointers read as a list, not as centred prose. */
.gw-ty__before { max-width: 560px; text-align: left; display: flex; flex-direction: column; gap: 14px; }
.gw-ty__before h2 { font-size: clamp(20px, 2.2vw, 26px); text-align: center; }
.gw-ty__before-intro { color: var(--gw-mid); font-size: var(--fs-body); line-height: 1.7; }
.gw-ty__before ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.gw-ty__before li { display: flex; gap: 12px; align-items: flex-start; color: var(--gw-mid); font-size: var(--fs-body); line-height: 1.6; }
.gw-ty__before li svg { flex-shrink: 0; margin-top: 4px; color: var(--gw-accent); }

.gw-ty__links-label {
  width: 100%; font-family: var(--gw-display); font-size: var(--fs-small);
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--gw-text-dim);
}
.gw-ty__links { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; margin-top: 4px; }
.gw-ty__link {
  display: inline-flex; align-items: center; gap: 10px;
  border: 1.5px solid var(--gw-line-strong); border-radius: var(--r-md, 8px);
  color: var(--gw-text); text-decoration: none;
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-meta);
  letter-spacing: 0.1em; text-transform: uppercase; padding: 14px 24px 12px;
}
.gw-ty__link:hover { border-color: var(--gw-accent); color: var(--gw-accent); }

/* ---------- Contact page ---------- */
.gw-cthero { position: relative; background: var(--gw-bg); overflow: hidden; }
.gw-cthero__glow { position: absolute; inset: 0; pointer-events: none; background: radial-gradient(ellipse 900px 420px at 80% 0%, rgba(240,163,26,.1), transparent 70%); }
.gw-cthero__inner { position: relative; max-width: var(--gw-max); margin: 0 auto; padding: clamp(48px,6vw,80px) var(--gw-gutter) clamp(32px,4vw,48px); }
.gw-cthero h1 { font-size: clamp(40px, 5.2vw, 72px); }
.gw-cthero__lead { margin: 12px 0 0; font-size: 19px; line-height: 1.6; color: var(--gw-mid); max-width: 560px; }

.gw-contact { background: var(--gw-bg); }
.gw-contact__grid {
  max-width: var(--gw-max); margin: 0 auto;
  padding: 0 var(--gw-gutter) clamp(56px,7vw,88px);
  display: grid; grid-template-columns: minmax(420px,1.5fr) minmax(300px,1fr);
  gap: clamp(32px,4vw,56px); align-items: start;
}

.gw-ctform {
  display: flex; flex-direction: column; gap: 20px;
  background: var(--gw-card); border: 1px solid var(--gw-line);
  border-radius: 12px; padding: clamp(24px,3vw,40px);
}
.gw-ctform__title {
  margin: 0; font-family: var(--gw-label); font-weight: 600; font-size: 26px;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-ctform__note { margin: 0; font-size: var(--fs-small); color: var(--gw-text-dim, rgba(245,242,236,.6)); }
.gw-btn--lg { font-size: 23px; padding: 13px 28px 9px; align-self: flex-start; }

.gw-ctcard {
  display: flex; flex-direction: column;
  background: var(--gw-card-alt); border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px; overflow: hidden;
}
.gw-ctcard__row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 22px 28px; border-bottom: 1px solid rgba(255,255,255,.1);
  color: var(--gw-text); text-decoration: none;
}
.gw-ctcard__row:hover { background: var(--gw-accent-tint); }
.gw-ctcard__lead { display: flex; align-items: center; gap: 14px; }
.gw-ctcard__icon { color: var(--gw-accent); width: 24px; display: inline-flex; justify-content: center; flex-shrink: 0; }
.gw-ctcard__big {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: 20px;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-ctcard__sub { display: block; font-size: var(--fs-small); color: var(--gw-text-dim, rgba(245,242,236,.65)); }
.gw-ctcard__arrow { color: var(--gw-accent); flex-shrink: 0; display: inline-flex; }
.gw-ctcard__pair { display: grid; grid-template-columns: 1fr 1fr; }
.gw-ctcard__cell { display: flex; flex-direction: column; gap: 6px; padding: 22px 28px; border-bottom: 1px solid rgba(255,255,255,.1); }
.gw-ctcard__cell:first-child { border-right: 1px solid rgba(255,255,255,.1); }
.gw-ctcard__label {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--gw-label); font-weight: 600; font-size: 14px;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-accent);
}
.gw-ctcard__text { font-size: 15px; line-height: 1.5; color: var(--gw-dim); }
.gw-ctcard__areas { display: flex; flex-direction: column; gap: 14px; padding: 22px 28px; }
.gw-ctcard__hint { margin: 0; font-size: 14px; line-height: 1.5; color: var(--gw-text-dim, rgba(245,242,236,.6)); }
.gw-taglist { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
/* The design shows these as bordered pills; .gw-tag alone was losing its border
   here because the anchor reset stripped it. */
.gw-taglist .gw-tag {
  display: inline-flex; align-items: center; text-decoration: none;
  border: 1px solid var(--gw-line-input); border-radius: 999px;
  padding: 8px 16px 6px;
  font-family: var(--gw-label); font-weight: 600; font-size: 14px;
  letter-spacing: 0.06em; text-transform: uppercase; color: rgba(245, 242, 236, 0.9);
}
.gw-taglist a.gw-tag:hover { border-color: var(--gw-accent); color: var(--gw-accent); }

.gw-cttrust { background: var(--gw-bg); border-top: 1px solid var(--gw-accent-line); }
.gw-cttrust__inner {
  max-width: var(--gw-max); margin: 0 auto; padding: clamp(32px,4vw,48px) var(--gw-gutter);
  display: flex; flex-wrap: wrap; gap: clamp(24px,4vw,56px); align-items: center; justify-content: center;
}
.gw-cttrust__item { display: flex; align-items: center; gap: 14px; }

.gw-ctmap { background: var(--gw-bg2); border-top: 1px solid var(--gw-accent-line); }
.gw-ctmap__frame, .gw-ctmap__placeholder { width: 100%; height: clamp(320px,40vw,480px); background: var(--gw-reviews-bg); }
.gw-ctmap__frame iframe { width: 100%; height: 100%; border: 0; display: block; }
/* Static-map variant: the whole image is the tap target that opens Maps. */
a.gw-ctmap__frame { display: block; overflow: hidden; }
a.gw-ctmap__frame img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s ease; }
a.gw-ctmap__frame:hover img { transform: scale(1.02); }
.gw-ctmap__caption { max-width: var(--gw-max); margin: 0 auto; padding: 14px var(--gw-gutter) 24px; font-size: 14px; color: var(--gw-mid); }
.gw-ctmap__placeholder {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; color: var(--gw-text-dim, rgba(245,242,236,.5)); text-align: center; padding: var(--gw-gutter);
}
.gw-ctmap__placeholder svg { color: var(--gw-accent); }
.gw-ctmap__title { font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body); letter-spacing: 0.1em; text-transform: uppercase; }
.gw-ctmap__hint { font-size: 14px; max-width: 320px; }

@media (max-width: 900px) {
  .gw-contact__grid { grid-template-columns: 1fr; }
}

/* ---------- Mega menu ----------
   Services and Areas open a panel listing the real Service / Location pages
   plus a feature image. Hover opens on pointer devices; the trigger is a real
   button so keyboard and touch work too.                                     */
/* The panel is positioned against the HEADER, not the nav item, so it can never
   overflow the viewport regardless of where the trigger sits. */
.gw-header__inner { position: relative; }
.gw-mega { position: static; }
.gw-mega__trigger {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; cursor: pointer; padding: 0;
  color: var(--gw-heading); font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-body); letter-spacing: var(--tr-nav); text-transform: uppercase;
}
.gw-mega__trigger:hover,
.gw-mega__trigger[aria-expanded="true"] { color: var(--gw-accent); }
.gw-mega__trigger svg { transition: transform 0.18s ease; }
.gw-mega__trigger[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Full-bleed bar across the viewport. The background spans edge to edge while
   the contents inside stay on the site container (see .gw-mega__inner), so the
   panel reads as a band rather than a floating card. */
.gw-mega__panel {
  position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%);
  z-index: 70; width: 100vw;
  background: var(--gw-bg);
  border-top: 1px solid var(--gw-accent-line);
  border-bottom: 1px solid var(--gw-accent-line);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
  padding: clamp(28px, 3vw, 44px) 0;
}
.gw-mega__panel[hidden] { display: none; }
/* Bridge the gap so moving the pointer from trigger to panel does not close it. */
.gw-mega__panel::before { content: ""; position: absolute; inset-inline: 0; top: -20px; height: 20px; }
/* No side feature any more — the entries carry their own thumbnails, so the
   panel is a single column constrained to the site container. The background
   stays full-bleed; only the contents are capped. */
.gw-mega__inner {
  max-width: var(--gw-max); margin-inline: auto;
  padding-inline: var(--gw-gutter);
}
/* Every image in every panel is rounded and clips its contents — one rule so
   the panels cannot drift apart. */
.gw-mega__panel .gw-frame {
  border-radius: var(--r-md, 8px); overflow: hidden;
}
.gw-mega__panel .gw-frame img { transition: transform 0.35s ease; }
@media (prefers-reduced-motion: reduce) {
  .gw-mega__panel .gw-frame img { transition: none; }
}
.gw-mega__list a:hover .gw-frame img { transform: scale(1.06); }
/* Exactly two columns of entries, filled down-then-across so the ragged edge
   lands at the bottom of the second column rather than mid-row (5 services read
   as 3 + 2). --gw-mega-rows is set inline per panel from the item count, so the
   split stays balanced as services or cities are added. */
.gw-mega__list ul {
  list-style: none; margin: 14px 0 18px; padding: 0;
  display: grid;
  grid-template-rows: repeat(var(--gw-mega-rows, 3), auto);
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 4px 40px; align-items: start;
}
/* Thumbnail left, text right. align-items:center keeps short one-line entries
   (cities) vertically centred against the image rather than top-hugging. */
.gw-mega__list a {
  display: flex; gap: 14px; align-items: center;
  padding: 12px 0; text-decoration: none;
  border-top: 1px solid var(--gw-line);
}
.gw-mega__text { display: block; min-width: 0; }
.gw-mega__name {
  display: block; font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-body);
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--gw-heading);
}
.gw-mega__list a:hover .gw-mega__name { color: var(--gw-accent); }
/* Sentence case + body font: these are descriptions, not labels. The uppercase
   label styling was inheriting from the nav. */
.gw-mega__blurb {
  display: block; font-family: var(--gw-body); font-size: var(--fs-small);
  font-weight: 400; letter-spacing: normal; text-transform: none;
  line-height: 1.45; color: var(--gw-dim); margin-top: 4px;
}
.gw-mega__list .gw-inlinelink { white-space: nowrap; }


/* ---------- Hamburger + mobile nav panel ----------
   Added beyond the designs: they hide .gw-nav below 1060px with no replacement,
   which left phones AND the whole tablet band with no way to reach the site's
   pages. Most paid traffic converts on mobile.                                 */
.gw-burger {
  display: none;
  margin-left: auto;
  background: transparent;
  border: 1.5px solid var(--gw-line-strong);
  border-radius: var(--r-md, 8px);
  color: var(--gw-heading);
  padding: 10px 12px;
  cursor: pointer;
  line-height: 0;
}
.gw-burger:hover { border-color: var(--gw-accent); color: var(--gw-accent); }
.gw-burger__box { display: block; width: 22px; height: 16px; position: relative; }
.gw-burger__bar {
  position: absolute; left: 0; right: 0; height: 2px;
  background: currentColor; border-radius: 2px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.gw-burger__bar:nth-child(1) { top: 0; }
.gw-burger__bar:nth-child(2) { top: 7px; }
.gw-burger__bar:nth-child(3) { top: 14px; }
/* Bars morph to an X while the panel is open. */
.gw-burger[aria-expanded="true"] .gw-burger__bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.gw-burger[aria-expanded="true"] .gw-burger__bar:nth-child(2) { opacity: 0; }
.gw-burger[aria-expanded="true"] .gw-burger__bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.gw-mobilenav { position: fixed; inset: 0; z-index: 90; }
.gw-mobilenav[hidden] { display: none; }
.gw-mobilenav__scrim { position: absolute; inset: 0; background: rgba(6, 6, 6, 0.72); backdrop-filter: blur(2px); }
.gw-mobilenav__panel {
  position: absolute; inset-block: 0; right: 0;
  width: min(360px, 86vw);
  background: var(--gw-bg);
  border-left: 1px solid var(--gw-accent-line);
  box-shadow: -16px 0 48px rgba(0, 0, 0, 0.55);
  padding: var(--gw-gutter);
  display: flex; flex-direction: column; gap: 2rem;
  overflow-y: auto;
  animation: gw-slide-in 0.22s ease;
}
@keyframes gw-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
@media (prefers-reduced-motion: reduce) {
  .gw-mobilenav__panel { animation: none; }
  .gw-burger__bar { transition: none; }
}
.gw-mobilenav__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.gw-mobilenav__title {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.28em; text-transform: uppercase; color: var(--gw-accent);
}
.gw-mobilenav__close {
  background: transparent; border: 0; cursor: pointer; color: var(--gw-heading);
  padding: 6px; line-height: 0; border-radius: var(--r-sm, 5px);
}
.gw-mobilenav__close:hover { color: var(--gw-accent); }
.gw-mobilenav__nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.gw-mobilenav__nav li { border-bottom: 1px solid var(--gw-line); }
.gw-mobilenav__nav a {
  display: block; padding: 1rem 0;
  font-family: var(--gw-display); font-size: 1.75rem; line-height: 1.1;
  text-transform: uppercase; color: var(--gw-heading); text-decoration: none;
}
.gw-mobilenav__nav a:hover,
.gw-mobilenav__nav .current-menu-item > a { color: var(--gw-accent); }
.gw-mobilenav__cta { display: flex; flex-direction: column; gap: 0.75rem; margin-top: auto; }
.gw-mobilenav__cta .gw-btn,
.gw-mobilenav__cta .gw-btn-outline { justify-content: center; }

/* Show the hamburger exactly where the desktop nav disappears. */
@media (max-width: 1060px) {
  .gw-burger { display: block; }
}

/* ---------- Mobile sticky bar ---------- */
.gw-mobilebar { display: none; }

/* ---------- Responsive (breakpoints as per the designs) ---------- */
/* Desktop nav hides here; the hamburger above takes over at the same width. */
@media (max-width: 1060px) { .gw-nav { display: none; } }
@media (max-width: 900px)  { .gw-footer__cols { grid-template-columns: 1fr 1fr; } }
@media (max-width: 860px)  {
  .gw-faq { grid-template-columns: 1fr; }
  .gw-field--row { grid-template-columns: 1fr; }
  /* Explicit single column — the services grid sets fixed track weights rather
     than auto-fit, so nothing collapses it on its own. 860px rather than the
     768px block below: the two tracks need ~700px of content width, and any
     narrower squeezes both under their minimums. */
  .gw-services__grid { grid-template-columns: 1fr; }
}
@media (max-width: 800px)  {
  .gw-areas__grid { grid-template-columns: 1fr; gap: 0; }
  /* Stacked: back in normal flow at its natural height, under the text. */
  .gw-areas__map { padding-bottom: clamp(48px, 6vw, 72px); }
  .gw-areas__map img { position: static; height: auto; }
}
@media (max-width: 768px) {
  .gw-header__cta { display: none; }
  /* Narrow phones: cap the logo so the hamburger always has room. The <img>
     carries width="180" as an HTML attribute, so the container must be the
     thing that is constrained. */
  .gw-header__inner { gap: 12px; flex-wrap: nowrap; }
  .gw-logo { flex: 1 1 auto; min-width: 0; overflow: hidden; }
  .gw-logo img { height: 40px; width: auto; max-width: 100%; object-fit: contain; object-position: left center; }
  /* Never let the hamburger be squeezed out — it is the only nav on mobile. */
  .gw-burger { flex: 0 0 auto; margin-left: auto; }
  .gw-hero__h1 { max-width: none; }
  .gw-services__panel { position: static; }
  .gw-trust { gap: 16px; }
  .gw-trust__sep { display: none; }
  .gw-svclist__row { flex-wrap: wrap; }
  .gw-mobilebar {
    display: flex; position: fixed; inset-inline: 0; bottom: 0; z-index: 70;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.5);
  }
  /* Kept deliberately compact — it is fixed over the content on every scroll,
     so height here is height taken from the page. 48px clears the 44px minimum
     tap target with room to spare; line-height is reset because the body's 1.6
     was adding ~10px of dead space inside a single-line button. */
  .gw-mobilebar a {
    flex: 1; display: flex; align-items: center; justify-content: center;
    font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-meta);
    letter-spacing: 0.1em; text-transform: uppercase;
    line-height: 1; min-height: 48px; padding: 4px 8px 0;
  }
  .gw-mobilebar__call { background: var(--gw-ink); color: #fefefe; border-top: 1px solid rgba(255,255,255,.15); }
  .gw-mobilebar__quote { background: var(--gw-accent); color: var(--gw-ink); }
  /* Must match the bar's height or the last content sits behind it. */
  body { padding-bottom: 48px; }
}

/* ---------- Project photo gallery (single project) ---------- */
/* Three across on desktop so six photos read as two even rows — auto-fit gave
   four and left a ragged 2-item remainder. */
.gw-projshots {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gw-gutter); list-style: none; margin: 40px 0 0; padding: 0;
}
@media (max-width: 900px) { .gw-projshots { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .gw-projshots { grid-template-columns: 1fr; } }
.gw-projshots .gw-frame { border-radius: var(--r-md, 8px); }

/* ---------- Page head (project single — no hero image) ---------- */
.gw-pagehead {
  padding-block: 0 clamp(32px, 5vw, 64px);
  border-bottom: 1px solid var(--gw-accent-line);
}
/* .gw-crumbs is absolutely positioned so it can overlay a hero image. There is
   no hero here, so it has nothing to sit over — make it a normal block that
   leads the page head instead of landing under the sticky header. */
.gw-pagehead .gw-crumbs { position: static; }
.gw-pagehead .gw-crumbs ol { padding-block: 24px 0; }
/* Space the whole flag, not just the H1 — the gold bar is a sibling of the
   text, so a margin on the heading alone left the bar hard against the
   breadcrumbs while the title moved down. */
.gw-pagehead .gw-hero__flag { margin-top: 56px; }
/* Veneer carries a lot of internal leading, so the H1's box runs well past its
   glyphs: the gold bar looked mistimed against the letters and the meta line
   sat ~32px away despite an 8px margin. text-box trims the box to the actual
   cap height; the negative margins are the fallback for browsers without it. */
.gw-pagehead__h1 {
  font-size: clamp(38px, 5vw, 76px); line-height: 1.02;
  margin-block: -0.09em -0.12em;
}
@supports (text-box: trim-both cap alphabetic) {
  .gw-pagehead__h1 { text-box: trim-both cap alphabetic; margin-block: 0; }
}
.gw-pagehead__lead {
  font-size: var(--fs-meta); color: var(--gw-mid);
  margin: 8px 0 0; max-width: 62ch;
}

/* ---------- Project gallery: hover zoom affordance ---------- */
.gw-shot { display: block; position: relative; }
.gw-shot .gw-frame { border-radius: var(--r-md, 8px); }
.gw-shot img { transition: transform 0.4s ease; }
.gw-shot:hover img, .gw-shot:focus-visible img { transform: scale(1.04); }
/* Dim the photo and centre the plus on hover, so the whole tile reads as the
   click target rather than a small corner badge. The overlay sits on the frame
   (which clips) so it follows the rounded corners. */
.gw-shot .gw-frame::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(10, 10, 10, 0.55);
  opacity: 0; transition: opacity 0.25s ease;
  pointer-events: none;
}
.gw-shot:hover .gw-frame::after,
.gw-shot:focus-visible .gw-frame::after { opacity: 1; }

.gw-shot__zoom {
  position: absolute; top: 50%; left: 50%;
  display: grid; place-items: center; width: 52px; height: 52px;
  background: var(--gw-accent); color: var(--gw-ink);
  border-radius: 50%; opacity: 0;
  transform: translate(-50%, -50%) scale(0.85);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
.gw-shot:hover .gw-shot__zoom,
.gw-shot:focus-visible .gw-shot__zoom {
  opacity: 1; transform: translate(-50%, -50%) scale(1);
}

/* ---------- Lightbox ---------- */
.gw-lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: none; align-items: center; justify-content: center;
  gap: clamp(8px, 2vw, 28px);
  background: rgba(6, 6, 6, 0.94);
  padding: clamp(16px, 4vw, 48px);
}
.gw-lightbox.is-open { display: flex; }
.gw-lightbox__stage { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 14px; min-width: 0; }
.gw-lightbox__stage img {
  max-width: min(1100px, 88vw); max-height: 78vh;
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--r-md, 8px);
}
.gw-lightbox__count {
  font-family: var(--gw-label); font-weight: 600; font-size: var(--fs-small);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gw-dim);
}
.gw-lightbox__nav, .gw-lightbox__close {
  background: rgba(255, 255, 255, 0.08); color: var(--gw-heading);
  border: 1px solid var(--gw-line); cursor: pointer;
  display: grid; place-items: center; flex-shrink: 0;
  transition: background-color 0.18s ease, color 0.18s ease;
}
.gw-lightbox__nav { width: 52px; height: 52px; border-radius: 50%; font-size: 30px; line-height: 1; padding-bottom: 4px; }
.gw-lightbox__close { position: absolute; top: 20px; right: 20px; width: 44px; height: 44px; border-radius: 50%; font-size: 26px; line-height: 1; }
.gw-lightbox__nav:hover, .gw-lightbox__close:hover { background: var(--gw-accent); color: var(--gw-ink); }

@media (max-width: 700px) {
  /* Arrows move below the photo so they do not overlap it on a narrow screen. */
  .gw-lightbox { flex-wrap: wrap; }
  .gw-lightbox__stage { order: -1; flex-basis: 100%; }
  .gw-lightbox__nav { width: 46px; height: 46px; }
}

/* ---------- Footer contact column ---------- */
/* Referenced in parts/footer.php; the column needs no extra rules beyond the
   shared .gw-footer__col, but the modifier is kept as a styling hook. */
.gw-footer__col--contact { min-width: 0; }

/* ---------- Prose page: child-page nav (page.php) ---------- */
.gw-prose__pages {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: var(--gw-gutter); padding-top: var(--gw-gutter);
  border-top: 1px solid var(--gw-line); list-style: none;
}
.gw-prose__pages ul { display: flex; flex-wrap: wrap; gap: 12px; list-style: none; margin: 0; padding: 0; }


/* ==========================================================================
   Scroll reveal
   --------------------------------------------------------------------------
   Progressive enhancement in three layers.

   1. No JS: nothing here applies. Every hiding rule is nested under .gw-anim,
      a class the reveal IIFE puts on <html> only after it has confirmed
      IntersectionObserver exists AND tagging found something. If the script is
      blocked or throws before that line, the page renders exactly as it did
      before this block existed. That fails OPEN, unlike the common
      "remove .no-js" pattern which strands content invisible on any error.

   2. JS present: .gw-reveal starts hidden and offset; the observer adds .is-in.

   3. Reduced motion: forced back to visible. The global * nuke near the top of
      this file clamps transition-duration to 0.01ms, which does NOT undo an
      opacity:0 starting state — without the explicit override below, a
      reduced-motion visitor would get a permanently blank page.

   Nothing that reveals is ever a <section>. The brush-edge matcher in
   inc/template-helpers.php probes the pixel directly above each .gw-brushwrap
   with elementFromPoint(); a translated or faded section makes it sample the
   wrong element and paint the wrong colour. Sections keep their background and
   their position from first paint — only their inner content moves.
   ========================================================================== */

.gw-anim .gw-reveal {
  opacity: 0;
  /* translate3d, not translateY: promotes to a compositor layer so the
     transition never rasters on the main thread. */
  transform: translate3d(0, var(--gw-reveal-dist), 0);
  transition:
    opacity var(--gw-reveal-dur) var(--gw-reveal-ease) var(--gw-reveal-delay, 0ms),
    transform var(--gw-reveal-dur) var(--gw-reveal-ease) var(--gw-reveal-delay, 0ms);
}

.gw-anim .gw-reveal.is-in { opacity: 1; transform: none; }

/* Stagger. The observer writes only an integer index on each child; the delay
   is derived here so the timing stays with the other motion tokens instead of
   being computed in milliseconds by JS. */
.gw-anim .gw-reveal[style*="--gw-reveal-i"] {
  --gw-reveal-delay: calc(var(--gw-reveal-i, 0) * var(--gw-reveal-step));
}

/* Fade with no movement. For anything inside a sticky or clipped context,
   where a transform would break stickiness or get cut off. */
.gw-anim .gw-reveal--fade { transform: none; }

/* will-change is applied by JS on reveal and dropped on transitionend, so it
   never sits on every tagged element at once. */
.gw-anim .gw-reveal.is-arming { will-change: opacity, transform; }

/* The escape hatch. Duplicated selectors rather than relying on the global
   nuke, because that only shortens the transition — it leaves opacity:0 in
   place. This is the most load-bearing rule in the block. */
@media (prefers-reduced-motion: reduce) {
  .gw-anim .gw-reveal,
  .gw-anim .gw-reveal.is-in,
  .gw-anim .gw-reveal--fade {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    will-change: auto !important;
    --gw-reveal-delay: 0ms;
  }
}

/* Never send a half-faded page to paper or PDF. */
@media print {
  .gw-anim .gw-reveal { opacity: 1 !important; transform: none !important; }
}

/* ==========================================================================
   Micro-interactions
   --------------------------------------------------------------------------
   State changes on elements that mostly already carried a transition. Every
   rule here animates only compositor-friendly properties (opacity, transform)
   or cheap paint ones (colour, border-colour, box-shadow) — never width,
   height or top/left, which would force layout on hover.

   The global prefers-reduced-motion rule near the top of this file already
   clamps all of these to 0.01ms. They need no separate escape hatch because
   none of them has a hidden starting state: without the transition they simply
   apply instantly, which is the correct reduced-motion behaviour.
   ========================================================================== */

/* ---------- Header: solidify on scroll ---------- */
/* .is-scrolled is set by JS past 40px. Padding, logo size, background and
   border are transitioned; transform deliberately is NOT touched, because a
   transform on a position:sticky element fights its own offset. */
.gw-header {
  transition:
    background-color var(--gw-micro-dur) var(--gw-micro-ease),
    border-color var(--gw-micro-dur) var(--gw-micro-ease),
    box-shadow var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-header__inner {
  transition: padding var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-logo img {
  transition: height var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-header.is-scrolled {
  background: var(--gw-bg);
  border-bottom-color: var(--gw-accent);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
}
.gw-header.is-scrolled .gw-header__inner { padding-top: 10px; padding-bottom: 10px; }
.gw-header.is-scrolled .gw-logo img { height: 44px; }

/* ---------- Buttons: lift, and nudge the trailing arrow ---------- */
.gw-btn, .gw-btn-outline, .gw-btn-ink {
  transition:
    background-color var(--gw-micro-dur) var(--gw-micro-ease),
    color var(--gw-micro-dur) var(--gw-micro-ease),
    border-color var(--gw-micro-dur) var(--gw-micro-ease),
    transform var(--gw-micro-dur) var(--gw-micro-ease),
    box-shadow var(--gw-micro-dur) var(--gw-micro-ease);
}
/* Client feedback: the phone and estimate hovers read as "quite subtle". The
   estimate button now brightens AND lifts with a stronger glow; the outline
   phone button fills solid gold on hover so the state change is unmissable. */
.gw-btn:hover, .gw-btn:focus-visible {
  background: var(--gw-accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(240, 163, 26, 0.38);
}
.gw-btn-outline:hover, .gw-btn-outline:focus-visible {
  background: var(--gw-accent); color: var(--gw-ink); border-color: var(--gw-accent);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(240, 163, 26, 0.28);
}
.gw-btn-ink:hover, .gw-btn-ink:focus-visible { transform: translateY(-2px); }
.gw-btn:active, .gw-btn-outline:active, .gw-btn-ink:active {
  transform: translateY(0);
  box-shadow: none;
}
/* The trailing arrow icon slides forward. Only the last SVG moves, so an
   icon that leads the label is left alone. */
.gw-btn > svg:last-child,
.gw-btn-outline > svg:last-child,
.gw-inlinelink > svg:last-child {
  transition: transform var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-btn:hover > svg:last-child,
.gw-btn:focus-visible > svg:last-child,
.gw-btn-outline:hover > svg:last-child,
.gw-inlinelink:hover > svg:last-child,
.gw-inlinelink:focus-visible > svg:last-child { transform: translateX(3px); }

/* ---------- Call link: underline grows from the left ---------- */
/* The 3px gold rule is already there; scaling it keeps the same geometry, so
   nothing reflows. transform-origin makes it read as drawn, not resized. */
.gw-calllink {
  position: relative;
  border-bottom-color: transparent;
  transition: color var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-calllink::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px;
  border-bottom: 3px solid var(--gw-accent);
  transform: scaleX(1); transform-origin: left center;
  transition: transform var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-calllink:hover::after, .gw-calllink:focus-visible::after { transform: scaleX(1.06); }

/* ---------- Nav links: gold underline ---------- */
.gw-nav a, .gw-mega__trigger { position: relative; transition: color var(--gw-micro-dur) var(--gw-micro-ease); }
.gw-nav a::after, .gw-mega__trigger::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 2px;
  background: var(--gw-accent);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-nav a:hover::after, .gw-nav a:focus-visible::after,
.gw-mega__trigger:hover::after,
.gw-mega__trigger[aria-expanded="true"]::after { transform: scaleX(1); }
/* The mega trigger's chevron already rotates; leave that rule alone. */

/* ---------- Cards: warm the border and lift ---------- */
.gw-card, .gw-valuecard {
  transition:
    border-color var(--gw-micro-dur) var(--gw-micro-ease),
    transform var(--gw-micro-dur) var(--gw-micro-ease),
    box-shadow var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-card:hover, .gw-valuecard:hover {
  border-color: var(--gw-accent-line-strong);
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
}
/* Project cards are bare links, so the frame carries the treatment. */
.gw-projgrid a .gw-frame,
.gw-grid > a .gw-frame {
  transition: box-shadow var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-projgrid a:hover .gw-frame,
.gw-grid > a:hover .gw-frame { box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4); }
.gw-projgrid a .gw-projgrid__name { transition: color var(--gw-micro-dur) var(--gw-micro-ease); }
.gw-projgrid a:hover .gw-projgrid__name,
.gw-projgrid a:focus-visible .gw-projgrid__name { color: var(--gw-accent); }
/* The image zoom on .gw-projgrid matches the one .gw-grid already has. */
.gw-projgrid a img { transition: transform 0.4s ease; }
.gw-projgrid a:hover img { transform: scale(1.04); }

/* ---------- Service rows ---------- */
.gw-svclist__row {
  transition:
    background-color var(--gw-micro-dur) var(--gw-micro-ease),
    border-color var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-svclist__row:hover { background: var(--gw-accent-tint); }
.gw-svclist__arrow { transition: transform var(--gw-micro-dur) var(--gw-micro-ease); }
.gw-svclist__row:hover .gw-svclist__arrow { transform: translateX(4px); }

/* ---------- Form chips and filter tags ---------- */
.gw-chip {
  transition:
    border-color var(--gw-micro-dur) var(--gw-micro-ease),
    background-color var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-chip:hover { background: var(--gw-accent-tint); }
.gw-taglist .gw-tag {
  transition:
    background-color var(--gw-micro-dur) var(--gw-micro-ease),
    border-color var(--gw-micro-dur) var(--gw-micro-ease),
    color var(--gw-micro-dur) var(--gw-micro-ease);
}

/* ---------- Mega panel: fade and settle ---------- */
/* The panel keeps its [hidden] attribute for the accessibility contract; JS
   adds .is-open one frame later so there is a state to transition from. */
/* The panel is full-bleed via left:50% + translateX(-50%), so the entry
   animation MUST compose with that centering rather than replace it — a bare
   translateY here silently drops the -50% and the panel stops being centred.
   Both offsets are kept in one transform, and the vertical part is a variable
   so only it changes between states. */
.gw-mega__panel {
  opacity: 0;
  --gw-mega-y: -6px;
  transform: translateX(-50%) translateY(var(--gw-mega-y));
  transition:
    opacity var(--gw-micro-dur) var(--gw-micro-ease),
    transform var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-mega__panel.is-open { opacity: 1; --gw-mega-y: 0px; }
@media (prefers-reduced-motion: reduce) {
  /* Reset opacity only. Never `transform: none` here — that would wipe the
     translateX(-50%) the layout depends on. */
  .gw-mega__panel {
    opacity: 1 !important;
    --gw-mega-y: 0px;
    transition: none !important;
  }
}

/* ---------- Stat count-up ---------- */
/* Tabular figures so the number does not jitter while it counts. */
.gw-stat__value { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Hero: load-in
   --------------------------------------------------------------------------
   A load animation, NOT a scroll reveal — the hero is above the fold, so there
   is nothing to scroll to. It stays excluded from the .gw-reveal system.

   What is deliberately NOT animated:
     - .gw-hero__media / the <img>. It is the LCP element; fading it in makes
       LCP land a whole transition later than the pixel actually painted.
     - .gw-hero__h1. Also an LCP candidate, and the text is the reason the page
       exists — it must be readable on the first frame.

   What IS animated is only chrome that carries no layout: the gold bar wipes
   down via scaleY (a 6px-wide flex item — no reflow), and the actions and
   badges lift very slightly. Everything uses transform/opacity only, so no
   layout is computed and CLS stays at zero.

   .gw-hero-in is set by JS on <html>, mirroring the .gw-anim pattern: without
   it the hero paints finished, so a blocked script cannot hide the first
   screen.
   ========================================================================== */

.gw-hero-in .gw-hero__bar,
.gw-hero-in .gw-pagehead .gw-hero__bar {
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 760ms var(--gw-reveal-ease) 80ms;
}
.gw-hero-in.gw-hero-ready .gw-hero__bar,
.gw-hero-in.gw-hero-ready .gw-pagehead .gw-hero__bar { transform: scaleY(1); }

/* The headline, revealed one visual line at a time.
 *
 * Each line FADES DOWN: it starts slightly above its resting position and
 * slightly transparent, then settles into place. No clipping mask — the line
 * simply moves and fades, so a single span per line is enough.
 *
 * The distance is small on purpose (0.22em, a fifth of the cap height). On a
 * display face this large a bigger travel reads as a bounce rather than a
 * settle, and the point is weight, not motion.
 *
 * transform + opacity only, so no layout is computed and CLS stays at zero.
 * The h1's own box never moves, which is what keeps the largest-contentful-
 * paint measurement honest.
 */
/* The per-word span is a measurement device, not a style hook: it must not
   change how the text lays out or the line grouping would measure the wrong
   geometry. inline (the default) keeps wrapping identical to the unsplit
   headline; offsetTop still reports the line box correctly. */
.gw-heroword { display: inline; }

.gw-heroline {
  display: inline-block;
  opacity: 0;
  transform: translateY(-0.22em);
  transition:
    opacity 620ms var(--gw-reveal-ease) var(--gw-heroline-delay, 0ms),
    transform 700ms var(--gw-reveal-ease) var(--gw-heroline-delay, 0ms);
}
.gw-hero-ready .gw-heroline { opacity: 1; transform: none; }

/* Fallback for the split never happening (JS bailed, or a single-word h1):
   the headline fades down as one block rather than staying hidden. */
.gw-hero-in .gw-hero__h1:not(:has(.gw-heroline)) {
  opacity: 0;
  transform: translateY(-0.18em);
  transition:
    opacity 700ms var(--gw-reveal-ease) 60ms,
    transform 780ms var(--gw-reveal-ease) 60ms;
}
.gw-hero-in.gw-hero-ready .gw-hero__h1:not(:has(.gw-heroline)) {
  opacity: 1; transform: none;
}

.gw-hero-in .gw-hero__actions,
.gw-hero-in .gw-hero__badges {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition:
    opacity var(--gw-reveal-dur) var(--gw-reveal-ease),
    transform var(--gw-reveal-dur) var(--gw-reveal-ease);
}
/* The badges trail the buttons slightly so the eye lands on the CTA first. */
.gw-hero-in .gw-hero__badges { transition-delay: 120ms; }
.gw-hero-in.gw-hero-ready .gw-hero__actions,
.gw-hero-in.gw-hero-ready .gw-hero__badges { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* Same trap as the reveal block: the global clamp shortens the transition
     but leaves scaleY(0) and opacity:0 in place. */
  .gw-hero-in .gw-hero__bar,
  .gw-hero-in .gw-hero__actions,
  .gw-hero-in .gw-hero__badges {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .gw-hero-in .gw-hero__h1,
  .gw-heroline {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}


/* ---------- CTA button pair: equal heights ---------- */
/* .gw-btn-ink runs at 23px and .gw-btn-outline at 21px, so their line boxes
   differ and no amount of padding tuning makes the two the same height —
   trimming the ink button's padding narrowed the gap to ~3px but could not
   close it.
   
   align-items:stretch makes both take the height of the taller one. Each
   button is already inline-flex with centred content, so the shorter one just
   gains symmetric breathing room rather than shifting its label. Applied only
   to .gw-cta__actions, where the two sit side by side. */
.gw-cta__actions { align-items: stretch; }


/* ---------- Nav: mega parent is also a real link ---------- */
/* The trigger stays a <button> for the dropdown, but the label beside it is an
   <a> to the hub, so the parent is reachable (client feedback) and still
   crawlable. The two sit flush so they read as one item. */
/* When the parent is a real link, the head holds two siblings — the <a> and a
   separate chevron button. The underline therefore belongs to the head, not to
   the <a>: drawn on the link it stopped at the end of the text and left the
   chevron sitting above a rule that did not reach it. */
.gw-mega__head { display: inline-flex; align-items: center; gap: 4px; position: relative; }
.gw-mega__head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 2px;
  background: var(--gw-accent);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--gw-micro-dur) var(--gw-micro-ease);
}
.gw-mega__head:hover::after,
.gw-mega__head:focus-within::after,
.gw-mega__head:has(.gw-mega__trigger[aria-expanded="true"])::after { transform: scaleX(1); }

/* The link's own underline is suppressed here so the two do not stack — the
   generic .gw-mega__trigger rule above still serves the non-linking parents,
   where the trigger IS the whole label and spans the full width already. */
.gw-mega__head > a::after,
.gw-mega__head .gw-mega__trigger::after { content: none; }

.gw-mega__head > a {
  color: var(--gw-heading); font-family: var(--gw-label); font-weight: 600;
  font-size: var(--fs-body); letter-spacing: var(--tr-nav); text-transform: uppercase;
  text-decoration: none; padding: 0;
}
.gw-mega__head > a:hover { color: var(--gw-accent); }
.gw-mega__head .gw-mega__trigger { padding: 0 2px; }
.gw-mega__head .gw-mega__trigger .gw-sr-only { position: absolute; }

/* ---------- Mobile nav: accordion parents ---------- */
/* Mobile renders the same items as desktop. Parents with children are a row of
   [link][toggle]; the toggle expands a nested list. Parents marked as non-linking
   (Service Areas, per the architecture sheet) render the label as a button. */
.gw-mobilenav__nav li.has-children { border-bottom: 1px solid var(--gw-line); }
.gw-mobilenav__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gw-mobilenav__row > a, .gw-mobilenav__row > .gw-mobilenav__label { flex: 1; border-bottom: 0; }
.gw-mobilenav__label {
  display: block; padding: 1rem 0; text-align: left; background: none; border: 0;
  font-family: var(--gw-display); font-size: 1.75rem; line-height: 1.1;
  text-transform: uppercase; color: var(--gw-heading); cursor: pointer;
}
.gw-mobilenav__toggle {
  flex: 0 0 48px; height: 48px; display: grid; place-items: center;
  background: none; border: 0; color: var(--gw-accent); cursor: pointer;
}
.gw-mobilenav__toggle svg { transition: transform 0.18s ease; }
.gw-mobilenav__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.gw-mobilenav__sub { list-style: none; margin: 0 0 0.5rem; padding: 0 0 0 1rem; }
.gw-mobilenav__sub[hidden] { display: none; }
.gw-mobilenav__sub li { border-bottom: 0; }
.gw-mobilenav__sub a { font-size: 1.15rem; padding: 0.6rem 0; font-family: var(--gw-label); letter-spacing: 0.04em; }

/* ---------- Hub: intro two-column with photo ---------- */
.gw-hubintro {
  display: grid; grid-template-columns: minmax(340px, 1.25fr) minmax(300px, 1fr);
  gap: clamp(32px, 5vw, 64px); align-items: center;
}
.gw-hubintro p { color: var(--gw-mid); font-size: 18px; line-height: 1.65; }
.gw-hubintro .gw-frame { border-radius: var(--r-md, 8px); }
@media (max-width: 860px) { .gw-hubintro { grid-template-columns: 1fr; } }

/* ---------- Materials intro line (hub) ---------- */
.gw-matgrid__intro { color: var(--gw-mid); margin: 0 0 8px; max-width: 60ch; }
.gw-areas__intro { color: var(--gw-mid); margin: 8px 0 28px; max-width: 60ch; }

/* Elfsight reviews widget slot — reserve height so the lazy app loading in
   does not push the services section down (CLS). */
.gw-reviews__widget { min-height: 260px; }

/* ---------- QA round (Oct 2026): centred callouts ---------- */
/* CTA bands: heading, sub line and buttons stacked and centred, sitewide —
   the split left-heading / right-buttons layout read as off-centre. */
.gw-cta__inner { flex-direction: column; justify-content: center; text-align: center; gap: 24px; }
.gw-cta__text { align-items: center; }
.gw-cta h2, .gw-cta__sub { margin-inline: auto; }
.gw-cta__actions { justify-content: center; }
/* Why Goodwell cards: checkmarks removed from the markup; text left-aligned
   and top-aligned so the three titles sit on one line across the row. */
.gw-valuecard { flex-direction: column; align-items: flex-start; justify-content: flex-start; text-align: left; }
/* Who We Are single photo (replaces the before/after pair). */
.gw-whoweare__photo { margin: 0; border-radius: 12px; align-self: center; }
/* Hub colour-consultation callout: centred, gold-ruled, between the value
   cards and the service grid. */
.gw-colourcall { background: var(--gw-reviews-bg); --gw-section-bg: var(--gw-reviews-bg); border-top: 1px solid var(--gw-accent-line); border-bottom: 1px solid var(--gw-accent-line); }
.gw-colourcall__inner { max-width: 760px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.gw-colourcall__inner p { margin: 0; color: var(--gw-mid); font-size: var(--fs-body); line-height: 1.7; }
/* Buttons one step bolder. Bebas Neue ships a single weight, and a synthetic
   bold smears it, so the strokes are thickened with a hairline stroke in the
   text's own colour instead — crisp at every size. */
.gw-btn, .gw-btn-outline, .gw-btn-ink, .gw-mobilebar a { -webkit-text-stroke: 0.45px currentColor; }
/* All headings one step bolder, same technique as the buttons. Sized in em so
   it scales with each heading's clamp()ed size, and set on descendants too so
   gold accent words take a gold stroke rather than inheriting the white. */
h1, h1 *, h2, h2 *, h3, h3 * { -webkit-text-stroke: 0.014em currentColor; }

/* ---------- QA round (Oct 2026): final feedback ---------- */
/* Accent run inside body copy (gw_rich): bold and brand gold. */
p > strong.is-accent { color: var(--gw-accent); font-weight: 600; }

/* "Change all of the paint accent lines to be straight lines." Every torn
   brush edge is hidden — CTA bands, hero bottoms, the homepage section tears —
   and the sections meet on a straight edge. Markup and the colour-matching
   JS stay in place so the tears can be switched back on by deleting this. */
.gw-brush, .gw-brushwrap { display: none !important; }

/* ---------- Form validation (gw inline script) ---------- */
.gw-field.is-invalid input, .gw-field.is-invalid select, .gw-field.is-invalid textarea {
  border-color: #e0574a; box-shadow: 0 0 0 3px rgba(224, 87, 74, 0.18);
}
.gw-field__error { display: block; margin-top: 6px; font-family: var(--gw-body); font-size: 13px; letter-spacing: normal; text-transform: none; color: #f08a80; }

/* ---------- Nav + mega menu, larger (Oct 2026 QA) ---------- */
/* Top-level nav items step up from 17px to 19px; the header buttons sit at
   21px, so the menu no longer reads a size smaller than the CTAs beside it. */
.gw-nav a, .gw-mega__trigger, .gw-mega__head > a { font-size: 19px; }
.gw-mega__trigger svg { width: 16px; height: 16px; }

/* Panel entries: bigger thumbnail, bigger title, full blurb clamped to two
   lines instead of a word-count cut that ended mid-sentence. */
.gw-mega__list a { gap: 20px; padding: 16px 0; }
.gw-mega__list .gw-frame--thumb { width: 148px; height: 100px; }
.gw-mega__name { font-size: 22px; letter-spacing: 0.05em; }
.gw-mega__blurb {
  font-size: var(--fs-meta); line-height: 1.5; margin-top: 6px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* With three or fewer entries (the launch set: 3 services, 3 cities) the
   two-column down-then-across grid strands one item on its own in column two.
   Lay those panels out as one row of equal cards instead. */
.gw-mega__list--row ul {
  grid-template-rows: none; grid-auto-flow: row;
  grid-template-columns: repeat(var(--gw-mega-count, 3), minmax(0, 1fr));
  gap: 0 40px;
}
