/* ==========================================================================
   BEAUS MOWING
   Lawn & garden care, Semaphore SA
   Hand written CSS. Mobile first. No frameworks, no build step.

   DESIGN NOTE
   Sections are flat colour, full stop. Solid --forest or --pine for the
   dark bands, solid --cream for the light ones, and nothing laid over
   either: no stripes, no grass, no pattern, no section animation. The
   video hero is the one moment of motion on the page, and everything
   after it is meant to be calm, so the work is done by colour, generous
   padding and strong type.

   Two lawn references survive, both outside the section grounds:
     1. .grass-edge, the organic silhouette that separates major
        sections, recoloured to suit each pair of bands
     2. Service cards get "mown" on hover, a stripe wiping left to right
   Earlier revisions striped the section grounds and later stood swaying
   grass along the dark bands. Both are gone. If a texture is ever
   reintroduced, it belongs behind the type, not competing with it.

   CONTENTS
   00. Tokens
   01. Reset & base
   02. Typography helpers
   03. Mown stripe motif
   04. Buttons
   05. Image placeholder slots
   06. Nav
   07. Hero (full bleed photo)
   08. Pride band
   09. Services
   10. Before & after gallery
   11. Why Beau
   12. Service area
   13. Contact & form
   14. Footer
   15. Thanks page
   16. Motion & print
   ========================================================================== */


/* ==========================================================================
   00. TOKENS
   ========================================================================== */
:root {
  /* Brand palette */
  --forest: #1B4332;
  --pine:   #14532D;
  --lime:   #74C043;
  --mint:   #A7D96C;
  --cream:  #F5F2EC;
  --ink:    #10261C;

  /* Derived tints. Kept off pure white / pure black on purpose. */
  --cream-deep: #EDE8DE;   /* card grounds */
  --mint-wash:  #E9F1DC;   /* very soft mint */
  --line:       rgba(27, 67, 50, 0.16);
  --line-strong:rgba(27, 67, 50, 0.30);
  --line-light: rgba(245, 242, 236, 0.20);
  --pine-light: #2D6A3A;   /* hairline rules on the dark grounds */

  /* Type */
  --display: "Clash Display", "Trebuchet MS", Impact, sans-serif;
  --body:    "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;

  /* Fluid sizes */
  --fs-body:   1.0625rem;                             /* 17px floor, older audience */
  --fs-lede:   clamp(1.125rem, 0.5vw + 1rem, 1.3125rem);
  --fs-h1:     clamp(2.6rem, 6vw, 4.75rem);
  --fs-h2:     clamp(2rem, 3.2vw + 1rem, 3.5rem);
  --fs-h3:     clamp(1.1875rem, 0.6vw + 1rem, 1.375rem);
  --fs-eyebrow:0.8125rem;

  /* Rhythm */
  --wrap:      1200px;
  --gutter:    clamp(1.25rem, 4vw, 2.5rem);
  --section-y: clamp(4.5rem, 9vw, 8rem);
  --radius:    6px;                                    /* never pills */
  --nav-h:     90px;
}


/* ==========================================================================
   01. RESET & BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 16px);       /* sticky nav clearance */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg { max-width: 100%; display: block; }
img { height: auto; }

ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--forest); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--pine); }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
  color: var(--forest);
}

/* Visible, on brand focus ring for keyboard users */
:focus-visible {
  outline: 3px solid var(--lime);
  outline-offset: 3px;
  border-radius: 2px;
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--forest);
  color: var(--cream);
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius);
}
.skip-link:focus { left: 1rem; top: 1rem; color: var(--cream); }


/* ==========================================================================
   02. TYPOGRAPHY HELPERS
   ========================================================================== */
.eyebrow {
  display: block;
  font-family: var(--body);
  font-weight: 700;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest);
  margin: 0 0 1rem;
}
.eyebrow--lime    { color: var(--lime); }
.eyebrow--light   { color: var(--mint); }
.eyebrow--onphoto { color: var(--mint); }

/* Wide enough that the longest section headline holds its two
   intended lines. The lede below is capped separately so body copy
   never runs to an uncomfortable measure. */
.section__head { max-width: 52rem; margin-bottom: clamp(2.5rem, 4vw, 3.75rem); }
.section__title { font-size: var(--fs-h2); margin-bottom: 0; }
.section__lede {
  font-size: var(--fs-lede);
  color: rgba(16, 38, 28, 0.78);
  margin-top: 1.25rem;
  max-width: 34rem;
}

/* Centred variant. Services uses this to break the page rhythm. */
.section__head--centre { margin-inline: auto; text-align: center; }
.section__head--centre .section__lede { margin-inline: auto; }

/* Line break that only applies on wider screens */
.br-md { display: none; }
@media (min-width: 700px) { .br-md { display: inline; } }


/* ==========================================================================
   03. MOWN STRIPE MOTIF
   ========================================================================== */

/* --- 3a. Section ground texture -------------------------------------------
   Retired. Every section is flat colour, light and dark alike. */

/* --- 3b. Divider ----------------------------------------------------------
   One organic cut grass edge, reused at every major section change.
   The silhouette lives in the SVG sprite at the top of index.html;
   these rules only size and colour it.

   Colour works in pairs, not by flipping the shape. Where the dark
   band is BELOW, the shape is filled dark over a transparent ground.
   Where the dark band is ABOVE, the wrapper takes the dark colour and
   the shape is filled cream, so the same silhouette reads as grass
   standing against the dark. One path, four transitions. */
.svg-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.grass-edge {
  display: block;
  width: 100%;
  height: clamp(30px, 3.6vw, 54px);
  line-height: 0;          /* no inline gap under the svg */
  overflow: hidden;

  /* Seam guard. Section boundaries land on fractional pixels as the
     page scrolls (measured between .17 and .78 of a pixel), and at
     those positions neither the divider's last row nor the next
     band's first row paints the shared row fully, so a hairline of
     whatever sits behind shows through.

     Overlapping by 2px deletes the shared edge rather than trying to
     align it. Safe in both directions because the 2px being covered
     is always the same colour as the thing covering it: the fill
     matches the band below, and the wrapper matches the band above. */
  margin-top: -2px;
  margin-bottom: -2px;
}
.grass-edge svg {
  display: block;
  width: 100%;
  height: 100%;
  vertical-align: bottom;  /* redundant while display is block, kept
                              so the seam cannot come back if the
                              display value is ever changed */
}

/* --- 3b-ii. The slow wave -------------------------------------------------
   Each divider stacks the same silhouette three times. The layers sit at
   slightly different heights, carry different opacities, and drift at
   different speeds, so the edge reads as depth rather than as one shape
   sliding along. Only the front layer is opaque, and it is painted last,
   so everything below the front crest line is solid colour and the seam
   fix still holds. The translucent layers are visible only where they
   rise above it, which is what gives the movement its softness.

   Two animations per layer, kept on separate elements because both drive
   transform: the outer group drifts sideways, the inner group breathes
   up and down. Different periods on each means the two never sync into
   an obvious loop.

   Timings are deliberately long. The fastest layer takes 54 seconds to
   travel one full silhouette width, which at a glance is barely motion
   at all, and that is the point. */
.grass-wave { animation: grassDrift 54s linear infinite; }
.grass-wave__sway { animation: grassSway 12s ease-in-out infinite alternate; }

.grass-wave--back  { opacity: 0.30; animation-duration: 96s; }
.grass-wave--mid   { opacity: 0.52; animation-duration: 72s; }
.grass-wave--front { opacity: 1;    animation-duration: 54s; }

.grass-wave--back  .grass-wave__sway { animation-duration: 19s; }
.grass-wave--mid   .grass-wave__sway { animation-duration: 15s; }

/* Promoted only while actually running, so paused dividers further down
   the page are not holding compositor layers open. */
.grass-edge:not(.is-paused) .grass-wave,
.grass-edge:not(.is-paused) .grass-wave__sway { will-change: transform; }

/* script.js parks whatever is off screen. Roughly two dividers are ever
   in view, so this keeps about ten of the fifteen layers idle. */
.grass-edge.is-paused .grass-wave,
.grass-edge.is-paused .grass-wave__sway { animation-play-state: paused; }

/* One silhouette width. The path tiles at exactly 1440, so this loops
   without a jump. */
@keyframes grassDrift {
  from { transform: translateX(0); }
  to   { transform: translateX(-1440px); }
}

/* Barely over two units of lift, which lands near two screen pixels once
   the viewBox is scaled down to the divider height. */
@keyframes grassSway {
  from { transform: translateY(0); }
  to   { transform: translateY(-2.5px); }
}

/* Mobile note: the back layer comes off below 700px. Three drifting
   layers on a phone buys detail nobody can see at that size and costs
   battery to composite. */
@media (max-width: 699px) {
  .grass-wave--back { display: none; }
}

/* Light ground above, dark band below */
.grass-edge--forest { color: var(--forest); }
.grass-edge--pine   { color: var(--pine); }

/* Dark band above, cream below */
.grass-edge--pine-to-cream   { background: var(--pine);   color: var(--cream); }
.grass-edge--forest-to-cream { background: var(--forest); color: var(--cream); }
/* Two dark greens meeting. Subtle by design: enough of a shift that the
   contact section and the footer do not read as one undifferentiated
   dark block, without a hard line between them. */
.grass-edge--forest-to-pine  { background: var(--forest); color: var(--pine); }
.grass-edge--pine-to-forest  { background: var(--pine);   color: var(--forest); }

/* Note: the hero deliberately has no divider at all. It ends on a
   straight edge and runs directly into the pride band. */


/* --- 3d. Section texture --------------------------------------------------
   Deliberately none. Every band is a flat colour: dark sections are solid
   --forest or --pine, light sections solid --cream. No stripes, no grass,
   no pattern, no animation. The video hero is the only moving thing on
   the page, and the sections carry themselves on colour and type. */

/* --- 3c. Mown circle behind the logo badge -------------------------------
   Retired now that the real logo is in. The supplied artwork already
   carries its own circular crest, so a second ring behind it just
   muddied the mark. Kept here as a note rather than deleted, in case
   a badge-only lockup lands later and wants it back. */


/* ==========================================================================
   04. BUTTONS
   Flat, 6px corners, no shadows, no gradients.
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--body);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.2;
  text-decoration: none;
  padding: 0.875rem 1.375rem;
  border-radius: var(--radius);
  border: 2px solid transparent;
  cursor: pointer;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease;
}

.btn--lime {
  background: var(--lime);
  color: var(--ink);
  border-color: var(--lime);
}
.btn--lime:hover,
.btn--lime:focus-visible {
  background: var(--forest);
  border-color: var(--forest);
  color: var(--cream);
}

.btn--outline {
  background: transparent;
  color: var(--forest);
  border-color: var(--forest);
}
.btn--outline:hover,
.btn--outline:focus-visible {
  background: var(--forest);
  color: var(--cream);
}

/* Outline button sitting on the hero photo. Solid cream border so it
   never gets lost against a busy image. */
.btn--onphoto {
  background: rgba(16, 38, 28, 0.30);
  color: var(--cream);
  border-color: var(--cream);
}
.btn--onphoto:hover,
.btn--onphoto:focus-visible { background: var(--cream); color: var(--forest); }

/* Reversed variant for dark bands */
.btn--cream {
  background: var(--cream);
  color: var(--forest);
  border-color: var(--cream);
}
.btn--cream:hover,
.btn--cream:focus-visible { background: var(--lime); border-color: var(--lime); color: var(--ink); }

.btn--lg { padding: 1.0625rem 1.75rem; font-size: 1.0625rem; }
.btn--block { width: 100%; }


/* ==========================================================================
   05. IMAGE PLACEHOLDER SLOTS
   Every slot shows its filename until the real photo lands.
   script.js removes any <img> that fails to load so the label stays
   clean instead of showing a broken image icon.
   ========================================================================== */
.slot {
  position: relative;
  display: block;
  overflow: hidden;
  margin: 0;
  background-color: var(--mint-wash);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
}

/* Filename label sits behind the photo */
.slot::after {
  content: attr(data-slot-label);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--body);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest);
  opacity: 0.55;
  text-align: center;
  padding: 0.5rem;
  pointer-events: none;
}

.slot img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* Once the real image is in, drop the dashed placeholder styling */
.slot.is-filled {
  border-style: solid;
  border-color: transparent;
  background-color: transparent;
  background-image: none;
}
.slot.is-filled::after { display: none; }

.slot--dark { background-color: rgba(245, 242, 236, 0.08); border-color: var(--line-light); }
.slot--dark::after { color: var(--cream); opacity: 0.6; }


/* ==========================================================================
   06. NAV
   ========================================================================== */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--cream);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease;
}
.nav.is-scrolled { border-bottom-color: var(--line); }

.nav__inner {
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 0.75rem var(--gutter);
  min-height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: 1rem;
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
  margin-right: auto;
}

/* Circular crest, vector. The source is a 1342x894 landscape canvas
   with the round badge centred and everything around it empty. A
   square box with object-fit:cover scales by height and trims those
   empty side margins, so the badge fills the box instead of the
   element being a third empty space. The crest renders about 54px
   inside a 56px box.

   The wordmark text beside it is not redundant at this size: the
   crest's own lettering is illegible under about 150px, so the text
   is what actually carries the brand name here. See the note in
   index.html. */
/* The crest is a dense illustration: ring text, laurel leaves, a figure
   with a mower, fence pickets, mown stripes. Rendered at 56px most of
   that detail fell under a single pixel and turned to grey haze, which
   is what made it look cheap. It needs size to hold together, so it runs
   at 68px with the bar grown to suit rather than being squeezed.
   geometricPrecision stops the browser trading accuracy for speed on
   the fine work. */
.nav__logo {
  display: block;
  width: 68px;
  height: 68px;
  flex: 0 0 auto;
  object-fit: cover;
  shape-rendering: geometricPrecision;
}

.nav__brand-text {
  font-family: var(--display);
  font-weight: 700;
  /* Scaled with the crest so the lockup stays balanced */
  font-size: 1.4375rem;
  letter-spacing: -0.01em;
  color: var(--forest);
  white-space: nowrap;
}

/* Hamburger */
.nav__toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 48px;
  height: 48px;
  padding: 0 11px;
  background: transparent;
  border: 2px solid var(--forest);
  border-radius: var(--radius);
  cursor: pointer;
}
.nav__toggle-bar {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--forest);
  transition: transform .2s ease, opacity .2s ease;
}
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile panel: full width drop down under the bar */
.nav__menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--cream);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 1rem var(--gutter) 1.75rem;
  display: none;
}
.nav__menu.is-open { display: block; }

.nav__links { display: flex; flex-direction: column; }
.nav__links a {
  display: block;
  padding: 0.875rem 0;
  font-weight: 500;
  font-size: 1.125rem;
  color: var(--forest);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.nav__links a:hover { color: var(--lime); }

.nav__actions {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  margin-top: 1.5rem;
}
.nav__phone {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.25rem;
  color: var(--forest);
  text-decoration: none;
}
.nav__phone:hover { color: var(--lime); }

/* Desktop nav */
@media (min-width: 1080px) {
  .nav__toggle { display: none; }
  .nav__menu {
    position: static;
    display: flex;
    align-items: center;
    gap: 2rem;
    background: transparent;
    border: 0;
    padding: 0;
  }
  .nav__links { flex-direction: row; gap: 1.75rem; }
  .nav__links a { padding: 0.25rem 0; border-bottom: 0; font-size: 1rem; }
  .nav__actions { flex-direction: row; align-items: center; gap: 1.25rem; margin-top: 0; }
  .nav__phone { font-size: 1.125rem; }
}

/* Mobile note: the lockup steps down a little at very narrow widths
   so it and the hamburger never collide. At 375px this leaves the
   logo at 100px wide against a 48px toggle inside a 335px row. */
@media (max-width: 400px) {
  .nav__logo { width: 58px; height: 58px; }
  .nav__brand-text { font-size: 1.125rem; }
  .nav__brand { gap: 0.5rem; }
}

/* The thanks page nav carries a phone button instead of a hamburger,
   which is wider. Below 430px the crest and the wordmark and that
   button no longer fit on one row, and flex was shrinking the button
   until its label wrapped onto two lines. The crest alone is enough
   identification on a confirmation page, so the wordmark steps aside
   there only. The main nav is untouched. */
.nav--compact .nav__inner .btn { white-space: nowrap; }

@media (max-width: 430px) {
  .nav--compact .nav__brand-text { display: none; }
}


/* ==========================================================================
   07. HERO (full bleed photo)
   ========================================================================== */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;      /* copy sits at the bottom */
  min-height: 88vh;
  /* Forest base means the hero is never a blank white box while the
     photo is loading, or if it never arrives. */
  background: var(--forest);
  padding-top: clamp(4rem, 12vw, 9rem);
  isolation: isolate;
}

/* Video layer. The poster also sits here as a background image, so
   there is always a still frame behind the scrim even if the video
   element never paints (failed load, unsupported codec, or the
   reduced motion path below where it is removed from the layout). */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  border: 0;
  border-radius: 0;
  background-color: var(--pine);
  background-image: url("poster.jpg");   /* PLACEHOLDER IMAGE: poster.jpg */
  background-size: cover;
  background-position: center;
}

.hero__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Reduced motion, or a video that cannot play: drop the element and
   let the poster background carry the hero. */
.hero.hero--still .hero__video,
.hero.hero--novideo .hero__video { display: none; }

/* Deep pine scrim, heaviest at the bottom left under the headline.
   This is one of only two gradients on the site.

   Weighted heavier than the photo version was: grass moving behind
   live text needs more protection than a static image does, since
   the local contrast under any given word keeps changing. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: linear-gradient(
    to top right,
    rgba(16, 38, 28, 0.96) 0%,
    rgba(20, 83, 45, 0.89) 34%,
    rgba(20, 83, 45, 0.64) 68%,
    rgba(27, 67, 50, 0.50) 100%
  );
}

/* Dev only: flags which asset is actually backing the hero when the
   video is not playing. Disappears once the video runs. */
.hero--still .hero__scrim::after,
.hero--novideo .hero__scrim::after {
  content: "poster.jpg";
  position: absolute;
  top: 1.25rem;
  right: 1.25rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream);
  opacity: 0.45;
  border: 1px dashed var(--line-light);
  border-radius: var(--radius);
  padding: 0.375rem 0.625rem;
}

.hero__content {
  position: relative;
  z-index: 2;
  /* Sets how far the copy sits off the bottom edge of the hero.
     Nothing sits below it now, so this is the only thing holding the
     badge clear of the cut. */
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
}

.hero__title {
  font-size: var(--fs-h1);
  color: var(--cream);
  margin-bottom: 1.5rem;
}

.hero__sub {
  font-size: var(--fs-lede);
  color: rgba(245, 242, 236, 0.92);
  max-width: 36rem;
  margin-bottom: 2rem;
}

.hero__buttons { display: flex; flex-wrap: wrap; gap: 0.875rem; }

/* Mobile note: buttons go full width below 480px so neither one wraps
   its label onto two lines at 375px. */
@media (max-width: 479px) {
  .hero__buttons .btn { width: 100%; }
}

.hero__badge {
  display: inline-block;
  margin: 1.75rem 0 0;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--cream);
  border-left: 3px solid var(--lime);
  padding-left: 0.875rem;
}
.hero__badge strong {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--mint);
  margin-right: 0.25rem;
}

@media (min-width: 900px) {
  .hero { min-height: 90vh; }
}

/* Mobile note: the scrim gets heavier and more even at small sizes,
   where the text block covers most of the photo. */
@media (max-width: 767px) {
  .hero { min-height: 86vh; }
  .hero__scrim {
    background-image: linear-gradient(
      to top,
      rgba(16, 38, 28, 0.95) 0%,
      rgba(20, 83, 45, 0.88) 45%,
      rgba(20, 83, 45, 0.66) 100%
    );
  }
}


/* ==========================================================================
   08. PRIDE BAND
   ========================================================================== */
.pride {
  background-color: var(--pine);
  color: var(--cream);
  padding-block: clamp(3rem, 6vw, 5rem);
}
.pride__inner { display: grid; gap: 1.5rem; align-items: start; }
.pride__title {
  color: var(--cream);
  font-size: clamp(1.875rem, 3vw + 1rem, 3rem);
  margin: 0;
}
.pride__text {
  color: rgba(245, 242, 236, 0.86);
  font-size: var(--fs-lede);
  max-width: 36rem;
  margin: 0;
}
@media (min-width: 860px) {
  .pride__inner { grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center; }
}


/* ==========================================================================
   09. SERVICES
   ========================================================================== */
.grid { display: grid; gap: 1.25rem; }
.grid--services { grid-template-columns: 1fr; }

@media (min-width: 620px)  { .grid--services { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .grid--services { grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } }

.card {
  position: relative;
  overflow: hidden;                 /* keeps the mowing wipe inside the card */
  background: var(--cream-deep);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.75rem 1.5rem;
  transition: border-color .2s ease, transform .25s ease;
}

/* The mow: a stripe pass wipes across the card from the left */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: repeating-linear-gradient(
    96deg,
    rgba(167, 217, 108, 0.55) 0 24px,
    rgba(116, 192, 67, 0.30)  24px 48px
  );
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .5s cubic-bezier(.22, .61, .36, 1);
}
.card:hover::before,
.card:focus-within::before { transform: scaleX(1); }

/* Card content rides above the wipe */
.card > * { position: relative; z-index: 1; }

.card:hover,
.card:focus-within {
  border-color: var(--lime);
  transform: translateY(-3px);
}

.card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin-bottom: 1.125rem;
  border-radius: var(--radius);
  background: var(--forest);
  color: var(--mint);
  transition: background-color .25s ease, color .25s ease;
}
.card__icon svg { width: 26px; height: 26px; }
.card:hover .card__icon { background: var(--forest); color: var(--lime); }

.card__title {
  font-size: var(--fs-h3);
  margin-bottom: 0.4rem;
  color: var(--forest);
}
.card__text {
  margin: 0;
  color: rgba(16, 38, 28, 0.78);
  font-size: 1rem;
}

/* Specialty flag */
.card--flag { border-color: var(--lime); background: var(--mint-wash); }
.card__tag {
  position: absolute;
  top: 1.25rem;
  right: 1.25rem;
  z-index: 2;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--lime);
  padding: 0.25rem 0.5rem;
  border-radius: 3px;
}


/* ==========================================================================
   10. BEFORE & AFTER GALLERY
   Full bleed dark band. The proof moment gets the strongest ground.
   ========================================================================== */
.gallery {
  background-color: var(--forest);
  color: var(--cream);
}
.gallery .section__title { color: var(--cream); }

.gallery__intro { color: rgba(245, 242, 236, 0.82); }

/* Three transformations stacked one per row, with real air between
   them. The photos are the proof, so they get the width rather than
   being squeezed into a three across thumbnail strip. */
.ba-list { display: grid; gap: clamp(3rem, 6vw, 5.5rem); }

/* --- the connecting thread ----------------------------------------------
   One segment per section rather than a single page long line, because
   every section paints an opaque background. A single element behind all
   of them would either be covered by those backgrounds (z-index -1) or
   sit on top of the copy (z-index 0), with nothing in between.

   So each section owns a segment, drawn above its own background and
   below its own content. script.js chains them: each segment starts on
   the side the previous one finished, so they read as one line running
   the length of the page.

   Colour follows the ground it crosses, which is the whole point of
   splitting it up. Both variants are deliberately faint: this is a guide
   for the eye, not a feature. */
.has-thread { position: relative; }
.has-thread > .wrap { position: relative; z-index: 1; }

.thread {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;      /* the curve reaches the edges, never past them */
}
.thread__svg { width: 100%; height: 100%; display: block; }

.thread__path {
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;   /* the drawn curve is a dense polyline */
}

/* Light line on the dark grounds, dark line on cream. Set as `color` so
   the stroke can just take currentColor. */
.thread--dark  { color: rgba(167, 217, 108, 0.30); }
.thread--light { color: rgba(27, 67, 50, 0.26); }

/* Mobile note: the curve still runs corner to corner at 375px, where the
   gutters are only 20px, so it shows in a narrow band either side of the
   content rather than sweeping wide. It can never affect layout width at
   any size, being absolutely positioned inside a relative parent. */

.ba { margin: 0; }   /* no card chrome, the photos sit straight on the dark */

/* Caption leads the row, so you know what you are looking at before
   you look at it. Kept consistent on every row rather than alternating
   sides, which starts to feel fussy once the images are this large. */
.ba__head { margin-bottom: clamp(1rem, 2vw, 1.5rem); }

.ba__title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.375rem, 1.6vw + 1rem, 2rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--cream);
  margin: 0 0 0.375rem;
}
.ba__detail {
  margin: 0;
  font-size: 1rem;
  color: rgba(245, 242, 236, 0.72);
  max-width: 40rem;
}

.ba__frames {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
}

/* Square across the board. The source photos are mixed orientation,
   four portrait and two landscape once EXIF rotation is applied, so
   some cropping is unavoidable whatever ratio is chosen. Square is the
   fairest split: it keeps 75% of a portrait shot's height and 75% of a
   landscape shot's width, where a 4:3 frame kept only 56% of the
   portraits and cut the overgrowth that makes the before shots land. */
.ba__frame {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius);
  background: rgba(245, 242, 236, 0.06);   /* holds the shape while loading */
  border: 1px solid rgba(245, 242, 236, 0.10);
}

/* The faint lift on the result. A slightly brighter edge is enough to
   pull the eye to the after shot without colour coding the labels. */
.ba__frame--after { border-color: rgba(167, 217, 108, 0.38); }

/* cover, never contain: fills the frame without letterboxing, and
   never stretches whatever the source aspect happens to be. */
.ba__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Labels sit under the photo rather than on it: nothing overlaps the
   image, nothing needs a scrim to stay legible, and before and after
   are styled identically so neither shouts. */
.ba__label {
  margin: 0.75rem 0 0;
  font-family: var(--body);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(245, 242, 236, 0.66);
}

/* Mobile note: below 620px the pair stacks, before above after, each
   running the full column width. Side by side at 375px left each photo
   about 150px wide, which is too small to read a transformation in.
   Seeing the change matters more than the side by side arrangement. */
@media (max-width: 619px) {
  .ba__frames { grid-template-columns: 1fr; gap: 1rem; }
}


/* ==========================================================================
   11. WHY BEAU
   ========================================================================== */
/* One solid dark ground, no split, no floating card. Beau's line is the
   headline and everything else sits quietly under it. */
.why {
  background: var(--cream);
  color: var(--ink);
}
.why__inner { max-width: 56rem; }

/* --- the hero line ------------------------------------------------------ */
.why__quote { margin: 0; }

.why__line {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 4.4vw + 0.5rem, 4rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  /* Slightly lifted off full --forest so the payoff below can land as
     the darkest thing in the section. Still a strong dark green, well
     clear of any contrast floor at this size. */
  color: rgba(27, 67, 50, 0.80);
  margin: 0;
}
/* On cream the payoff goes DARKER rather than brighter. Lime on cream
   measures about 2.3:1 and reads washed out at display size, so the
   emphasis comes from weight of colour instead: the bulk sits just off
   --forest and the payoff drops to --ink, the darkest tone on the site.
   Tested both ways; matching the bulk to full --forest left the two
   indistinguishable, and this reads boldest. */
.why__punch { color: var(--ink); }

.why__attrib {
  display: inline-flex;
  align-items: center;
  margin-top: clamp(1.25rem, 2vw, 1.75rem);
  font-family: var(--body);
  font-style: normal;
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(27, 67, 50, 0.72);
}
/* Drawn rule instead of a typed dash, which keeps em dashes out of the
   copy while still reading as an attribution. */
.why__attrib::before {
  content: "";
  width: 1.75rem;
  height: 1px;
  margin-right: 0.75rem;
  background: var(--forest);
  opacity: 0.5;
}

/* --- supporting copy ---------------------------------------------------- */
.why__support {
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  max-width: 44rem;
  font-size: var(--fs-lede);
  color: rgba(16, 38, 28, 0.80);
}

/* --- stat row ----------------------------------------------------------- */
.why__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);   /* 2x2 on mobile */
  gap: clamp(1.75rem, 3vw, 2.5rem) 1.5rem;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2rem, 3.5vw, 3rem);
  border-top: 1px solid var(--line);
}
@media (min-width: 700px) {
  .why__stats { grid-template-columns: repeat(4, 1fr); }
}

.stat { padding: 0; border: 0; }

.stat__num {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  /* Medium, not a shouting dashboard. The row is meant to sit under the
     line, not compete with it. */
  font-size: clamp(1.75rem, 2.2vw + 1rem, 2.5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--forest);
}
/* Keeps the row from twitching while the count runs */
.stat__count { font-variant-numeric: tabular-nums; }
/* Lime is too pale to hold against cream, so the accent steps to the
   mid green, which keeps it clearly green and still readable. */
.stat__plus { color: var(--pine-light); }

/* "Ray White" is words, not a figure, so it steps down to sit level with
   the numbers rather than out-measuring them. */
.stat__num--word { font-size: clamp(1.375rem, 1.4vw + 1rem, 1.875rem); }

.stat__label {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  /* 0.70 not 0.62: at 14px these labels are normal sized text, and 0.62
     measured 4.38:1 on cream, just under the 4.5:1 floor. */
  color: rgba(16, 38, 28, 0.70);
}

/* --- single CTA --------------------------------------------------------- */
.why__cta { margin-top: clamp(2.5rem, 5vw, 3.5rem); }


/* ==========================================================================
   11b. MOWING BAND (full bleed video breather)
   Butts straight against Why Beau above and the service area below.
   No dividers, no margins, and the video is display:block so there is
   no inline baseline gap under it.
   ========================================================================== */
.mowband {
  position: relative;
  display: flex;
  align-items: flex-end;
  /* Substantial enough to read as a pause rather than a strip. */
  min-height: 52vh;
  margin: 0;
  overflow: hidden;
  /* Never a blank white box while the video loads */
  background: var(--pine);
  isolation: isolate;
}

.mowband__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-color: var(--pine);
  background-image: url("mow-poster.jpg");   /* PLACEHOLDER IMAGE: mow-poster.jpg */
  background-size: cover;
  background-position: center;
}

.mowband__video {
  display: block;            /* kills the inline baseline sliver */
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Reduced motion, or a video that cannot play: drop the element and
   let the poster background carry the band. */
.mowband--still .mowband__video,
.mowband--novideo .mowband__video { display: none; }

/* Lighter than the hero scrim on purpose. The mowing is the point
   here, so this only darkens enough to hold the one line of text,
   with a little more weight at the bottom where that text sits. */
.mowband__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: linear-gradient(
    to top,
    rgba(16, 38, 28, 0.62) 0%,
    rgba(20, 83, 45, 0.34) 45%,
    rgba(20, 83, 45, 0.20) 100%
  );
}

.mowband__inner {
  position: relative;
  z-index: 2;
  padding-block: clamp(2rem, 4vw, 3.5rem);
}

.mowband__line {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.875rem, 4vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--cream);
}

@media (min-width: 900px) {
  .mowband { min-height: 64vh; }
}

/* Mobile note: the band keeps a real presence at 375px rather than
   collapsing to a strip, and the scrim firms up because the text sits
   over a larger share of the frame at that size. */
@media (max-width: 767px) {
  .mowband { min-height: 50vh; }
  .mowband__scrim {
    background-image: linear-gradient(
      to top,
      rgba(16, 38, 28, 0.70) 0%,
      rgba(20, 83, 45, 0.42) 50%,
      rgba(20, 83, 45, 0.26) 100%
    );
  }
}


/* ==========================================================================
   12b. FAQ
   Plain rows on a hairline rule rather than boxes. The section sits
   between two other cream bands, so structure does the separating.
   ========================================================================== */
/* Dark ground. --pine rather than --forest deliberately: the contact
   section below is already --forest, and two identical greens meeting
   would read as one slab. Running pine here, forest for contact and pine
   again for the footer means every boundary in the dark run has a real
   tonal step, without recolouring anything else. */
.faq {
  background: var(--pine);
  color: var(--cream);
}
.faq .section__title { color: var(--cream); }

.faq__list { max-width: 52rem; border-top: 1px solid var(--pine-light); }
.faq__item { border-bottom: 1px solid var(--pine-light); }
.faq__q { margin: 0; }

.faq__btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  width: 100%;
  padding: 1.375rem 0;
  background: none;
  border: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.0625rem, 0.8vw + 0.9rem, 1.375rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--cream);
  text-align: left;
  cursor: pointer;
  transition: color .18s ease;
}
.faq__btn:hover,
.faq__btn:focus-visible { color: var(--lime); }

/* Plus that loses its upright stroke when open. The only moving part,
   and the global reduced motion rule flattens its transition. */
.faq__icon { position: relative; flex: 0 0 auto; width: 20px; height: 20px; }
.faq__icon::before,
.faq__icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--lime);
  transform: translate(-50%, -50%);
}
.faq__icon::before { width: 16px; height: 2px; }
.faq__icon::after  { width: 2px; height: 16px; transition: transform .2s ease; }
.faq__btn[aria-expanded="true"] .faq__icon::after { transform: translate(-50%, -50%) scaleY(0); }

.faq__a { padding: 0 0 1.5rem; max-width: 44rem; }
.faq__a p { margin: 0; color: var(--mint); }

/* Mobile note: the question and the plus sit on one row at 375px, with
   the question wrapping to as many lines as it needs. The icon never
   shrinks, so the tap target stays put. */
@media (max-width: 479px) {
  .faq__btn { gap: 1rem; padding: 1.25rem 0; }
}


/* ==========================================================================
   12. SERVICE AREA
   ========================================================================== */
.areas__grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }

/* Map leads on desktop and takes the larger share; it is the feature.
   Stacks with the map on top on mobile. */
@media (min-width: 900px) {
  .areas__grid { grid-template-columns: 1.35fr 0.65fr; gap: clamp(2.5rem, 4vw, 4rem); }
}

/* --- the map panel ------------------------------------------------------ */
.map {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  /* barely there lift, just enough to read as a panel on the cream */
  box-shadow: 0 1px 2px rgba(16, 38, 28, 0.05);
}
.map__svg { display: block; width: 100%; height: auto; }

/* Abstract streets. Low contrast on purpose: texture, not information. */
.map__roads path {
  fill: none;
  stroke: var(--forest);
  stroke-opacity: 0.30;
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* The 30km edge */
.map__ring {
  fill: none;
  stroke: var(--forest);
  stroke-opacity: 0.45;
  stroke-width: 1.5;
  stroke-dasharray: 7 7;
}

/* Suburb markers */
.map__dot { fill: var(--lime); stroke: #EFEADF; stroke-width: 2; }
.map__label {
  font-family: var(--body);
  font-size: 15px;
  font-weight: 500;
  fill: var(--forest);
}

/* Home base, the heaviest mark on the panel */
.map__home-ring { fill: var(--cream); }
.map__home-dot  { fill: var(--forest); }
.map__home-label {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 700;
  fill: var(--forest);
}
.map__home-sub {
  font-family: var(--body);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  fill: var(--forest);
  fill-opacity: 0.62;
}

/* Mobile note: the SVG scales as one piece, so label text shrinks with
   it and would turn to mush. Type is pushed back up, and the two labels
   that sit closest to their neighbours drop out entirely rather than
   collide. Every name is still in the list below, so nothing is lost. */
@media (max-width: 719px) {
  .map__label { font-size: 21px; }
  .map__home-label { font-size: 27px; }
  .map__home-sub { font-size: 15px; }
  /* Hide the whole marker, not just its label: a dot with no name
     beside it reads as an orphan rather than a place. */
  .map__place--minor { display: none; }
  .map__roads path { stroke-width: 2; }
}

/* --- map motion ---------------------------------------------------------
   Two touches, both slight. The coverage glow breathes on a very slow
   cycle, and the suburb markers fade in one after another once the map
   scrolls into view. Both are gated behind .js-motion, so with JS off or
   reduced motion set the map simply renders finished and still. */
.js-motion .map__glow {
  animation: mapBreathe 9s ease-in-out infinite alternate;
  transform-box: fill-box;
  transform-origin: center;
}
@keyframes mapBreathe {
  from { opacity: 0.72; transform: scale(0.97); }
  to   { opacity: 1;    transform: scale(1.03); }
}

.js-motion .map__place {
  opacity: 0;
  transition: opacity .5s ease;
}
/* The reveal class lands on .map, so the markers wait for the panel */
.js-motion .map.is-visible .map__place {
  opacity: 1;
  transition-delay: var(--d, 0ms);   /* staggered per marker in the markup */
}

/* --- the suburb list ---------------------------------------------------- */
.areas__list-title {
  font-family: var(--body);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest);
  margin: 0 0 1.25rem;
}

/* Two tidy columns, every entry identical. The previous version gave a
   scattered few a solid fill and a larger size, which read as meaning
   something it did not. */
.areas__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem 1.25rem;
}
@media (min-width: 900px) and (max-width: 1099px) {
  .areas__list { grid-template-columns: 1fr; }
}

.areas__list li {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink);
}
.areas__list li::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--lime);
  transform: translateY(-2px);
}

.areas__more {
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  font-size: 0.9375rem;
  color: rgba(16, 38, 28, 0.70);
}


/* ==========================================================================
   13. CONTACT & FORM
   The lead capture moment, so it gets the weight: solid --forest, full
   width, cream type. It also restores the light/dark rhythm at the base
   of the page, which had run to one long stretch of cream.
   ========================================================================== */
.contact {
  background: var(--forest);
  color: var(--cream);
  /* Tightened. The cream map section above and this one were each
     carrying a full --section-y, which stacked into a screen height of
     empty ground between the map and this headline. The colour change
     now does the separating, so the padding does not have to. */
  padding-top: clamp(3rem, 5vw, 5rem);
}
.areas { padding-bottom: clamp(2.5rem, 4vw, 4rem); }

.contact .section__title { color: var(--cream); }
.contact .section__lede  { color: var(--mint); }

.contact__grid { display: grid; gap: clamp(2.5rem, 4vw, 3.5rem); align-items: start; }
@media (min-width: 940px) { .contact__grid { grid-template-columns: 1.15fr 0.85fr; } }

/* No panel. The form sits straight on the dark ground and the fields
   themselves carry the contrast. */
.form { background: none; border: 0; padding: 0; }

/* Honeypot, hidden from people but present for Netlify */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__row { display: grid; gap: 1.25rem; margin-bottom: 1.25rem; }
@media (min-width: 560px) { .form__row { grid-template-columns: 1fr 1fr; } }

.field { display: block; margin-bottom: 1.25rem; }
.form__row .field { margin-bottom: 0; }

.field label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mint);
  margin-bottom: 0.5rem;
}

/* Cream fill on the dark ground: clean and inviting rather than a
   washed out outline. Ink text on cream keeps the contrast high. */
.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: var(--body);
  font-size: 1.0625rem;                 /* 17px stops iOS zooming on focus */
  line-height: 1.5;
  color: var(--ink);
  background: var(--cream);
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 0.9375rem 1rem;              /* a little roomier than before */
  -webkit-appearance: none;
  appearance: none;
}
.field textarea { resize: vertical; min-height: 8rem; }

/* Custom select arrow, drawn as an inline SVG data URI so there is no
   extra request and no library. */
.field select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%231B4332' stroke-width='1.6'%3E%3Cpath d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 12px 8px;
  padding-right: 2.75rem;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--lime);
  outline: 3px solid var(--lime);
  outline-offset: 1px;
}

.field input::placeholder,
.field textarea::placeholder { color: rgba(16, 38, 28, 0.45); }

.form__note {
  margin: 1rem 0 0;
  font-size: 0.9375rem;
  color: var(--mint);
  text-align: center;
}

/* --- the aside --------------------------------------------------------- */
.contact__aside-title {
  font-size: var(--fs-h3);
  color: var(--cream);
  margin-bottom: 1.5rem;
}

.details { margin: 0 0 2rem; }
.details dt {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mint);
  opacity: 0.85;
  margin-bottom: 0.25rem;
}
.details dd {
  margin: 0 0 1.25rem;
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--cream);
}
.details dd a {
  color: var(--lime);
  text-decoration: none;
  border-bottom: 1px solid rgba(116, 192, 67, 0.45);
}
.details dd a:hover { color: var(--mint); border-bottom-color: var(--mint); }

.social { padding-top: 1.5rem; border-top: 1px solid var(--line-light); }
.social .eyebrow { color: var(--lime); margin-bottom: 0.875rem; }
/* One channel, so it reads as a proper link rather than a lone tag
   left over from a row. */
.social__link {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--cream);
  text-decoration: none;
  border: 1px solid var(--line-light);
  border-radius: var(--radius);
  padding: 0.6875rem 1rem;
}
.social__link:hover {
  background: var(--cream);
  color: var(--forest);
  border-color: var(--cream);
}
.social__icon { width: 18px; height: 18px; flex: 0 0 auto; }

.contact__trust {
  margin-top: 1.75rem;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--mint);
}


/* ==========================================================================
   14. FOOTER
   ========================================================================== */
.footer {
  background-color: var(--pine);
  color: var(--cream);
  padding-top: clamp(3rem, 6vw, 4.5rem);
}

.footer__grid {
  display: grid;
  gap: 2.5rem;
  padding-bottom: clamp(2.5rem, 5vw, 3.5rem);
}
@media (min-width: 640px)  { .footer__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 3rem; } }

@media (min-width: 640px) and (max-width: 999px) {
  .footer__brand { grid-column: 1 / -1; }
}

/* Footer logo. Same crest and same square crop as the nav, larger.

   The crest's outermost ring is #053d17, which against the --pine
   footer measures about 1.3:1, so that edge is effectively invisible
   and the badge loses its silhouette. A cream disc puts the edge back
   without introducing a shape the brand does not already use: the
   crest is round, so the panel is round too. */
/* Same reasoning as the nav: at 100px the ring text was still soft. The
   footer has the room, so the crest gets the size it needs to read. */
.footer__logo {
  display: block;
  width: 132px;
  height: 132px;
  object-fit: cover;
  margin-bottom: 1.25rem;
  background: var(--cream);
  border-radius: 50%;
  padding: 9px;
  shape-rendering: geometricPrecision;
}

.footer__blurb {
  color: rgba(245, 242, 236, 0.78);
  max-width: 26rem;
  font-size: 1rem;
}
.footer__trust {
  margin-top: 1rem;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--mint);
}

.footer__abn {
  margin: 0.75rem 0 0;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: rgba(245, 242, 236, 0.55);
}

.footer__heading {
  font-family: var(--body);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mint);
  margin-bottom: 1rem;
}
.footer__col ul { display: grid; gap: 0.625rem; }
.footer__col li { color: rgba(245, 242, 236, 0.82); font-size: 1rem; }
.footer__col a { color: rgba(245, 242, 236, 0.82); text-decoration: none; }
.footer__col a:hover { color: var(--lime); }

.footer__bar {
  border-top: 1px solid var(--line-light);
  padding-block: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-size: 0.875rem;
  color: rgba(245, 242, 236, 0.7);
}
.footer__bar p { margin: 0; }
.footer__bar a { color: rgba(245, 242, 236, 0.7); text-decoration: none; border-bottom: 1px solid var(--line-light); }
.footer__bar a:hover { color: var(--lime); border-bottom-color: var(--lime); }

@media (min-width: 700px) {
  .footer__bar { flex-direction: row; align-items: center; justify-content: space-between; }
}


/* ==========================================================================
   15. THANKS PAGE
   ========================================================================== */
.thanks {
  min-height: 70vh;
  display: flex;
  align-items: center;
  padding-block: clamp(4rem, 10vw, 8rem);
  background-color: var(--cream);
}
.thanks__inner { max-width: 36rem; }
.thanks__title { font-size: var(--fs-h2); margin-bottom: 1.25rem; }
.thanks__text { font-size: var(--fs-lede); color: rgba(16, 38, 28, 0.8); margin-bottom: 2rem; }
.thanks__buttons { display: flex; flex-wrap: wrap; gap: 0.875rem; }
@media (max-width: 479px) { .thanks__buttons .btn { width: 100%; } }

.thanks__tick {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: var(--radius);
  background: var(--lime);
  color: var(--forest);
  margin-bottom: 1.75rem;
}
.thanks__tick svg { width: 34px; height: 34px; }


/* ==========================================================================
   16. MOTION & PRINT

   Reveal animation is opt in from JS: the hidden state only applies
   once <html> has .js-motion. If JS fails, or the visitor has asked
   for reduced motion, everything renders visible in normal flow.
   ========================================================================== */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .55s ease, transform .55s cubic-bezier(.22, .61, .36, 1);
  will-change: opacity, transform;
}
.js-motion [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    /* Without this an infinite animation keeps looping, just too fast
       to see, which burns a frame budget forever instead of stopping */
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  /* The divider drift is decorative and endless, so it comes off
     entirely rather than being collapsed to a near zero duration.
     The dividers keep their shape and simply sit still. */
  .grass-wave, .grass-wave__sway, .map__glow { animation: none !important; }
  /* Markers render in place rather than fading in one by one */
  .map__place { opacity: 1 !important; transition: none !important; }
  .card:hover { transform: none; }
  .card:hover::before, .card:focus-within::before { transform: scaleX(0); }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}

@media print {
  .nav, .footer__bar, .hero__buttons, .form, .grass-edge { display: none; }
  body { background: #fff; color: #000; }
  .hero__scrim, .hero__bg { display: none; }
  .hero__title, .hero__sub { color: #000; }
}
