/* ===========================================================================
   All Fencing Solutions - allfencingsolutions.co.nz
   ---------------------------------------------------------------------------
   The client named neweraroofing.nz as the design they want, so this is a
   deliberate visual match rather than a site "inspired by" it. Every token
   below was read off that site's own CSS (bb-theme skin + the Beaver Builder
   layout cache) and checked against screenshots at 1440px, not guessed:

     body      #f2f2f2 page / #000 text / Red Hat Text 17px / line-height 1.6
     headings  Red Hat Display 600-700, letter-spacing 0
     bands     white, #e8e8e8 light, #333334 dark, #000 footer strip
     buttons   solid #000, white uppercase label, SHARP corners, ring-arrow
     sections  75px top and bottom
     hero      full-bleed photo, content bottom-aligned, black gradient rising
               from the bottom: linear-gradient(0deg,rgba(0,0,0,.95) 20%,transparent)

   🔴 There is NO colour accent. An earlier build of this site used a green
   (#2e6b3e) on the reasoning that two agency sites should not be identical -
   that was the wrong call, because the client asked for THIS look, and the
   reference is strictly monochrome (black / grey / white) with yellow review
   stars as the only colour on the page. Adding an accent back is what made the
   first build read as a different site. Keep it monochrome.

   --accent is retained as a token, pointing at black, so any rule that still
   references it renders correctly rather than silently reintroducing colour.

   Type is Red Hat Display / Red Hat Text, as on the reference, self-hosted
   rather than fetched from Google. Dependency-free and deliberately small:
   Otago customers are often on rural mobile data.
   =========================================================================== */

/* ---------------------------------------------------------------- fonts
   Self-hosted latin subsets (SIL OFL 1.1). Three files, 72 kB total, versus a
   Google Fonts request that costs a DNS lookup, a connection and a redirect
   before a single glyph arrives. */
@font-face {
  font-family: 'Red Hat Display';
  src: url('/assets/fonts/redhatdisplay-700-latin.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Red Hat Text';
  src: url('/assets/fonts/redhattext-400-latin.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Red Hat Text';
  src: url('/assets/fonts/redhattext-700-latin.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ---------------------------------------------------------------- tokens */
:root {
  --ink:        #000000;   /* the reference sets body colour to #000, not a soft black */
  --ink-soft:   #3a3a3a;
  --ink-mute:   #666666;
  --dark:       #333334;   /* the reference site's dark band AND its footer */
  --darker:     #000000;   /* the black strip under the footer */
  --band:       #e8e8e8;   /* the reference site's light band */
  --line:       #dddddd;
  --white:      #ffffff;

  /* Monochrome. These exist so nothing that still says var(--accent) turns a
     stray colour back on - see the header comment. */
  --accent:     #000000;
  --accent-dk:  #333334;
  --accent-lt:  #ffffff;   /* the "accent" ON dark is simply white */

  --btn-bg:     #000000;
  --btn-fg:     #ffffff;

  --wrap:       1180px;
  --gap:        clamp(20px, 3vw, 40px);
  --sec-y:      clamp(46px, 6vw, 75px);   /* the reference row padding is 75px */
  --radius:     0;                        /* sharp corners everywhere, as the reference */
  --shadow:     0 6px 22px rgba(0,0,0,.18);

  --font-head:  'Red Hat Display', 'Segoe UI', system-ui, sans-serif;
  --font-body:  'Red Hat Text', 'Segoe UI', system-ui, sans-serif;

  /* The ring-and-arrow glyph that sits inside every link button on the
     reference site, as a mask so it inherits the button's text colour and
     needs no markup change. One path, fill-rule evenodd: outer circle, inner
     circle punched out to leave a ring, then the arrowhead - which sits inside
     two boundaries plus its own, so it counts odd and fills. */
  --ico-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1.6A10.4 10.4 0 1 0 12 22.4 10.4 10.4 0 0 0 12 1.6Zm0 1.9a8.5 8.5 0 1 1 0 17 8.5 8.5 0 0 1 0-17Zm-1.9 4.2 6 4.3-6 4.3Z'/%3E%3C/svg%3E");
  /* The tick inside every disc in the reference's confirmation lists. */
  --ico-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 12.5 5 5 10-11'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The header is sticky, so every in-page anchor - "Get a Quote" on every page,
     and the service links - would otherwise land with its heading hidden behind
     the header. One property fixes all of them. */
  scroll-padding-top: 96px;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--white);
  overflow-x: hidden;
}

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

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: 0;      /* the reference runs zero tracking on headings */
  margin: 0 0 .55em;
  color: var(--ink);
  text-wrap: balance;
}

/* The reference site's proportions are unusual and worth copying exactly: its
   section H2s (52px) are LARGER than its hero H1 (36px). The section headings
   are what carry the page - "Our Roofing Services", "Why Choose New Era
   Roofing?" - and the hero heading sits quietly above a bold lead sentence.
   Matching that is most of why this reads like the same designer.

   Display type still scales to the CONTENT: build.mjs stamps data-len with the
   H1's character count, because an eight-character heading and a
   hundred-character heading cannot share a font size without one of them
   either looking timid or pushing the hero CTA off the screen. */
h1 { font-size: clamp(1.85rem, 3.7vw, 2.6rem); }
h1[data-len="long"]  { font-size: clamp(1.65rem, 3.1vw, 2.2rem); }
h1[data-len="xlong"] { font-size: clamp(1.45rem, 2.6vw, 1.9rem); }

h2 { font-size: clamp(1.85rem, 4.1vw, 3.25rem); }   /* 52px at 1440px */
h3 { font-size: clamp(1.2rem, 1.9vw, 1.4rem); line-height: 1.35; }
h4 { font-size: 1.05rem; line-height: 1.4; }

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

/* Black links, underlined on hover only - exactly the reference's rule. */
a { color: var(--ink); text-decoration: none; }
a:hover, a:focus-visible { color: var(--ink); text-decoration: underline; }

strong { font-weight: 700; }

:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}
/* A black focus ring is invisible on the dark bands, the hero and the footer.
   Same rule, inverted, wherever the background is dark. */
.sec--dark :focus-visible,
.hero :focus-visible,
.trust :focus-visible,
.ftr :focus-visible,
.faq :focus-visible,
.form :focus-visible { outline-color: #fff; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(18px, 4vw, 32px);
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: #fff; padding: 12px 18px;
}
.skip:focus { left: 8px; top: 8px; }

/* The reference has no eyebrow labels at all - a section is just its big H2.
   These are kept because they carry keyword context for a crawler, but they are
   muted to a quiet grey so they read as a kicker rather than the flash of brand
   colour that made the first build look like a different site. */
.eyebrow {
  display: block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: .76rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: .8em;
}
.sec--dark .eyebrow, .faq-band .eyebrow { color: rgba(255,255,255,.66); }

/* ---------------------------------------------------------------- buttons
   Components must beat prose. `.prose a` is (0,1,1) and `.btn` is (0,1,0), so
   without the :not() scoping further down, a body-link colour rule silently
   wins over a button's own colour and every CTA inside prose renders dark text
   on a dark button. That shipped once on prestigepainters at 1.00:1. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: .95rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.2;
  padding: 15px 28px;
  border: 1px solid var(--btn-bg);
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  cursor: pointer;
  text-decoration: none;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.btn:hover, .btn:focus-visible {
  background: var(--dark);
  border-color: var(--dark);
  color: #fff;
  text-decoration: none;
}

/* The ring-arrow rides on link buttons only. The reference's own SEND button
   is a plain label, and a "go somewhere" arrow on a form submit is a lie. */
a.btn::before {
  content: '';
  flex: 0 0 auto;
  width: 1.22em; height: 1.22em;
  background: currentColor;
  -webkit-mask: var(--ico-arrow) center / contain no-repeat;
          mask: var(--ico-arrow) center / contain no-repeat;
}
a.btn--bare::before { display: none; }

.btn--sm { padding: 11px 20px; font-size: .82rem; }
.btn--ghost {
  background: transparent;
  color: #fff;
  border-color: #fff;
}
.btn--ghost:hover, .btn--ghost:focus-visible {
  background: #fff; border-color: #fff; color: var(--ink);
}
.btn--light {
  background: #fff; border-color: #fff; color: var(--ink);
}
.btn--light:hover, .btn--light:focus-visible {
  background: var(--band); border-color: var(--band); color: var(--ink);
}
/* A white button on a WHITE section needs its own edge. Replaces the
   style="border-color:#1b1b1b" that was inlined on three of these. */
.btn--outline {
  background: #fff; border-color: var(--ink); color: var(--ink);
}
.btn--outline:hover, .btn--outline:focus-visible {
  background: var(--ink); border-color: var(--ink); color: #fff;
}

/* ---------------------------------------------------------------- header
   The reference header: white, sticky, logo hard left, the menu centred in
   UPPERCASE with a house glyph as its first item, and a single solid-black
   phone button hard right. It is tall on load and shrinks once the page
   scrolls - site.js flips [data-stuck] on, everything else is CSS.

   No border-bottom: the reference separates the bar from the page with a soft
   drop shadow, which is what lets the hero photo sit flush underneath it. */
.hdr {
  position: sticky;
  top: 0;
  z-index: 90;
  background: #fff;
  box-shadow: 0 1px 10px rgba(0,0,0,.16);
}
.hdr__in {
  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 26px);
  min-height: 118px;
  transition: min-height .22s ease;
}
.hdr[data-stuck] .hdr__in { min-height: 82px; }
@media (prefers-reduced-motion: reduce) { .hdr__in { transition: none; } }

/* Brand. The client's own logo as of 2026-08-19, replacing the live-text lockup
   that stood in for it while there was no artwork on file.
   The supplied PNG is 591x184 and FULLY OPAQUE - a green illustrated panel, not
   a lockup on transparency - so it renders as a rectangular image on both the
   white header and the dark footer. That is the artwork, and the site's own
   palette stays monochrome around it: no accent colour has been reintroduced
   anywhere else, which is a standing decision (see the design notes). */
.brand {
  display: inline-flex;
  align-items: center;
  color: var(--ink);
  text-decoration: none;
  flex: 0 0 auto;
}
.brand:hover, .brand:focus-visible { text-decoration: none; color: var(--ink); }
/* Sized by HEIGHT, with width:auto, because the aspect ratio is fixed at 3.212:1
   and the header is a height-constrained band. The intrinsic width/height on the
   tag still reserves the correct box before the file arrives, so there is no
   layout shift. The transition matches .hdr__in's, so it shrinks with the bar
   rather than a beat behind it. */
.brand__logo {
  flex: 0 0 auto;
  width: auto;
  height: 64px;
  max-width: 100%;
  transition: height .22s ease;
}
.hdr[data-stuck] .brand__logo { height: 50px; }
@media (prefers-reduced-motion: reduce) { .brand__logo { transition: none; } }

/* nowrap, not wrap. Six items plus a brand lockup and a phone-and-button group
   is a tight fit inside a 1220px container, and with flex-wrap:wrap the overflow
   is silent - "Contact" simply drops onto a second row and the header grows by
   60px. Below the breakpoint the burger takes over instead. */
/* flex:1 rather than margin:auto. With auto margins the bar centres inside
   whatever space is left over, so an asymmetric brand and CTA push it off
   centre - and this brand lockup is much wider than the reference's monogram.
   Taking the remaining space and centring inside it puts the menu where the
   reference has it. */
.nav {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1.4vw, 22px);
}
/* Uppercase, REGULAR weight, no tracking - the reference menu is set in plain
   Red Hat Text at 16px and the restraint is a large part of its look. Bolding
   it, which the first build did, immediately reads as a different site. */
.nav a {
  font-size: .95rem;
  font-weight: 400;
  text-transform: uppercase;
  color: var(--ink);
  padding: 6px 2px;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
}
.nav a:hover, .nav a:focus-visible {
  color: var(--ink);
  border-bottom-color: var(--ink);
  text-decoration: none;
}
.nav a[aria-current="page"] { border-bottom-color: var(--ink); }

/* The house glyph that opens the reference menu in place of a "Home" label.
   🔴 flex:0 0 auto and an EXPLICIT width/height are both load-bearing - without
   them this glyph silently absorbs the entire overflow of the nav bar.
   .nav is flex-wrap:nowrap and every other item is text with white-space:nowrap,
   so nothing else can shrink below its min-content width. This one is an <img>-
   like replaced element with no text floor, and the global rule at the top of
   this file (`img, svg, video { max-width:100%; height:auto }`) leaves its height
   free to follow. So it took up 100% of the shortfall on its own. Measured
   before the fix, against an intended 23px:
       1440px -> 14.1px      1280px -> 21.5px
       1160px -> 19.4px      1141px ->  2.4px   (invisible, as reported)
   Note it gets WORSE as the window gets WIDER, because the bar's own
   gap:clamp(10px,1.4vw,22px) grows with the viewport faster than the space
   does - which is why it can look fine on one screen and be a speck on another,
   and why it read as "small on the home page" rather than as a layout fault.
   The size is now identical on every page and at every width. Nothing keys off
   aria-current except the underline below, so being ON the home page cannot
   change it; verify.mjs asserts that. */
.nav__home { display: inline-flex; align-items: center; padding: 6px 2px; flex: 0 0 auto; }
.nav__home svg { display: block; flex: 0 0 auto; width: 30px; height: 30px; }

/* Services dropdown. Six services will not fit in a flat bar, and the reference
   site uses a dropdown too. Opens on hover AND on :focus-within, so it works
   with no JS and from the keyboard; the JS click handler exists only for touch
   devices, which never fire :hover reliably. */
.nav__drop { position: relative; display: flex; align-items: center; }
.nav__dropbtn {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-body);
  font-size: .95rem; font-weight: 400;
  text-transform: uppercase;
  color: var(--ink);
  background: none; border: 0;
  border-bottom: 2px solid transparent;
  padding: 6px 2px; cursor: pointer;
  white-space: nowrap;
}
.nav__dropbtn:hover, .nav__drop:focus-within .nav__dropbtn {
  color: var(--ink); border-bottom-color: var(--ink);
}
.nav__panel {
  position: absolute;
  top: 100%; left: 50%;
  transform: translateX(-50%);
  min-width: 278px;
  margin-top: 16px;
  padding: 8px 0;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  opacity: 0; visibility: hidden;
  transition: opacity .14s ease, visibility .14s ease;
}
/* Bridges the gap the margin opens up. Without it the pointer leaves .nav__drop
   on the way down to the panel, :hover drops and the menu closes under the
   cursor - which reads as a broken menu rather than a 16px offset. */
.nav__panel::before {
  content: ''; position: absolute; left: 0; right: 0; top: -18px; height: 18px;
}
.nav__drop:hover .nav__panel,
.nav__drop:focus-within .nav__panel,
.nav__drop[data-open] .nav__panel { opacity: 1; visibility: visible; }
.nav__panel a {
  display: block;
  padding: 9px 18px;
  border-bottom: 0;
  white-space: nowrap;
}
.nav__panel a:hover, .nav__panel a:focus-visible { background: #f5f5f5; border-bottom: 0; }
.nav__panel a[aria-current="page"] { border-bottom: 0; }

.hdr__cta { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }

/* The reference's one and only header CTA: a solid black block carrying a
   handset glyph and the number, with a soft shadow under it. It replaces the
   plain phone link plus separate quote button the first build used - two
   competing CTAs is the thing that made that header look generic.
   "Get a quote" is still one tap away: it leads every hero, it is the first
   item in the mobile menu, and the float bar carries it on every scroll. */
.hdr__phone {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .01em;
  color: #fff;
  background: var(--btn-bg);
  padding: 15px 26px;
  box-shadow: 0 3px 10px rgba(0,0,0,.3);
  white-space: nowrap;
}
.hdr__phone:hover, .hdr__phone:focus-visible {
  color: #fff; background: var(--dark); text-decoration: none;
}
.hdr__phone svg { flex: 0 0 auto; }

.burger {
  display: none;
  width: 46px; height: 40px;
  padding: 9px 8px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
.burger span {
  display: block; height: 2px; background: var(--ink); border-radius: 2px;
}
.burger span + span { margin-top: 5px; }

.nav-m { display: none; border-top: 1px solid var(--line); background: #fff; padding: 8px 0 18px; }
.cnc-js .nav-m[data-open] { display: block; }
/* With JS off the desktop bar is still hidden below 1140px, so the mobile list
   has to be visible by default there or the site has no navigation at all. */
@media (max-width: 1140px) {
  html:not(.cnc-js) .nav-m { display: block; }
}
.nav-m a {
  display: block;
  padding: 12px clamp(18px, 4vw, 32px);
  font-weight: 700;
  color: var(--ink);
  border-bottom: 1px solid #f0f0f0;
}
.nav-m a:hover, .nav-m a:focus-visible { background: #f4f4f4; color: var(--ink); text-decoration: none; }
.nav-m__cta {
  margin: 14px clamp(18px, 4vw, 32px) 0;
  background: var(--btn-bg); color: #fff !important;
  text-transform: uppercase; letter-spacing: .06em;
  text-align: center; border-radius: var(--radius); border-bottom: 0 !important;
}
.nav-m__cta:hover, .nav-m__cta:focus-visible { background: var(--dark); }
.nav-m__cta + .nav-m__cta { margin-top: 10px; }

@media (max-width: 1140px) {
  .nav { display: none; }
  .burger { display: block; }
  .hdr__cta { margin-left: auto; }
  .hdr__in, .hdr[data-stuck] .hdr__in { min-height: 86px; }
}
/* Phones. The client asked for a quote CTA "up top somewhere to save people
   scrolling the whole page", so it has to survive down to a 360px viewport -
   alongside the brand, a phone link and the burger. At full size that group
   measured 531px inside a 375px viewport: `overflow-x: hidden` on the body hid
   the symptom, so it looked fine and the page was still 156px too wide.
   Everything below is that group being made to genuinely fit. */
@media (max-width: 720px) {
  .hdr__in, .hdr[data-stuck] .hdr__in { gap: 8px; min-height: 84px; }
  .hdr__cta { gap: 8px; }
  /* Sized to USE the row rather than to survive it. At 40px the logo was 128px
     wide with ~144px of empty white between it and the phone block, which read
     as a small image adrift rather than as a masthead - the whole row is 375px
     and less than half of it was doing anything.
     52px tall is 167px wide, and the row then measures 167 + 8 + 46 + 8 + 42 =
     271px inside a 347px content box at the narrowest phone width, so it still
     has room. Held flat when stuck: the bar does not shrink at this width. */
  .brand__logo, .hdr[data-stuck] .brand__logo { height: 52px; }
  /* The number itself goes, the black block stays: at 360px the brand, a phone
     button and a burger have to share the row, and the group measured 531px
     inside a 375px viewport before this. `overflow-x:hidden` on the body hid
     that, so it looked perfect and was still 156px too wide. */
  .hdr__phone span { display: none; }
  .hdr__phone { padding: 11px; box-shadow: none; }
  .burger { width: 42px; }
}
@media (max-width: 400px) {
  .wrap { padding-inline: 14px; }
  /* 46px -> 148px wide. Below 400px the phone block and burger take a bigger
     share of the row, so the logo gives some back rather than crowding them. */
  .brand__logo, .hdr[data-stuck] .brand__logo { height: 46px; }
}

/* ---------------------------------------------------------------- hero
   The reference site runs a background slideshow behind a bottom-up black
   gradient. One image plus the same gradient reads identically and costs one
   request. */
/* Photo, then a single black gradient rising from the bottom - the reference's
   own rule is linear-gradient(0deg, rgba(0,0,0,.95) 20%, transparent 100%), so
   the top of the image stays fully bright and all the darkening happens under
   the text. The first build faded the whole photo to 28% at the top, which is
   why its hero read as a grey box rather than a photograph. */
.hero { position: relative; isolation: isolate; background: #111; }
.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; }
/* 🔴 LIGHTENED 2026-08-14. The client's note on every page was the same: "the first part of the
   screen is very dark, the picture is hard to see."
   The old rule was the reference site's own - rgba(0,0,0,.95) flat for the bottom 20% then a ramp
   to transparent. Two things made that read as a black box rather than a photograph here: the
   floor is nearly solid, and because .hero__in is justify-content:flex-end the copy sits inside
   that floor, so the darkest band is exactly the part of the photo a visitor looks at first.
   The stops below are chosen against the WORST CASE - a pure white photograph - so the text band
   still clears 4.5:1 on white type:
     alpha .82 -> #2e2e2e -> 11.9:1     alpha .72 -> #474747 -> 8.0:1
     alpha .55 -> #737373 -> 4.9:1      alpha .18 -> #d1d1d1 -> 1.5:1  (no text sits this high)
   Above ~45% of the hero the photo is now essentially uncovered, which is the half the client
   was asking to see. Anything moved further has to be re-checked against that table, not by eye -
   a hero photograph is chosen by the client and can be light or dark on any given page. */
.hero::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(0deg,
    rgba(0,0,0,.82) 0%,
    rgba(0,0,0,.72) 20%,
    rgba(0,0,0,.55) 42%,
    rgba(0,0,0,.18) 68%,
    rgba(0,0,0,0)   88%);
}
.hero__in {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  text-align: center;                       /* the reference home hero is centred */
  min-height: clamp(460px, 68vh, 700px);
  padding-top: clamp(56px, 9vw, 110px);
  padding-bottom: clamp(40px, 5vw, 62px);
  color: #fff;
  max-width: var(--wrap);
}
.hero__in h1 { color: #fff; max-width: 22ch; }
.hero .eyebrow { color: rgba(255,255,255,.8); }
/* Bold lead sentence, then the paragraph - the reference stacks exactly these
   two. build.mjs emits the first line of .hero__lede in a <strong>. */
.hero__lede {
  font-size: clamp(1.02rem, 1.4vw, 1.12rem);
  max-width: 74ch;
  color: rgba(255,255,255,.94);
  margin-bottom: 1.8em;
}
.hero__lede strong { display: block; margin-bottom: .5em; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }

/* Interior pages keep the reference's OTHER hero treatment - the one on its
   contact page: shorter, and left-aligned rather than centred. */
.hero--sm .hero__in {
  min-height: clamp(300px, 42vh, 430px);
  align-items: flex-start;
  text-align: left;
}
.hero--sm .hero__cta { justify-content: flex-start; }

/* Trust strip under the hero - the reference site's "family owned / warranty"
   promises, pulled up where they are seen before any scrolling. */
.trust { background: var(--dark); color: #fff; }
.trust__in {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 4px clamp(18px, 3vw, 40px);
  padding: 24px 0;
}
.trust__item {
  display: flex; align-items: center; gap: 11px;
  font-size: .93rem; font-weight: 700; color: #fff;
  padding: 7px 0;
}
.trust__item svg { flex: 0 0 auto; color: #fff; }

/* ---------------------------------------------------------------- sections */
.sec { padding-block: var(--sec-y); }
.sec--white { background: #fff; }
.sec--band  { background: var(--band); }
.sec--dark  { background: var(--dark); color: #fff; }
.sec--dark h2, .sec--dark h3 { color: #fff; }
.sec--dark a:not(.btn):not(.card) { color: #fff; }
.sec--dark a:not(.btn):not(.card):hover { color: #fff; }
.sec--tight { padding-block: clamp(32px, 4vw, 56px); }

/* On the reference, a section heading sits on its own line at full width and
   the supporting sentence runs under it - the heading is never squeezed into a
   narrow measure. 900px keeps a 52px heading to two lines at most. */
.sec__head { max-width: 900px; margin-bottom: clamp(28px, 4vw, 46px); }
.sec__head--mid { margin-inline: auto; text-align: center; }
.sec__head p { color: var(--ink-soft); font-size: 1.05rem; max-width: 74ch; }
.sec--dark .sec__head p { color: rgba(255,255,255,.88); }
.sec__head--mid p { margin-inline: auto; }

/* ---------------------------------------------------------------- prose */
.prose h2 { margin-top: 1.7em; }
.prose h2:first-child { margin-top: 0; }
/* An eyebrow is a kicker ON the heading, not a paragraph before it. Without
   this it takes the 1.7em section gap and the label floats 90px above the
   heading it belongs to. */
.prose .eyebrow + h2 { margin-top: 0; }
.prose h3 { margin-top: 1.5em; }
.prose ul, .prose ol { margin: 0 0 1.2em; padding-left: 1.25em; }
.prose li { margin-bottom: .45em; }
.prose li::marker { color: var(--ink); }

/* Scope body-link styling away from components. Without :not(.btn) the rule
   below is more specific than .btn and repaints every CTA inside prose. */
.prose a:not(.btn):not(.tlink) {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.prose a:not(.btn):not(.tlink):hover { color: var(--ink); text-decoration-thickness: 2px; }
.sec--dark .prose a:not(.btn):not(.tlink) { color: #fff; }

.prose blockquote {
  margin: 1.6em 0;
  padding: 4px 0 4px 22px;
  border-left: 4px solid var(--ink);
}
.prose blockquote cite {
  display: block; margin-top: .7em; font-style: normal;
  font-weight: 700; font-size: .92rem; color: var(--ink-mute);
}

.split {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 60px);
  align-items: start;
}
.split--flip .split__media { order: -1; }
.split--even { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
/* The reference frames every inline photo in a white border with a drop shadow
   under it, which is what stops the two-column rows looking like a slide deck.
   On a white section the frame is invisible and only the shadow reads - which
   is exactly how it behaves there too. */
.split__media img {
  width: 100%;
  border: 10px solid #fff;
  border-radius: 0;
  box-shadow: 0 8px 26px rgba(0,0,0,.22);
}
.sec--dark .split__media img { border-color: #fff; }
.split__media > * + * { margin-top: 22px; }
/* A service page's copy column runs several times the height of its photo, so
   without this the row ends in several hundred pixels of empty white. Sticky
   keeps the photo alongside the text it belongs to. 118px clears the header. */
@media (min-width: 861px) {
  .split__media { position: sticky; top: 118px; }
}
@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; }
  .split--flip .split__media { order: 0; }
}

.note {
  font-size: .95rem;
  background: var(--band);
  border-left: 4px solid var(--ink);
  padding: 14px 18px;
  border-radius: var(--radius);
}
.sec--dark .note { background: rgba(255,255,255,.1); color: #fff; border-left-color: #fff; }

/* ---------------------------------------------------------------- feature list
   The reference's "why we stand apart" list: a small SOLID BLACK disc with a
   white tick, a bold lead-in and the explanation running on after it. The disc
   is 30px and sits on the first line of text, not in a 52px column of its own -
   getting that size and alignment right is most of the resemblance. */
.feat { display: grid; gap: clamp(16px, 2vw, 22px); }
.feat__item { display: grid; grid-template-columns: 30px 1fr; gap: 14px; align-items: start; }
.feat__ico {
  width: 30px; height: 30px;
  margin-top: 2px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
}
/* 🔴 Every disc on the reference holds the SAME white tick - it is a
   confirmation list, not an icon set, and swapping in a different pictogram per
   row (a house, a shield, a map pin) reads as a feature grid instead. The
   per-page inline SVGs are hidden and one tick is masked in here, so the rule
   holds on all thirteen pages without editing any of them. */
.feat__ico svg { display: none; }
.feat__ico::before {
  content: '';
  width: 15px; height: 15px;
  background: currentColor;
  -webkit-mask: var(--ico-tick) center / contain no-repeat;
          mask: var(--ico-tick) center / contain no-repeat;
}
.sec--dark .feat__ico { background: #fff; color: var(--dark); }
.feat__item h3 { font-size: 1.02rem; margin-bottom: .2em; }
.feat__item p { color: var(--ink-soft); margin: 0; font-size: .98rem; }
.sec--dark .feat__item p { color: rgba(255,255,255,.88); }

/* ---------------------------------------------------------------- statement
   The dark band that breaks the home page in half: a sentence about the
   business on the left and one button pushed hard to the right edge of the
   content width. Same shape as the reference's "...teamwork, and community."
   row, which also sets the sentence well below section-heading size - it is a
   statement, not a heading, and at the 52px h2 scale it swamps the section.

   Was two inline-styled equal columns, which put the button in the middle of
   the row with nothing either side of it. */
.statement {
  display: grid;
  grid-template-columns: 75% auto;
  gap: clamp(20px, 3vw, 44px);
  align-items: center;
}
.statement h2 {
  margin: 0;
  font-size: clamp(1.25rem, 2.1vw, 1.75rem);
  line-height: 1.35;
  text-wrap: pretty;   /* balance leaves a stubby last line on a long sentence */
}
.statement__cta { justify-self: end; }
.statement__cta .btn { white-space: nowrap; }

/* Below ~900px a 75/auto split leaves the button squeezed into a sliver, so
   stack and let it sit under the sentence at full width on a phone. */
@media (max-width: 900px) {
  .statement { grid-template-columns: 1fr; gap: 22px; }
  .statement__cta { justify-self: start; }
}
@media (max-width: 460px) {
  .statement__cta, .statement__cta .btn { width: 100%; }
}

/* ---------------------------------------------------------------- callout */
.callout {
  background: var(--dark);
  color: #fff;
  border-radius: var(--radius);
  padding: clamp(22px, 3vw, 34px);
}
.callout h3 { color: #fff; margin-bottom: .6em; }
.callout dl { margin: 0; }
.callout dt {
  font-family: var(--font-head); font-weight: 700; font-size: 1.02rem;
  color: #fff; margin-top: 1.1em;
}
.callout dt:first-child { margin-top: 0; }
.callout dd { margin: .2em 0 0; color: rgba(255,255,255,.88); font-size: .97rem; }
.callout__note { display: block; margin-top: 1.4em; font-size: .82rem; color: rgba(255,255,255,.62); }
/* 🔴 The global rule is `a { color: var(--ink) }` and --ink is #000000, so every link inside this
   dark panel rendered BLACK ON #333334 - about 1.4:1, effectively invisible. The client found it
   on both pages that use a callout ("the phone number at the bottom is black and hard to read",
   "the black box on the right has links that are black and hard to see").
   .sec--dark and .ftr already had their own white-link rules; the callout is the case that was
   missed, because it sits inside a WHITE section and so looks safe in the markup. Whenever a new
   dark surface is added, this rule has to be added with it - check the rendered page, not the
   stylesheet. */
.callout a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.callout a:hover, .callout a:focus-visible { color: #fff; text-decoration: none; }

/* ---------------------------------------------------------------- cards */
/* 🔴 The reference has NO card chrome. Its services grid is a photo, a bold
   heading and a paragraph, sitting directly on the band - no white panel, no
   border, no radius, no shadow, no hover lift. The first build wrapped each one
   in a bordered white card, and that single decision is what made the grid look
   like a different site: the reference's grey band shows THROUGH the grid, and
   a row of white panels covers it up. Do not put the panel back. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(285px, 1fr));
  gap: clamp(26px, 3vw, 42px);
}
.cards--3 { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.card {
  display: flex;
  flex-direction: column;
  background: none;
  border: 0;
  color: var(--ink);
  text-decoration: none;
}
.card:hover, .card:focus-visible { text-decoration: none; color: var(--ink); }
.card__img { aspect-ratio: 4 / 3; overflow: hidden; background: var(--line); }
.card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
.card:hover .card__img img { transform: scale(1.045); }
.card__body { padding: 20px 0 0; display: flex; flex-direction: column; flex: 1 1 auto; }
.card__body h3 { margin-bottom: .5em; }
.card:hover .card__body h3 { text-decoration: underline; text-underline-offset: 3px; }
.card__body p { color: var(--ink-soft); font-size: .98rem; margin-bottom: 1.1em; }
.card__more {
  margin-top: auto;
  font-weight: 700; font-size: .86rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink);
}
.sec--dark .card, .sec--dark .card:hover { color: #fff; }
.sec--dark .card__body p { color: rgba(255,255,255,.88); }
.sec--dark .card__more { color: #fff; }
/* Not every card is a link. A hover cue on something with no destination reads
   as a broken button, so the static variant drops it. */
.card--static { cursor: default; }
.card--static:hover .card__img img { transform: none; }
.card--static:hover .card__body h3 { text-decoration: none; }

/* ---------------------------------------------------------------- reviews */
/* The generic five-star graphic is GONE - the client's words were "a generic 5 star means
   nothing", and they are right: a star row nobody earned on this site is decoration, not
   evidence. The Google mark replaces it, because the point of this section is now to send people
   to the real listing where the reviews cannot be edited by us.
   The Google logo is the one place colour is allowed back into a strictly monochrome design; it
   is a trademark and recolouring it to fit the palette is not an option. */
.reviews { text-align: center; }
.reviews__g { display: block; width: 34px; margin: 0 auto 14px; }
.reviews h2 { max-width: 34ch; margin-inline: auto; font-size: clamp(1.5rem, 2.6vw, 2.05rem); }
.reviews__note {
  max-width: 62ch; margin: 0 auto 24px; color: var(--ink-soft);
}
.reviews__alt { margin: 18px 0 0; font-size: .93rem; color: var(--ink-mute); }
.reviews__alt a { text-decoration: underline; text-underline-offset: 3px; }
/* Collapses to nothing until a review widget is actually wired in - an empty
   60px gap between the copy and the buttons reads as a broken embed. */
.reviews__slot:empty { display: none; }
.reviews__slot { margin: 0 0 22px; }
.reviews__cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }

/* ---------------------------------------------------------------- process */
.steps { counter-reset: step; display: grid; gap: clamp(18px, 2.4vw, 26px); }
.step {
  counter-increment: step;
  display: grid; grid-template-columns: 44px 1fr; gap: 16px; align-items: start;
}
.step::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-head); font-weight: 700; font-size: 1rem;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ink); color: #fff;
}
.sec--dark .step::before { background: #fff; color: var(--dark); }
.step h3 { margin-bottom: .3em; }
.step p { margin: 0; color: var(--ink-soft); font-size: .98rem; }
.sec--dark .step p { color: rgba(255,255,255,.86); }

/* ---------------------------------------------------------------- gallery */
.gal {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: clamp(14px, 2vw, 26px);   /* the reference gallery is generously spaced */
}
/* The home page shows exactly six. At the full-width auto-fill above that lands
   as five plus a lone orphan; wider columns give two clean rows of three. */
.gal--six { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.gal__btn {
  padding: 0; border: 0; background: var(--band);
  aspect-ratio: 4 / 3; overflow: hidden; cursor: zoom-in;
  border-radius: var(--radius);
}
.gal__btn img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.gal__btn:hover img, .gal__btn:focus-visible img { transform: scale(1.06); }

.lb {
  position: fixed; inset: 0; z-index: 300;
  display: none;
  background: rgba(10,10,10,.94);
  padding: clamp(12px, 3vw, 40px);
}
.lb[data-open] { display: grid; grid-template-rows: 1fr auto; place-items: center; }
.lb__img {
  max-width: min(1200px, 100%); max-height: 78vh;
  width: auto; object-fit: contain; border-radius: var(--radius);
}
.lb__cap { color: #fff; text-align: center; font-size: .92rem; padding-top: 14px; max-width: 70ch; }
.lb__btn {
  position: absolute;
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.32);
  color: #fff; width: 48px; height: 48px;
  border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  font-size: 1.3rem; line-height: 1;
}
.lb__btn:hover { background: rgba(255,255,255,.3); }
.lb__close { top: clamp(10px, 2vw, 24px); right: clamp(10px, 2vw, 24px); }
.lb__prev  { left: clamp(6px, 2vw, 24px); top: 50%; transform: translateY(-50%); }
.lb__next  { right: clamp(6px, 2vw, 24px); top: 50%; transform: translateY(-50%); }
body[data-lb] { overflow: hidden; }
body[data-lb] .hdr { display: none; }

/* ---------------------------------------------------------------- FAQ band
   🔴 Inverted from the first build to match the reference exactly. There the
   photo stays BRIGHT and a near-white panel is laid over the middle of it,
   holding a centred black heading and a stack of dark accordion bars. The
   first build darkened the whole photo to 85% black and set the questions in
   white on hairline rules - the same content, and nothing about it recognisable
   as the same design. */
.faq-band {
  position: relative;
  isolation: isolate;
  background: var(--dark);
  color: var(--ink);
  padding-block: clamp(50px, 7vw, 100px);
}
.faq-band__bg {
  position: absolute; inset: 0; z-index: -2;
  background-image: url('/assets/img/bg-faq-rural-fencing.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* background-attachment: fixed is skipped on touch devices - iOS renders it at
   the wrong scale and it costs a repaint on every scroll frame. */
@media (hover: hover) and (min-width: 900px) {
  .faq-band__bg { background-attachment: fixed; }
}
/* A light scrim only - just enough to hold the panel off a busy photograph. */
.faq-band::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: rgba(0,0,0,.28);
}

/* The panel. Near-white and translucent, so the photograph still reads behind
   it, and centred in the band. */
.faq {
  max-width: 960px;
  margin-inline: auto;
  background: rgba(255,255,255,.93);
  padding: clamp(28px, 4.5vw, 56px) clamp(20px, 3.5vw, 44px);
  text-align: center;
}
.faq-band h2, .faq-band h3 { color: var(--ink); }
.faq > h2 { font-size: clamp(1.55rem, 3vw, 2.3rem); margin-bottom: 1em; }
.faq > .eyebrow { color: var(--ink-mute); }

/* Each question is a solid dark BAR, not a hairline rule. Closed bars are
   #333334, the open one goes to pure black - that step is how the reference
   shows which is expanded, and it is only visible if the closed state is not
   already black. */
.faq details { margin-bottom: 12px; text-align: left; }
.faq summary {
  cursor: pointer;
  list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: 18px;
  padding: 15px 22px;
  background: var(--dark);
  font-family: var(--font-body); font-weight: 400;
  font-size: clamp(1rem, 1.4vw, 1.08rem);
  color: #fff;
  transition: background .16s ease;
}
.faq summary:hover { background: #222; }
.faq details[open] summary { background: #000; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+';
  flex: 0 0 auto;
  font-size: 1.4rem; line-height: 1; color: #fff;
}
.faq details[open] summary::after { content: '\2212'; }   /* a true minus sign */
.faq details p, .faq details ul { color: var(--ink); font-size: .99rem; }
.faq details > *:not(summary) { margin: 1em 0 0; padding-inline: 22px; }
.faq details > *:last-child { margin-bottom: 22px; }

/* ---------------------------------------------------------------- areas */
.areas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 14px;
}
.area {
  background: #fff;
  border: 0;
  border-left: 3px solid var(--ink);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.sec--dark .area { background: rgba(255,255,255,.08); border-left-color: #fff; color: #fff; }
.area h3 { font-size: 1.05rem; margin-bottom: .35em; }
.area p { font-size: .93rem; color: var(--ink-soft); margin: 0; }

/* ---------------------------------------------------------------- enquiry
   The reference's pre-footer block: a WHITE section, the brand and a big
   heading down the left, and the form in a solid #333334 panel on the right
   with plain white fields inside it. The first build had it the other way
   round - a light grey section with a white card - which is the same
   arrangement wearing none of the same clothes. */
.enq { background: #fff; padding-block: var(--sec-y); }
.enq__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .95fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}
@media (max-width: 860px) { .enq__grid { grid-template-columns: 1fr; } }
.enq__copy p { color: var(--ink-soft); }
.enq__copy h2 { font-size: clamp(1.7rem, 3.2vw, 2.5rem); }
.enq__big {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-head); font-weight: 700;
  font-size: clamp(1.4rem, 3vw, 2rem);
  color: var(--ink) !important;
  text-decoration: none !important;
  margin-bottom: .5em;
}
.enq__big:hover { text-decoration: underline !important; }
.enq__facts { list-style: none; margin: 1.2em 0; padding: 0; }
.enq__facts li {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 10px 0; border-bottom: 1px solid var(--line);
  font-size: .97rem;
}
.enq__facts svg { flex: 0 0 auto; margin-top: 4px; color: var(--ink); }
.enq__note { font-size: .87rem; color: var(--ink-mute); }

/* The Tradify enquiry panel that replaced the on-site form (2026-08-14). It deliberately keeps
   the dark card the form used, so the section's weight and the page rhythm do not change - the
   only thing that changed is what is inside it.
   🔴 Every colour is stated outright rather than inherited. This panel is #333334 and the site's
   global link colour is #000000; anything left to inherit here renders black on dark grey, which
   is the exact defect the client reported on the callout boxes. */
.enq__panel {
  background: var(--dark);
  color: #fff;
  border-radius: var(--radius);
  padding: clamp(24px, 3.2vw, 38px);
}
.enq__panel h3 {
  color: #fff;
  font-size: clamp(1.25rem, 2.2vw, 1.6rem);
  margin-bottom: .55em;
}
.enq__panel > p { color: rgba(255,255,255,.88); font-size: .99rem; }
.enq__go {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  width: 100%;
  margin: 1.5em 0 1.1em;
  padding: 17px 26px;
  background: #fff;
  color: #000 !important;
  font-family: var(--font-head); font-weight: 700;
  font-size: 1.06rem;
  line-height: 1.2;
  text-decoration: none !important;
  border: 2px solid #fff;
  transition: background .18s ease, color .18s ease;
}
.enq__go svg { flex: 0 0 auto; transition: transform .18s ease; }
.enq__go:hover, .enq__go:focus-visible {
  background: transparent;
  color: #fff !important;
}
.enq__go:hover svg { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) { .enq__go, .enq__go svg { transition: none; } }
.enq__or { margin: 0; font-size: .95rem; color: rgba(255,255,255,.88); }
.enq__or a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.enq__legal { font-size: .8rem; color: rgba(255,255,255,.66); margin-top: 1.2em; margin-bottom: 0; }

.form {
  background: var(--dark);
  border: 0;
  border-radius: var(--radius);
  padding: clamp(22px, 3vw, 34px);
  box-shadow: none;
  color: #fff;
}
.form__row { margin-bottom: 14px; }
.form label {
  display: block; font-weight: 400; font-size: .9rem;
  margin-bottom: 6px; color: #fff;
}
/* 🔴 Every colour is stated outright. A field that inherits its colour from the
   panel renders white-on-white and the form silently disappears while the panel
   around it still looks perfect - the failure that cost an afternoon on
   mortgage-iq. -webkit-text-fill-color goes alongside `color` because WebKit
   honours it in preference. */
.form input, .form select, .form textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 12px 14px;
  border: 1px solid #fff;
  border-radius: var(--radius);
  background: #fff;
  color: #000;
  -webkit-text-fill-color: #000;
}
.form ::placeholder { color: #6a6a6a; opacity: 1; }
.form textarea { resize: vertical; min-height: 118px; }
.form input:focus, .form select:focus, .form textarea:focus {
  outline: none; border-color: #fff;
  box-shadow: 0 0 0 3px rgba(255,255,255,.45);
}
/* White submit on the dark panel, as the reference's SEND. */
.form .btn {
  width: 100%; margin-top: 6px;
  background: #fff; border-color: #fff; color: #000;
}
.form .btn:hover, .form .btn:focus-visible {
  background: var(--band); border-color: var(--band); color: #000;
}
.err { display: block; color: #ffb4b0; font-size: .85rem; margin-top: 5px; }
.form__msg { margin: 12px 0 0; font-size: .93rem; min-height: 1.2em; color: #fff; }
.form__msg.err { color: #ffb4b0; }
.form__legal { font-size: .8rem; color: rgba(255,255,255,.72); margin-top: 14px; }

/* Honeypot. Off-canvas, not display:none and not type=hidden - capable bots
   skip both. The server check is the real one; this only catches the crude. */
.hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px; overflow: hidden;
}

/* ---------------------------------------------------------------- footer
   #333334, three columns - contact details as an icon list, a link column
   whose rows are separated by hairline rules, and a paragraph about the
   business - then a full-width service-areas line, then a PURE BLACK strip
   holding one line of small centred credit text. The black strip is what ends
   the reference page, and leaving it off (as the first build did) is
   surprisingly noticeable. */
.ftr { background: var(--dark); color: rgba(255,255,255,.86); padding-top: clamp(40px, 5vw, 66px); }
.ftr__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(255px, 1fr));
  gap: clamp(26px, 3.5vw, 54px);
  padding-bottom: clamp(24px, 3vw, 34px);
}
.ftr h3 {
  color: #fff; font-size: 1.28rem; letter-spacing: 0;
  text-transform: none; margin-bottom: .8em;
}
.ftr p { font-size: .96rem; color: rgba(255,255,255,.86); }
.ftr a { display: block; padding: 5px 0; font-size: .96rem; color: rgba(255,255,255,.9); }
.ftr a:hover, .ftr a:focus-visible { color: #fff; text-decoration: underline; }
/* The logo at the foot of the About Us column, per the client's "add our logo to
   the right-side bottom below about us". Sized by WIDTH here, not height: this
   is a prose column with a definite width, where the header is a definite-height
   band. Capped at 240px so it never outgrows the 255px minimum column. */
.brand__logo--ftr {
  width: min(240px, 100%);
  height: auto;
  margin-top: 20px;
  transition: none;
}

/* The link column: a rule under each row, none under the last. */
.ftr__links a {
  padding: 11px 4px 11px 10px;
  border-bottom: 1px solid rgba(255,255,255,.28);
}
.ftr__links a:last-child { border-bottom: 0; }

/* The contact column: a glyph, then the value, aligned on the first line. */
.ftr__contact { list-style: none; margin: 0 0 14px; padding: 0; }
.ftr__contact li { display: flex; gap: 13px; align-items: flex-start; padding: 7px 0; font-size: .96rem; }
.ftr__contact svg { flex: 0 0 auto; margin-top: 4px; color: #fff; }
.ftr__contact a { display: inline; padding: 0; }
.ftr__social { display: flex; gap: 12px; margin-top: 10px; }
.ftr__social a { display: inline-flex; padding: 0; color: #fff; }
.ftr__social a:hover { opacity: .75; text-decoration: none; }

.ftr__areas-row { padding-bottom: clamp(28px, 4vw, 44px); }
.ftr__areas-row h3 { margin-bottom: .55em; }
.ftr__areas { font-size: .96rem; color: rgba(255,255,255,.86); }
/* .ftr a is display:block for the link column; this one runs inside a sentence. */
.ftr__areas a { display: inline; padding: 0; text-decoration: underline; }

/* The black strip. Full-bleed, so it needs its own background rather than
   living inside .ftr's padding. */
.ftr__btm-wrap { background: #000; }
.ftr__btm {
  display: flex; flex-wrap: wrap; gap: 6px 22px;
  justify-content: center; align-items: center; text-align: center;
  padding-block: 16px;
  font-size: .82rem;
  color: rgba(255,255,255,.8);
}
/* Fleet standard: the WHOLE phrase is the link, underlined on hover only, so
   it reads as a credit rather than competing with the footer nav. */
.ftr__credit { display: inline !important; padding: 0 !important; color: rgba(255,255,255,.8) !important; }
.ftr__credit:hover, .ftr__credit:focus-visible { color: #fff !important; text-decoration: underline; }

/* ---------------------------------------------------------------- float bar
   Fleet component: expandable icon stack, lower right. Every link and colour
   comes from /dash, and a blank field renders no icon at all - the bar is only
   ever as big as what the client filled in. */
.floatbar {
  position: fixed;
  right: clamp(12px, 2vw, 22px);
  bottom: clamp(12px, 2vw, 22px);
  z-index: 95;
  display: flex; flex-direction: column-reverse; align-items: center; gap: 9px;
  --fb-bg: #000000;
  --fb-icon: #ffffff;
  --fb-hover-bg: #333334;
  --fb-hover-icon: #ffffff;
}
.floatbar__btn {
  width: 50px; height: 50px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--fb-bg);
  color: var(--fb-icon);
  border: 0; cursor: pointer;
  box-shadow: 0 3px 12px rgba(0,0,0,.28);
  transition: background .16s ease, color .16s ease, transform .16s ease;
  text-decoration: none;
}
.floatbar__btn:hover, .floatbar__btn:focus-visible {
  background: var(--fb-hover-bg);
  color: var(--fb-hover-icon);
  transform: scale(1.08);
  text-decoration: none;
}
.floatbar__toggle { order: 1; }
.floatbar__list { display: flex; flex-direction: column-reverse; gap: 9px; }
/* Collapse is scoped to .cnc-js so a visitor with no JS gets the full stack
   rather than an empty bar. */
.cnc-js .floatbar:not([data-open]) .floatbar__list { display: none; }
/* Restated AFTER the collapse rule: an equal-specificity `transform: none`
   later in the file silently killed the hover scale on the reference build. */
.floatbar__list .floatbar__btn:hover,
.floatbar__list .floatbar__btn:focus-visible { transform: scale(1.08); }

.fb-pop {
  position: fixed; inset: 0; z-index: 300;
  display: none; place-items: center;
  background: rgba(10,10,10,.7);
  padding: 20px;
}
.fb-pop[data-open] { display: grid; }
.fb-pop__card {
  position: relative;
  background: #fff; border-radius: var(--radius);
  padding: 34px 40px; text-align: center; max-width: 380px;
}
.fb-pop__card p { color: var(--ink-soft); font-size: .95rem; }
.fb-pop__num {
  display: block; font-family: var(--font-head); font-weight: 700;
  font-size: clamp(1.5rem, 5vw, 2.1rem); color: var(--ink); margin-bottom: .3em;
  text-decoration: none;
}
.fb-pop__close {
  position: absolute; top: 8px; right: 8px;
  width: 36px; height: 36px; border: 0; background: transparent;
  font-size: 1.4rem; line-height: 1; cursor: pointer; color: var(--ink-mute);
}

/* ---------------------------------------------------------------- reveal
   threshold: 0 with rootMargin only. A ratio threshold never fires for an
   element taller than about 16x the viewport, and a service page's copy column
   is routinely 4,000px+ - so the whole body would sit at opacity 0 until the
   visitor happened to scroll. Scoped to .cnc-js so crawlers and no-JS visitors
   always see everything. */
.cnc-js [data-reveal] { opacity: 0; transform: translateY(18px); }
.cnc-js [data-reveal].is-in {
  opacity: 1; transform: none;
  transition: opacity .55s ease, transform .55s ease;
}
.cnc-js [data-reveal][data-delay="1"].is-in { transition-delay: .12s; }
.cnc-js [data-reveal][data-delay="2"].is-in { transition-delay: .24s; }
@media (prefers-reduced-motion: reduce) {
  .cnc-js [data-reveal] { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------- print */
@media print {
  .hdr, .floatbar, .enq, .lb, .fb-pop { display: none !important; }
  body { font-size: 12pt; }
  .hero::after { display: none; }
}
