/* True Blue Tree Lopping -- refined-tradie design system (rebrand 2026-07-24)
   Tokens documented in DESIGN.md + content.md. Palette: logo green + lime accent, blue retired.
   Fonts: Anton (display, 400 only) + Instrument Sans (body, 400/600).
   Mobile-web-build-standard applies in full. Keep this file under 50KB / 350 rules (IG in-app cap). Split if it grows past that. */

:root {
  --green: #02793D;
  --deep: #063D22;
  --ink: #0A2015;
  --lime: #C8DC28;
  --paper: #F7F8F4;
  --surface-2: #ffffff;
  --text: #0A2015;
  --muted: #4A5A50;
  --muted-dark: #B9C9BE;
  --error: #C0392B;
  --border: #E2E0D2;
  --radius: 12px;
  --radius-sm: 6px;
  --shadow: 0 12px 32px rgba(10, 32, 21, 0.14);
  --shadow-sm: 0 4px 12px rgba(10, 32, 21, 0.10);
  --maxw: 1160px;
  --font-display: 'Anton', 'Arial Narrow', sans-serif;
  --font-body: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--font-body);
  color: var(--text);
  background: var(--paper);
  line-height: 1.65;
  -webkit-tap-highlight-color: transparent;
  overflow-x: hidden;
  overflow-wrap: break-word;
  word-break: break-word;
}

img, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
button { font-family: inherit; cursor: pointer; }
input, textarea, select {
  font-family: inherit;
  font-size: 16px;
}

/* F-404: focus-visible rings site-wide, never the browser-default blue.
   Base (paper/white/light surfaces): green 2px ring. Dark-surface ancestors
   (hero, nav, mobile menu, sticky CTA bar, footer, deep-band CTA, stats band)
   switch the ring to lime so it reads against ink backgrounds. :focus alone
   (mouse/pointer activation) never draws a ring -- only :focus-visible does. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
  border-radius: 4px;
}
.hero :focus-visible,
.hero-compact :focus-visible,
.nav :focus-visible,
.mobile-menu :focus-visible,
.footer :focus-visible,
.cta-band :focus-visible,
.stats-band :focus-visible {
  outline-color: var(--lime);
}

h1, h2 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: 0.015em;
  text-transform: uppercase;
}
h3, h4 { font-family: var(--font-body); font-weight: 600; line-height: 1.25; }
/* Polish-5: floor lowered from 2.6rem so narrow phones (320-430) don't pin to
   a fixed 41.6px and stack the home H1 into 7 lines -- 1.75rem stays above the
   24px-rendered-minimum DESIGN.md sets for Anton. Anything >=400px viewport was
   already governed by the 7vw preferred value, so this is a small-phone-only change. */
h1 { font-size: clamp(1.75rem, 7vw, 5rem); }
h2 { font-size: clamp(1.9rem, 4.5vw, 3rem); }
h3 { font-size: clamp(1.2rem, 3vw, 1.5rem); }
p { color: var(--muted); }

/* Polish: scroll-margin-top on every anchor target site-wide (nav height 72px + 16px)
   so a jump-to-section never lands a heading under the fixed nav. */
[id] { scroll-margin-top: 88px; }

.container { max-width: var(--maxw); margin: 0 auto; padding: 0 20px; }
.section { padding: clamp(4rem, 9vw, 7.5rem) 0; }
.section-tight { padding: 48px 0; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--green); margin-bottom: 14px;
}
.eyebrow::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--lime); display: inline-block; }
.section-head { max-width: 640px; margin-bottom: 40px; }
.section-head p { margin-top: 12px; font-size: 1.05rem; }
.center { text-align: center; margin-left: auto; margin-right: auto; }

/* Progress bar -- green, scroll-triggered, reduced-motion gated */
#scroll-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--green); z-index: 999;
}
@media (prefers-reduced-motion: no-preference) {
  #scroll-progress { transition: width 0.1s linear; }
}
@media (prefers-reduced-motion: reduce) {
  #scroll-progress { display: none; }
}

/* Nav */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 500;
  background: rgba(10, 32, 21, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.nav-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 0 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  height: 72px;
}
/* F-503: min-width:0 so these two flex items are allowed to shrink instead of
   forcing nav-inner wider than the viewport and pushing the burger off-screen. */
.nav-logo { display: flex; align-items: center; gap: 10px; font-family: var(--font-body); font-weight: 600; text-transform: uppercase; letter-spacing: 0.01em; color: #fff; font-size: 1.1rem; flex-shrink: 0; min-width: 0; min-height: 44px; }
.nav-logo img { height: 34px; width: auto; flex-shrink: 0; }
.nav-logo-text { white-space: nowrap; }
.nav-links { display: none; gap: 18px; align-items: center; flex-wrap: nowrap; }
.nav-links a { color: rgba(255,255,255,0.82); font-size: 0.85rem; font-weight: 400; padding: 10px 0; white-space: nowrap; }
.nav-links a:hover, .nav-links a.active { color: var(--lime); }
.nav-cta {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0; min-width: 0;
}
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 12px 22px;
  border-radius: 999px; font-weight: 600; font-size: 0.95rem;
  border: 1px solid transparent; transition: box-shadow 0.15s ease, background 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
}
.btn-primary { background: var(--lime); color: var(--ink); box-shadow: var(--shadow-sm); }
.btn-primary:hover { box-shadow: var(--shadow); }
.btn-outline { background: transparent; border-color: rgba(255,255,255,0.35); color: #fff; }
.btn-outline:hover { background: rgba(255,255,255,0.1); }
.btn-dark { background: var(--ink); color: #fff; }
.btn-dark:hover { box-shadow: var(--shadow); }
.btn-block { width: 100%; }
/* button lime sweep on hover -- transform only, reduced-motion gated */
.btn::after {
  content: ''; position: absolute; inset: 0; left: -120%;
  background: rgba(255,255,255,0.22); width: 60%; transform: skewX(-18deg);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .btn { transition: box-shadow 0.15s ease, background 0.15s ease, border-color 0.15s ease, transform 0.15s ease; }
  .btn-primary:hover, .btn-dark:hover { transform: translateY(-2px); }
  .btn::after { transition: left 0.5s var(--ease); }
  .btn:hover::after { left: 130%; }
}
.nav-burger {
  display: flex; flex-direction: column; gap: 5px; width: 44px; height: 44px;
  align-items: center; justify-content: center; background: none; border: none;
}
.nav-burger span { width: 22px; height: 2px; background: #fff; display: block; border-radius: 2px; }
/* F-504: visibility:hidden (not just translateX) so the closed menu's 10 links
   drop out of the tab order. Transitioned so the slide-out still finishes
   animating before the links disappear: on close, visibility flips to hidden
   only after the 0.35s delay (past the 0.25s transform); on open it flips to
   visible instantly so the links are interactive the moment the panel appears. */
.mobile-menu {
  position: fixed; inset: 72px 0 0 0; background: var(--ink); z-index: 490;
  transform: translateX(100%); padding: 24px 20px; overflow-y: auto;
  visibility: hidden;
}
@media (prefers-reduced-motion: no-preference) {
  .mobile-menu { transition: transform 0.25s var(--ease), visibility 0s linear 0.35s; }
  .mobile-menu.open { transition: transform 0.25s var(--ease), visibility 0s linear 0s; }
}
.mobile-menu.open { transform: translateX(0); visibility: visible; }
.mobile-menu a {
  display: block; padding: 16px 4px; font-size: 1.15rem; color: #fff;
  border-bottom: 1px solid rgba(255,255,255,0.08); min-height: 44px;
}
.mobile-menu a:hover { color: var(--lime); }
.mobile-menu .btn { margin-top: 20px; }

/* Services dropdown (desktop >=1120px nav) -- hover, focus (JS-driven open),
   click/tap toggle for touch laptops. Closed panel stays visibility:hidden so
   its links drop out of tab order (F-504 pattern reused). */
.nav-dropdown { position: relative; }
.nav-dropdown-trigger {
  background: none; border: none; font-family: inherit; color: rgba(255,255,255,0.82);
  font-size: 0.85rem; font-weight: 400; padding: 10px 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
}
.nav-dropdown-trigger:hover,
.nav-dropdown-trigger.active,
.nav-dropdown.open .nav-dropdown-trigger { color: var(--lime); }
.nav-dropdown-trigger::after {
  content: ''; width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); margin-top: -4px; flex-shrink: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .nav-dropdown-trigger::after { transition: transform 0.15s ease; }
  .nav-dropdown.open .nav-dropdown-trigger::after,
  .nav-dropdown:hover .nav-dropdown-trigger::after { transform: rotate(-135deg); margin-top: 3px; }
}
.nav-dropdown-panel {
  position: absolute; top: 100%; left: 50%; margin-top: 12px;
  transform: translateX(-50%) translateY(-6px);
  min-width: 240px; max-width: calc(100vw - 40px);
  background: var(--ink); border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: 8px;
  visibility: hidden; opacity: 0; pointer-events: none;
}
.nav-dropdown:hover .nav-dropdown-panel,
.nav-dropdown.open .nav-dropdown-panel {
  visibility: visible; opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: no-preference) {
  .nav-dropdown-panel { transition: opacity 0.15s ease, transform 0.15s var(--ease), visibility 0s linear 0.15s; }
  .nav-dropdown:hover .nav-dropdown-panel,
  .nav-dropdown.open .nav-dropdown-panel { transition: opacity 0.15s ease, transform 0.15s var(--ease), visibility 0s linear 0s; }
}
.nav-dropdown-panel a {
  display: flex; align-items: center; min-height: 44px; padding: 11px 14px; border-radius: var(--radius-sm);
  color: rgba(255,255,255,0.82); font-size: 0.9rem; white-space: nowrap; line-height: 1.2;
}
.nav-dropdown-panel a:hover,
.nav-dropdown-panel a.active { color: var(--lime); background: rgba(255,255,255,0.06); }

/* Mobile menu services accordion */
.mobile-accordion-toggle {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  background: none; border: none; font-family: inherit; color: #fff; text-align: left;
  padding: 16px 4px; font-size: 1.15rem; border-bottom: 1px solid rgba(255,255,255,0.08);
  min-height: 44px; cursor: pointer;
}
.mobile-accordion-toggle::after {
  content: ''; width: 9px; height: 9px; border-right: 2px solid #fff; border-bottom: 2px solid #fff;
  transform: rotate(45deg); flex-shrink: 0; margin-left: 12px;
}
@media (prefers-reduced-motion: no-preference) {
  .mobile-accordion-toggle::after { transition: transform 0.15s ease; }
}
.mobile-accordion-toggle[aria-expanded="true"]::after { transform: rotate(-135deg); }
.mobile-accordion-panel { display: none; padding-left: 12px; }
.mobile-accordion-panel.open { display: block; }

/* Floating call widget -- desktop only (>=1120px); mobile already has the sticky call bar
   (a later pass replaces the mobile sticky bar with its own floating pill -- the
   .call-fab-* pieces below are the shared, breakpoint-agnostic building blocks so
   that pass can reuse them inside its own wrapper instead of duplicating styles;
   .call-widget itself only supplies the desktop breakpoint gate + fixed position). */
.call-widget { display: none; }
@media (min-width: 1120px) {
  .call-widget {
    display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
    position: fixed; right: 24px; bottom: 24px; z-index: 470;
  }
  .call-widget.widget-hide { opacity: 0; pointer-events: none; }
  @media (prefers-reduced-motion: no-preference) {
    .call-widget { transition: opacity 0.2s ease; }
  }
}
.call-fab-trigger {
  width: 56px; height: 56px; border-radius: 50%; background: var(--lime); color: var(--ink);
  display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow);
  flex-shrink: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .call-fab-trigger { animation: call-pulse 1.8s var(--ease) 1s 3; }
}
@keyframes call-pulse {
  0%, 100% { box-shadow: var(--shadow), 0 0 0 0 rgba(200,220,40,0.5); }
  50% { box-shadow: var(--shadow), 0 0 0 10px rgba(200,220,40,0); }
}
.call-fab-actions {
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
  visibility: hidden; opacity: 0; transform: translateY(6px); pointer-events: none;
}
.call-fab-group.open .call-fab-actions,
.call-fab-group:hover .call-fab-actions,
.call-fab-group:focus-within .call-fab-actions {
  visibility: visible; opacity: 1; transform: translateY(0); pointer-events: auto;
}
@media (prefers-reduced-motion: no-preference) {
  .call-fab-actions { transition: opacity 0.15s ease, transform 0.15s var(--ease), visibility 0s linear 0.15s; }
  .call-fab-group.open .call-fab-actions,
  .call-fab-group:hover .call-fab-actions,
  .call-fab-group:focus-within .call-fab-actions { transition: opacity 0.15s ease, transform 0.15s var(--ease), visibility 0s linear 0s; }
}
.call-fab-action {
  display: inline-flex; align-items: center; min-height: 44px; padding: 10px 18px;
  border-radius: 999px; background: var(--ink); color: #fff; font-size: 0.88rem; font-weight: 600;
  white-space: nowrap; box-shadow: var(--shadow-sm);
}
.call-fab-action:hover { color: var(--lime); }


/* Hero */
.hero {
  position: relative; min-height: 100dvh; display: flex; align-items: center;
  padding-top: 100px; padding-bottom: 60px;
  background: linear-gradient(180deg, var(--ink) 0%, #10281a 100%);
  color: #fff; overflow: hidden;
}
.hero-bg {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  opacity: 0.55; transform: scale(1);
}
/* Ken Burns stays on the static-image layer only, never the video (no double
   motion). transform:scale is compositor-only (no paint), and under
   no-preference the hero-video sits opaquely on top of .hero-bg anyway, so
   this keeps animating harmlessly behind it rather than adding video-specific
   gating complexity. Reduced-motion already turns this off entirely below. */
@media (prefers-reduced-motion: no-preference) {
  html.js .hero-bg { animation: kenburns 14s ease-in-out infinite alternate; }
}
@keyframes kenburns { from { transform: scale(1); } to { transform: scale(1.06); } }
/* Hero video -- AI-generated ambient arborist-working loop (SOURCES.md).
   Same position/size/opacity treatment as .hero-bg so it sits in the identical
   spot in the overlay stack (bg -> video -> glow -> dark gradient -> Australia
   outline -> content). Mobile src ships in the HTML by default; main.js
   upgrades to the desktop file on wide/no-reduced-motion/non-save-data
   viewports and strips the src under reduced-motion so nothing downloads. */
.hero-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0.55;
}
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
}
.hero-glow {
  position: absolute; inset: 0;
  background: radial-gradient(60% 50% at 70% 30%, rgba(2,121,61,0.45), transparent 70%);
  pointer-events: none;
}
.hero::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10,32,21,0.35) 0%, rgba(10,32,21,0.85) 100%);
}
/* Fix 3b/3c (2026-07-24, Robin: "look at the background, this is horrendous"):
   the desktop overlay stack (bg/video opacity + green glow + dark gradient)
   reads fine at 1440 but triple-darkens the new portrait mobile video into a
   murky, detail-less green-black smear. Lighten the mobile-only stack: bg/video
   opacity raised so the scene actually reads, glow halved, gradient range
   narrowed. Contrast headroom checked empirically (qa/gen/overlay-tune.mjs,
   text hidden and swatch sampled against the composited background): even at
   this lighter setting, white H1/sub text against the brightest sky frame
   samples well past the 4.5:1 AA floor for body text, so there is room to
   lighten without risking legibility. .hero-bg carries the same rule, so the
   reduced-motion/no-JS static fallback gets the identical treatment. */
@media (max-width: 767px) {
  .hero-bg, .hero-video { opacity: 0.68; }
  .hero-glow { background: radial-gradient(60% 50% at 70% 30%, rgba(2,121,61,0.22), transparent 70%); }
  .hero::after { background: linear-gradient(180deg, rgba(10,32,21,0.50) 0%, rgba(10,32,21,0.68) 100%); }
}
.hero-inner { position: relative; z-index: 2; max-width: var(--maxw); margin: 0 auto; padding: 0 20px; width: 100%; }
.hero-badge {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px;
  border-radius: 999px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.18);
  font-size: 0.85rem; font-weight: 600; margin-bottom: 22px;
}
/* F-602: .badge-text is the ONE flex item inside .hero-badge (gap stays
   reserved for a future icon flex item, never for the text itself). Text and
   .badge-extra live inside it as plain inline content so the badge always
   reads as one flowing line instead of splitting into two flex columns. */
.badge-text { display: inline; }
.badge-extra { display: inline; }
.hero h1 { color: #fff; max-width: 820px; }
.hero-sub { color: rgba(255,255,255,.88); font-size: 1.15rem; max-width: 620px; margin-top: 20px; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 32px; }
.hero-trust { display: flex; flex-wrap: wrap; gap: 24px; margin-top: 48px; color: rgba(255,255,255,0.7); font-size: 0.88rem; }
.hero-trust strong { color: var(--lime); display: block; font-family: var(--font-body); font-weight: 600; text-transform: uppercase; font-size: 1.4rem; letter-spacing: 0.01em; }

/* Australia-outline SVG motif -- decorative, real simplified coastline
   (F-405: built from Natural Earth 50m admin-0 boundary data, mainland +
   Tasmania as separate paths, ~109 points total), never the logo file itself.
   vector-effect: non-scaling-stroke means the stroke-width below is always
   that many CSS px on screen regardless of how much the 640x520 viewBox is
   scaled down to fit -- this is what guarantees the >=3px effective weight
   DESIGN.md requires at every rendered size, including the smallest (divider
   at 320px viewport). Lime on dark surfaces (hero/footer), green on paper
   (divider) per the amended DESIGN.md contrast rule. */
.au-outline { display: block; overflow: visible; }
.au-outline path {
  fill: none; stroke: var(--lime); stroke-linejoin: round; stroke-linecap: round;
  stroke-dashoffset: 0; vector-effect: non-scaling-stroke;
}
.au-outline .au-mainland { stroke-width: 3.5px; }
.au-outline .au-tas { stroke-width: 3px; }
.hero-au { position: absolute; right: -40px; bottom: -30px; width: clamp(220px, 30vw, 420px); opacity: 0.6; z-index: 1; pointer-events: none; }
/* Fix 3a (2026-07-24, Robin: "why is the Australian image at the top of the
   mobile view"): the outline was competing with the hero badge/copy at phone
   widths no matter which corner it anchored to. Below 768px it is hidden
   entirely -- the hero video/image now carries the visual weight on mobile.
   Desktop (>=768px) keeps the original bottom-right placement above. */
@media (max-width: 767px) {
  .hero-au { display: none; }
}
/* Measured via path.getTotalLength() against the real path (qa/gen/build_australia_path.py
   + qa/gen/au-render.html): mainland 2175.6, Tasmania 185.8. Dasharray/offset
   rounded up so the full outline always completes the draw (fixes N-408). */
@media (prefers-reduced-motion: no-preference) {
  html.js .hero-au .au-mainland,
  html.js .hero-au .au-tas { animation: au-draw 2.2s var(--ease) 0.3s forwards; }
  html.js .hero-au .au-mainland { stroke-dasharray: 2180; stroke-dashoffset: 2180; }
  html.js .hero-au .au-tas { stroke-dasharray: 190; stroke-dashoffset: 190; }
}
@keyframes au-draw { to { stroke-dashoffset: 0; } }
/* F-403: bumped from a 220px hairline to a real signature size + weight so
   it registers as the section-divider motif DESIGN.md calls for. */
.divider-au { width: clamp(240px, 28vw, 400px); opacity: 1; margin: 0 auto 32px; }
.divider-au path { stroke: var(--green); }
.divider-au .au-mainland { stroke-width: 5px; }
.divider-au .au-tas { stroke-width: 4px; }
@media (prefers-reduced-motion: no-preference) {
  html.js .reveal .divider-au .au-mainland,
  html.js .reveal .divider-au .au-tas { transition: stroke-dashoffset 1.6s var(--ease); }
  html.js .reveal .divider-au .au-mainland { stroke-dasharray: 2180; stroke-dashoffset: 2180; }
  html.js .reveal .divider-au .au-tas { stroke-dasharray: 190; stroke-dashoffset: 190; }
  html.js .reveal.in .divider-au path { stroke-dashoffset: 0; }
}
.footer-au { width: 200px; opacity: 0.14; position: absolute; right: 0; bottom: -20px; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) {
  html.js .footer-au { animation: au-float 6s ease-in-out infinite; }
}
@keyframes au-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }

/* Service-page compact hero */
.hero-compact { min-height: auto; padding-top: 140px; padding-bottom: 64px; }
.breadcrumb { color: rgba(255,255,255,0.6); font-size: 0.85rem; margin-bottom: 18px; }
.breadcrumb a:hover { color: var(--lime); }

/* Council/suburb marquee band -- pure-CSS, pauses on hover/focus, static list without motion pref */
.trust-strip { background: var(--surface-2); border-bottom: 1px solid var(--border); padding: 28px 0; }
.trust-strip-label { font-size: 0.78rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin-bottom: 16px; }
/* N-505: min-height on the focusable container so its keyboard-focus target
   is a full 44px even though the logo row itself is 26px tall. */
.marquee { overflow: hidden; border-radius: 4px; min-height: 44px; display: flex; align-items: center; }
/* flex-shrink:0 keeps this at its natural max-content width -- without it,
   being a flex child of the now-flex .marquee (added for the 44px focus-area
   fix above) would shrink it to fit the viewport and silently break the
   whole scrolling-marquee effect. */
.marquee-track { display: flex; align-items: center; width: max-content; flex-shrink: 0; }
/* F-401/F-501: both halves of the marquee share every rule (display/gap/padding/img
   styling) so their box models -- and therefore rendered widths -- are
   identical; the translateX(-50%) wrap only reads seamless when they match.
   Each half now carries the 6 logos twice (12 imgs/half, second copy
   aria-hidden) so a half is always >= the widest marquee window (~1160px
   content max) -- at 6 logos a half topped out at ~828.6px, leaving up to a
   283px hole at 1440 before the loop caught up. */
.trust-logos, .trust-logos-dup { display: flex; flex-wrap: wrap; align-items: center; gap: 30px 40px; padding-right: 40px; }
.trust-logos img, .trust-logos-dup img { height: 31px; width: auto; opacity: 0.7; filter: grayscale(1); }
.trust-logos-dup { display: none; }
.trust-disclaimer { font-size: 0.76rem; color: var(--muted); margin-top: 16px; max-width: 720px; }
/* Polish: council logos sized up for desktop legibility (~40px rendered vs
   the 31px mobile size above). Gap/padding scaled up alongside so the band
   still breathes at the bigger logo size. Both halves share this rule too
   (F-401/F-501 still applies) so box models stay identical. */
@media (min-width: 768px) {
  .trust-logos, .trust-logos-dup { gap: 36px 50px; padding-right: 50px; }
  .trust-logos img, .trust-logos-dup img { height: 40px; }
}
@media (prefers-reduced-motion: no-preference) {
  /* Duration doubled from the original 32s alongside the doubled (6->12) logo
     count per half, so the marquee's px/second speed -- and therefore how
     fast it reads -- stays the same as before the duplication fix. Bumped
     again 64s->96s when the desktop logo/gap upsize grew each half's width
     by ~50% (1645px->2467px at 1440), keeping px/second speed unchanged. */
  .marquee-track { animation: marquee-scroll 96s linear infinite; }
  .trust-logos, .trust-logos-dup { flex-wrap: nowrap; }
  .trust-logos-dup { display: flex; }
  .marquee:hover .marquee-track, .marquee:focus-within .marquee-track { animation-play-state: paused; }
}
/* Polish: slightly faster read on mobile, independent of the desktop-speed
   compensation above. Bumped 23s->27s alongside the mobile logo/gap upsize
   (half grew ~17% at 390, 1645px->1927px) to hold the same px/second speed. */
@media (prefers-reduced-motion: no-preference) and (max-width: 767px) {
  .marquee-track { animation-duration: 27s; }
}
@keyframes marquee-scroll { to { transform: translateX(-50%); } }

/* Cards grid */
.grid { display: grid; gap: 24px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-2col { display: grid; gap: 40px; grid-template-columns: 1fr; }
@media (min-width: 700px) { .grid-2col { grid-template-columns: 1fr 1fr; } }
.grid-2col-wide { display: grid; gap: 48px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 780px) { .grid-2col-wide { grid-template-columns: 1.2fr 0.8fr; } }
/* Services grid (home "What We Do" only): forces 3+3 at desktop instead of
   .grid's own auto-fit, which already gives 3 columns between ~868-1152px
   container width but jumps to 4+2 above that (the 1120px-capped .container
   fits a 4th 260px column once the viewport clears ~1152px, e.g. 1280/1440).
   Scoped to this class (not .grid itself) so the review-card echo other
   pages reuse .grid for is untouched. Mobile/tablet stacking (1/2 cols,
   below this same site-wide 1120px desktop gate) is unchanged. */
@media (min-width: 1120px) {
  .services-grid { grid-template-columns: repeat(3, 1fr); }
}
.card {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 28px; transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
@media (prefers-reduced-motion: no-preference) {
  .card { transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease; }
  .card:hover { transform: translateY(-4px); }
}
.card:hover { box-shadow: var(--shadow); }
/* N-506: emoji swapped for inline SVG line icons (stroke: currentColor), so
   this just sizes/positions the svg now instead of a font-sized glyph. */
.card-icon {
  width: 48px; height: 48px; border-radius: 12px; background: rgba(2,121,61,0.1);
  display: flex; align-items: center; justify-content: center; color: var(--green);
  margin-bottom: 18px;
}
.card-icon svg { width: 24px; height: 24px; }
.card h3 { margin-bottom: 10px; }
.card p { font-size: 0.95rem; }
/* F-601: 76 numeric-entity emoji (checkmark/pushpin/shield bullets on service
   + about-us "what's included" lists) replaced with inline data-URI SVG
   markers via li::before -- three rules total, one per list type. currentColor
   is not available inside a data URI so the green #02793D stroke is baked in
   directly. Purely decorative (::before content is never exposed to a
   screen reader), sized to sit on a 24px line-height baseline. */
.list-check li::before { content: ''; display: inline-block; width: 18px; height: 18px; margin-right: 8px; vertical-align: -3px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2302793D' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") no-repeat center / contain; }
.list-pin li::before { content: ''; display: inline-block; width: 18px; height: 18px; margin-right: 8px; vertical-align: -3px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2302793D' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 22s7-7.6 7-13a7 7 0 1 0-14 0c0 5.4 7 13 7 13Z'/%3E%3Ccircle cx='12' cy='9' r='2.5'/%3E%3C/svg%3E") no-repeat center / contain; }
.list-shield li::before { content: ''; display: inline-block; width: 18px; height: 18px; margin-right: 8px; vertical-align: -3px; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2302793D' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2 4 5v6c0 5.5 3.5 9.7 8 11 4.5-1.3 8-5.5 8-11V5Z'/%3E%3C/svg%3E") no-repeat center / contain; }
/* N-505: ::before expands the click/tap target to 44px without touching the
   visible link size or spacing. */
.card-link { position: relative; display: inline-flex; align-items: center; gap: 6px; margin-top: 16px; font-weight: 600; font-size: 0.9rem; color: var(--green); }
.card-link::before { content: ''; position: absolute; inset: -14px -8px; }
.card-link::after {
  content: ''; position: absolute; left: 0; bottom: -3px; width: 100%; height: 2px; background: var(--lime);
  transform: scaleX(0); transform-origin: left;
}
@media (prefers-reduced-motion: no-preference) {
  .card-link::after { transition: transform 0.3s var(--ease); }
}
.card:hover .card-link::after, .card-link:hover::after { transform: scaleX(1); }

/* Stats band */
.stats-band { background: var(--ink); color: #fff; }
.stats-grid { display: grid; gap: 32px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.stat-num { font-family: var(--font-display); font-weight: 400; text-transform: uppercase; font-size: clamp(2.2rem, 5vw, 3rem); color: var(--lime); }
.stat-label { color: var(--muted-dark); font-size: 0.92rem; margin-top: 6px; }

/* Before/after slider */
.slider-wrap { max-width: 760px; margin: 0 auto; }
.ba-slider {
  position: relative; width: 100%; aspect-ratio: 4/5; max-height: 620px;
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
  touch-action: pan-y; user-select: none;
}
.ba-slider img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ba-after { z-index: 1; }
.ba-before { z-index: 2; clip-path: inset(0 50% 0 0); }
.ba-handle {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 4px; background: var(--lime);
  z-index: 3; transform: translateX(-2px); box-shadow: 0 0 0 4px rgba(200,220,40,0.3);
}
.ba-handle::after {
  content: '\2194'; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 44px; height: 44px; background: var(--lime); border-radius: 50%; color: var(--ink);
  display: flex; align-items: center; justify-content: center; font-size: 1.1rem; box-shadow: var(--shadow-sm);
}
/* handle pulse hint -- 3 pulses then stop, reduced-motion gated */
@media (prefers-reduced-motion: no-preference) {
  html.js .ba-handle::after { animation: handle-pulse 1.6s var(--ease) 3; }
}
@keyframes handle-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(200,220,40,0.5), var(--shadow-sm); }
  50% { box-shadow: 0 0 0 10px rgba(200,220,40,0), var(--shadow-sm); }
}
.ba-tag { position: absolute; top: 16px; z-index: 4; background: var(--ink); color: #fff; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; padding: 6px 12px; border-radius: 999px; }
.ba-tag-before { left: 16px; }
.ba-tag-after { right: 16px; }
.ba-caption { text-align: center; margin-top: 18px; font-size: 0.92rem; color: var(--muted); }
.ba-stacked { display: none; }
noscript .ba-slider { display: none; }
noscript .ba-stacked { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; }

/* Process steps */
.steps { display: grid; gap: 24px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.step-num { font-family: var(--font-display); font-weight: 400; font-size: 2.2rem; color: var(--green); opacity: 0.35; margin-bottom: 10px; }

/* Reviews */
.review-card { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 26px; }
.review-stars { color: var(--green); font-size: 0.95rem; margin-bottom: 12px; letter-spacing: 2px; }
.review-name { font-weight: 600; margin-top: 14px; font-size: 0.92rem; }
.review-quote { font-size: 0.98rem; color: var(--text); }
/* Real reviews expanded to 9 (2026-07-24): mobile shows the first 3 only
   (CSS-only cut, all 9 stay in the DOM for no-JS), desktop shows all 9 in
   a 3x3 grid. Separate class from the generic .grid (which auto-fits and
   would give 4+ columns at desktop widths) so this section gets exact
   column control without touching any other grid on the site. */
.reviews-grid { display: grid; gap: 24px; grid-template-columns: 1fr; }
.reviews-grid .review-card:nth-child(n+4) { display: none; }
@media (min-width: 768px) {
  .reviews-grid { grid-template-columns: repeat(3, 1fr); }
  .reviews-grid .review-card:nth-child(n+4) { display: block; }
}
.review-count-band { margin-top: 16px; font-size: 1rem; color: var(--muted); }
.review-count-band strong { color: var(--text); font-weight: 700; }
.review-count-band a { color: var(--green); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.review-count-band a:hover { text-decoration-thickness: 2px; }

/* Gallery */
.gallery { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.gallery img { border-radius: var(--radius-sm); aspect-ratio: 4/3; object-fit: cover; }

/* FAQ */
.faq-item { border-bottom: 1px solid var(--border); padding: 20px 0; }
.faq-item summary { cursor: pointer; font-weight: 600; font-size: 1.02rem; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; min-height: 44px; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: '+'; font-size: 1.4rem; color: var(--green); flex-shrink: 0; }
.faq-item[open] summary::after { content: '\2212'; }
.faq-item p { margin-top: 12px; font-size: 0.95rem; }

/* CTA band -- deep dark-green, lime primary CTA (all primary CTAs are lime, no exceptions) */
.cta-band { background: var(--deep); color: #fff; border-radius: var(--radius); padding: 48px 32px; text-align: center; }
.cta-band h2 { color: #fff; }
.cta-band p { color: rgba(255,255,255,0.85); margin-top: 10px; }
.cta-band .hero-actions { justify-content: center; margin-top: 26px; }

/* Service area band */
.area-band { background: var(--surface-2); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.area-list { display: flex; flex-wrap: wrap; gap: 10px 16px; margin-top: 20px; }
.area-list span { font-size: 0.9rem; color: var(--muted); padding: 6px 14px; border: 1px solid var(--border); border-radius: 999px; }

/* Form */
.form-wrap { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 32px; box-shadow: var(--shadow-sm); }
.form-row { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; margin-bottom: 16px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 0.85rem; font-weight: 600; }
.field input, .field select, .field textarea {
  min-height: 44px; padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: #fff; color: var(--text);
  width: 100%; min-width: 0; box-sizing: border-box;
}
.field textarea { min-height: 100px; resize: vertical; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--green); outline-offset: 1px; }
.form-msg { margin-top: 16px; padding: 14px 16px; border-radius: var(--radius-sm); font-size: 0.92rem; display: none; }
.form-msg.show { display: block; }
.form-msg.ok { background: rgba(2,121,61,0.1); color: var(--deep); border: 1px solid rgba(2,121,61,0.25); }
.form-msg.err { background: rgba(192,57,43,0.08); color: var(--error); border: 1px solid rgba(192,57,43,0.3); }
.form-msg.err a { font-weight: 700; text-decoration: underline; }

/* Contact page split */
.contact-grid { display: grid; gap: 40px; grid-template-columns: minmax(0, 1fr); }
.contact-grid > * { min-width: 0; }
.form-row { min-width: 0; }
.form-row > .field { min-width: 0; }
.contact-info-item { display: flex; gap: 14px; align-items: flex-start; padding: 16px 0; border-bottom: 1px solid var(--border); }
.contact-info-item .card-icon { flex-shrink: 0; margin-bottom: 0; }
.map-embed { border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border); height: 280px; background: var(--paper); }
.map-embed iframe { width: 100%; height: 100%; border: 0; }

/* Footer */
.footer { position: relative; background: var(--ink); color: rgba(255,255,255,0.75); padding: 56px 0 28px; overflow: hidden; }
.footer-grid { position: relative; z-index: 1; display: grid; gap: 32px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin-bottom: 40px; }
.footer h4 { color: #fff; font-size: 1rem; margin-bottom: 16px; }
/* N-505: 44px hit area via padding, replacing the old 6px-padding block links. */
.footer a { color: rgba(255,255,255,0.7); font-size: 0.9rem; display: flex; align-items: center; min-height: 44px; padding: 0; position: relative; }
.footer a:hover { color: var(--lime); }
.footer-logo { display: flex; align-items: center; gap: 10px; color: #fff; font-family: var(--font-body); font-weight: 600; text-transform: uppercase; letter-spacing: 0.01em; margin-bottom: 14px; }
.footer-logo img { height: 32px; }
.footer-social { display: flex; gap: 12px; margin-top: 16px; }
/* N-505: visible circle stays 40px; ::before extends the actual tap area to 44px. */
.footer-social a { width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,0.08); display: flex; align-items: center; justify-content: center; padding: 0; position: relative; }
.footer-social a::before { content: ''; position: absolute; inset: -2px; }
.footer-social a:hover { background: var(--lime); color: var(--ink); }
.footer-bottom { position: relative; z-index: 1; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 24px; display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between; font-size: 0.82rem; }
.footer-bottom a { display: inline-block; padding: 0; position: relative; }
.footer-bottom a::before { content: ''; position: absolute; inset: -13px -8px; }
.footer-bottom a:hover { color: var(--lime); }
.footer-disclaimer { position: relative; z-index: 1; font-size: 0.78rem; color: rgba(255,255,255,0.45); margin-top: 20px; max-width: 720px; }

/* Reveal animation -- content visible by default, JS only enhances.
   The .js class is added synchronously at the top of <body> the instant JS runs.
   Without it (no-JS / in-app browser that drops the script), .reveal elements
   simply stay at their default visible state -- no permanently-blank content. */
.reveal { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  html.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); }
  html.js .reveal.in { opacity: 1; transform: none; }
  /* staggered headline rise -- hero children step 70-90ms apart */
  html.js .hero .reveal { transition-delay: calc(var(--stg, 0) * 80ms); }
}
.hero-badge { --stg: 0; }
.hero h1 { --stg: 1; }
.hero-sub { --stg: 2; }
.hero-actions { --stg: 3; }
.hero-trust { --stg: 4; }

/* Floating mobile call pill (<1120px) -- 2026-07-24 amendment: replaces the
   full-width ink sticky-cta bar + separate Text Us button with a single lime
   "Call" pill, floating rather than edge-to-edge. Reuses .call-fab-trigger's
   own lime/ink/shadow/pulse tokens as its base (adds a text label + pill
   shape instead of the 56px icon-only circle) so it reads as one system with
   the >=1120px desktop .call-widget; the two share the same 1120px gate
   (folded into that existing media query below) so they never co-display. */
.call-fab-trigger.mobile-call-pill {
  position: fixed; left: 50%; z-index: 480;
  bottom: 16px; bottom: max(16px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: auto; height: auto; border-radius: 999px;
  padding: 13px 24px; gap: 10px;
}
.mobile-call-pill span { font-size: 0.92rem; font-weight: 600; white-space: nowrap; }
.mobile-call-pill.widget-hide { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) {
  .mobile-call-pill { transition: opacity 0.2s ease; }
}

@media (min-width: 860px) {
  .contact-grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); }
  .form-wrap { padding: 40px; }
}

/* F-105: the full inline nav (logo + 8 links + Call Now + Free Quote) needs
   ~1050px to lay out without clipping. 860px left a dead zone where the
   burger was already hidden but the links did not yet fit. Raised to 1120px
   (safety margin above the measured requirement) so the burger stays the nav
   UI until the full row genuinely has room. */
@media (min-width: 1120px) {
  .nav-links { display: flex; }
  .nav-burger { display: none; }
  .mobile-call-pill { display: none; }
  /* F-504: belt-and-braces on top of visibility:hidden -- the desktop nav
     never opens this panel, so remove it from layout/tab-order entirely. */
  .mobile-menu { display: none; }
}

@media (max-width: 859px) {
  .hero { padding-bottom: 100px; }
  .section { padding: 56px 0; }
  body { padding-bottom: 0; }
}

@media (max-width: 480px) {
  .form-row { grid-template-columns: 1fr; }
  .cta-band { padding: 36px 20px; }
}

/* F-503: the wordmark text is what pushed the burger off the 320px viewport
   (nav-logo + nav-cta no longer fit at once); the logo image alone still
   carries the brand below this width. */
@media (max-width: 399px) {
  .nav-logo-text { display: none; }
}

/* Polish: keep the "Established in 1990" claim on one line at 320 by hiding
   the trailing descriptive clause; the claim itself is never shortened or
   changed, only the elaboration after it. */
@media (max-width: 379px) {
  .badge-extra { display: none; }
}
