/* 0-Nine Lawn Care — palette lifted straight off the logo: a near-black
   ground, a chrome-lime accent, and one deeper grass green that is dark
   enough to carry white button text on a white page. */

:root{
  --ink:#0A0D0A;          /* logo ground: nav, footer, dark bands */
  --ink-2:#161B15;        /* raised surface on dark */
  --body:#232823;         /* body copy */
  --muted:#5C6B57;        /* captions, small print */
  --green:#2E7D14;        /* primary action + links (4.9:1 on white) */
  --green-dk:#20600C;     /* hover */
  --green-br:#7CD62F;     /* the logo lime. On dark only (9.6:1 on --ink) */
  --silver:#C9D2CE;       /* the chrome edge in the logo */
  --mint:#EAF7DA;         /* soft proof panels */
  --chalk:#F6F9F3;        /* page background / alternating bands */
  --white:#FFFFFF;
  --line:#DEE6DA;
  --radius:10px;
  --radius-lg:16px;
  --shadow:0 1px 2px rgba(16,18,16,.06), 0 10px 30px rgba(16,18,16,.08);
  --shadow-lg:0 2px 6px rgba(16,18,16,.08), 0 24px 60px rgba(16,18,16,.14);
  --wrap:1140px;
  --font:"Segoe UI",-apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif;
}

*,*::before,*::after{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip;}
body{
  margin:0;font-family:var(--font);font-size:16px;line-height:1.65;
  color:var(--body);background:var(--white);overflow-x:clip;
}
img{max-width:100%;height:auto;display:block;}
a{color:var(--green);text-decoration:none;}
a:hover{color:var(--green-dk);text-decoration:underline;}
h1,h2,h3,h4{color:var(--ink);margin:0 0 .6em;line-height:1.15;letter-spacing:-.02em;}
h1{font-size:clamp(2rem,5.2vw,3.25rem);font-weight:800;}
h2{font-size:clamp(1.55rem,3.4vw,2.25rem);font-weight:750;}
h3{font-size:1.15rem;font-weight:700;letter-spacing:-.01em;}
p{margin:0 0 1em;}
.wrap{width:100%;max-width:var(--wrap);margin:0 auto;padding:0 20px;}
.section{padding:72px 0;}
.section--tight{padding:52px 0;}
.section--alt{background:var(--chalk);}
.section--dark{background:var(--ink);color:#D8E2D9;}
.section--dark h2,.section--dark h3{color:#fff;}
.eyebrow{
  font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.16em;
  color:var(--green);margin:0 0 .8rem;
}
.section--dark .eyebrow{color:var(--green-br);}
.lede{font-size:1.075rem;color:var(--muted);max-width:62ch;}
.section--dark .lede{color:#AFC0B2;}
.center{text-align:center;}
.center .lede{margin-left:auto;margin-right:auto;}
.skip{position:absolute;left:-9999px;}
.skip:focus{left:8px;top:8px;z-index:200;background:#fff;padding:10px 14px;border-radius:8px;}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:.5rem;justify-content:center;
  font:inherit;font-weight:700;font-size:1rem;line-height:1.2;
  padding:14px 24px;border-radius:var(--radius);border:2px solid transparent;
  cursor:pointer;text-decoration:none;transition:background .15s,color .15s,border-color .15s;
}
.btn:hover{text-decoration:none;}
.btn--primary{background:var(--green);color:#fff;}
.btn--primary:hover{background:var(--green-dk);color:#fff;}
.btn--dark{background:var(--ink);color:#fff;}
.btn--dark:hover{background:#000;color:#fff;}
.btn--ghost{background:transparent;color:var(--ink);border-color:var(--line);}
.btn--ghost:hover{border-color:var(--ink);color:var(--ink);}
/* Ghost buttons default to dark ink for light sections. Every dark-background
   block must be listed here or the button renders near-black on near-black.
   The hero and .cta-band were missing, which hid the Call button - the main
   action on the site. Add any new dark block to this list. */
.section--dark .btn--ghost,
.hero .btn--ghost,
.cta-band .btn--ghost{color:#fff;border-color:rgba(255,255,255,.5);}
.section--dark .btn--ghost:hover,
.hero .btn--ghost:hover,
.cta-band .btn--ghost:hover{border-color:#fff;color:#fff;background:rgba(255,255,255,.12);}
.btn--lg{padding:16px 30px;font-size:1.05rem;}
.btn-row{display:flex;flex-wrap:wrap;gap:12px;}
.center .btn-row{justify-content:center;}

/* ---------- header ---------- */
.site-header{
  position:sticky;top:0;z-index:60;background:var(--ink);
  border-bottom:1px solid rgba(255,255,255,.08);
}
.site-header .wrap{display:flex;align-items:center;gap:18px;min-height:88px;}
.brand{display:flex;align-items:center;gap:10px;margin-right:auto;}
.brand img{height:64px;width:auto;}
.brand span{display:none;}
.nav{display:flex;align-items:center;gap:4px;}
.nav a{
  color:#D8E2D9;font-weight:600;font-size:.95rem;padding:9px 13px;border-radius:8px;
}
.nav a:hover{color:#fff;background:rgba(255,255,255,.08);text-decoration:none;}
.nav a[aria-current="page"]{color:#fff;background:rgba(124,214,47,.18);}
.call-btn{
  display:inline-flex;align-items:center;gap:.5rem;background:var(--green);color:#fff;
  font-weight:700;padding:11px 18px;border-radius:var(--radius);white-space:nowrap;
}
.call-btn:hover{background:var(--green-dk);color:#fff;text-decoration:none;}
.call-btn svg{flex:none;animation:ring 2.4s ease-in-out infinite;transform-origin:50% 60%;}
@keyframes ring{
  0%,62%,100%{transform:rotate(0);}
  66%{transform:rotate(-14deg);} 70%{transform:rotate(12deg);}
  74%{transform:rotate(-10deg);} 78%{transform:rotate(8deg);}
  82%{transform:rotate(-5deg);} 86%{transform:rotate(0);}
}
@media (prefers-reduced-motion:reduce){.call-btn svg{animation:none;}}
.call-btn .call-num{display:inline;}
.nav-toggle{
  display:none;background:transparent;border:1px solid rgba(255,255,255,.28);
  border-radius:8px;padding:9px 11px;cursor:pointer;
}
.nav-toggle span{display:block;width:20px;height:2px;background:#fff;margin:4px 0;border-radius:2px;}

@media (max-width:960px){
  .brand img{height:50px;}
  .nav-toggle{display:block;order:3;}
  .nav{
    order:4;display:none;flex-direction:column;align-items:stretch;gap:2px;
    width:100%;padding:10px 0 16px;border-top:1px solid rgba(255,255,255,.1);
  }
  .nav.is-open{display:flex;}
  .nav a{padding:13px 12px;font-size:1rem;}
  .site-header .wrap{flex-wrap:wrap;gap:10px;}
}
@media (max-width:560px){
  .call-btn .call-num{display:none;}
  .call-btn{padding:11px 15px;}
}

/* ---------- hero ---------- */
.hero{position:relative;background:var(--ink);color:#fff;overflow:hidden;}
.hero::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(105deg,rgba(10,13,10,.92) 0%,rgba(10,13,10,.78) 45%,rgba(10,13,10,.30) 100%);
}
/* The hero photo is wrapped in <picture> so phones can be served a portrait
   crop instead of a sliver of the wide one. <picture> is an inline box that
   would otherwise sit in the flow, so it takes the positioning and the img
   fills it. */
.hero picture{position:absolute;inset:0;}
.hero__bg{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 60%;
}
.hero__inner{position:relative;z-index:2;padding:96px 0 88px;max-width:660px;}
.hero h1{color:#fff;}
.hero h1 em{font-style:normal;color:var(--green-br);}
.hero p{color:#C6D4C8;font-size:1.125rem;max-width:52ch;}
.hero .btn-row{margin-top:26px;}
.hero__note{margin-top:18px;font-size:.9rem;color:#9FB2A2;}
@media (max-width:700px){
  .hero__bg{object-position:center 28%;}
  .hero__inner{padding:60px 0 56px;}
  .hero::after{background:linear-gradient(180deg,rgba(10,13,10,.88) 0%,rgba(10,13,10,.8) 100%);}
}

/* page banner (inner pages) */
.banner{background:var(--ink);color:#fff;padding:60px 0 56px;}
.banner h1{color:#fff;margin-bottom:.35em;}
.banner p{color:#AFC0B2;max-width:58ch;margin:0;font-size:1.05rem;}

/* ---------- trust bar ---------- */
.trustbar{background:var(--mint);border-bottom:1px solid #D2E9B8;}
.trustbar ul{
  list-style:none;margin:0;padding:16px 0;display:flex;flex-wrap:wrap;
  gap:10px 34px;justify-content:center;
}
.trustbar li{
  display:flex;align-items:center;gap:.5rem;font-weight:650;font-size:.94rem;color:#1D4A0C;
}
.trustbar svg{flex:none;color:var(--green);}

/* ---------- grids & cards ---------- */
.grid{display:grid;gap:24px;}
.grid--3{grid-template-columns:repeat(auto-fit,minmax(290px,1fr));}
.grid--2{grid-template-columns:repeat(auto-fit,minmax(320px,1fr));}
.grid--4{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));}

.card{
  background:var(--white);border:1px solid var(--line);border-radius:var(--radius-lg);
  overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--shadow);
}
.card__img{aspect-ratio:16/10;width:100%;object-fit:cover;background:var(--chalk);}
.card__body{padding:22px 22px 24px;display:flex;flex-direction:column;flex:1;}
.card__body h3{margin-bottom:.45em;}
.card__body p{color:var(--muted);margin-bottom:1.1em;}
.card__link{margin-top:auto;font-weight:700;display:inline-flex;align-items:center;gap:.35rem;}
.card__link:hover{gap:.6rem;text-decoration:none;}

/* The tagline card. It fills the sixth slot the five services leave empty in a
   three-across grid, so the row does not sit half finished. It carries no photo
   on purpose - it is the odd one out by design, not a service.
   Every colour here is a token, so it follows whichever palette is active
   rather than needing a light and a dark version. */
.card--tagline{background:var(--mint);border-color:var(--line);justify-content:center;}
.card--tagline .card__body{padding:30px 26px;}
.card--tagline h3{font-size:1.5rem;line-height:1.2;margin-bottom:.5em;}
/* --green reads on the pale mint of the light palette; the dark pass swaps
   it for the lime, which is the one that reads on the dark tint. */
.card--tagline h3 em{font-style:normal;color:var(--green);}
.card--tagline p{color:var(--body);}
@media (max-width:560px){
  .card--tagline .card__body{padding:24px 22px;}
  .card--tagline h3{font-size:1.3rem;}
}

/* feature list */
.ticks{list-style:none;margin:0;padding:0;display:grid;gap:12px;}
.ticks li{display:flex;gap:.7rem;align-items:flex-start;}
.ticks svg{flex:none;margin-top:3px;color:var(--green);}
.section--dark .ticks svg{color:var(--green-br);}

/* split (image + text) */
.split{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;}
.split img{border-radius:var(--radius-lg);width:100%;box-shadow:var(--shadow);}
.split--flip .split__text{order:2;}
@media (max-width:860px){
  .split{grid-template-columns:1fr;gap:32px;}
  .split--flip .split__text{order:0;}
}

/* numbered steps */
.steps{counter-reset:step;list-style:none;margin:0;padding:0;display:grid;gap:26px;
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));}
.steps li{counter-increment:step;}
.steps li::before{
  content:counter(step);display:flex;align-items:center;justify-content:center;
  width:42px;height:42px;border-radius:50%;background:var(--green);color:#fff;
  font-weight:800;font-size:1.05rem;margin-bottom:14px;
}
.steps h3{margin-bottom:.35em;}
.steps p{color:var(--muted);margin:0;}

/* ---------- services detail ---------- */
.svc{
  display:grid;grid-template-columns:1fr 1fr;gap:44px;align-items:center;
  padding:44px 0;border-bottom:1px solid var(--line);
}
.svc:last-child{border-bottom:0;}
.svc img{border-radius:var(--radius-lg);width:100%;aspect-ratio:16/11;object-fit:cover;box-shadow:var(--shadow);}
.svc:nth-child(even) .svc__text{order:2;}
@media (max-width:860px){
  .svc{grid-template-columns:1fr;gap:26px;padding:34px 0;}
  .svc:nth-child(even) .svc__text{order:0;}
}

/* ---------- gallery ---------- */
.gallery{columns:4 260px;column-gap:18px;}
.gallery figure{
  break-inside:avoid;margin:0 0 18px;background:var(--white);
  border:1px solid var(--line);border-radius:var(--radius-lg);overflow:hidden;
  box-shadow:var(--shadow);
}
.gallery img{width:100%;height:auto;display:block;background:var(--chalk);}
.gallery figcaption{
  padding:11px 14px;font-size:.82rem;font-weight:650;color:var(--muted);
  display:flex;align-items:center;gap:.45rem;
}
.gallery figcaption b{color:var(--green);font-weight:800;}
.gallery-note{
  display:flex;flex-wrap:wrap;gap:10px 22px;justify-content:center;
  margin:0 0 34px;padding:0;list-style:none;
}
.gallery-note li{
  display:flex;align-items:center;gap:.5rem;font-weight:650;font-size:.94rem;color:var(--body);
}
.gallery-note svg{flex:none;color:var(--green);}
@media (max-width:520px){.gallery{columns:1;}}

/* ---------- touch targets ----------
   On a phone a link is hit with a thumb, not a cursor. These three were
   23-26px tall, well under the 44px a thumb reliably lands on. Padding is
   added on the touch side only, and negative margins soak it back up so
   nothing moves visually - the target grows, the layout does not. */
@media (max-width:860px){
  .card__link{padding:10px 0;margin-bottom:-10px;}
  .detail a{display:inline-block;padding:11px 0;margin:-11px 0;}
  .footer-grid a{display:inline-block;padding:10px 0;margin:-10px 0;}
  .nav-toggle{padding:11px;}
}

/* ---------- contact ---------- */
.contact-grid{display:grid;grid-template-columns:1fr 1.1fr;gap:48px;align-items:start;}
@media (max-width:860px){.contact-grid{grid-template-columns:1fr;gap:34px;}}
.detail{display:flex;gap:14px;align-items:flex-start;margin-bottom:24px;}
.detail svg{flex:none;color:var(--green);margin-top:3px;}
.detail h3{font-size:.78rem;text-transform:uppercase;letter-spacing:.12em;color:var(--muted);margin:0 0 .2em;}
.detail p,.detail a{margin:0;font-size:1.075rem;font-weight:650;color:var(--ink);}
.detail a:hover{color:var(--green);}

/* ---------- forms ---------- */
.form-panel{
  background:var(--white);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:28px;box-shadow:var(--shadow);
}
.field{margin-bottom:16px;}
.field label{display:block;font-size:.83rem;font-weight:650;color:var(--muted);margin-bottom:6px;}
.field input,.field textarea{
  width:100%;font-family:inherit;font-size:16px;color:var(--body);background:#fff;
  border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;
}
.field textarea{min-height:120px;resize:vertical;}
.field input:focus,.field textarea:focus{
  outline:none;border-color:var(--green);box-shadow:0 0 0 3px rgba(46,125,20,.18);
}
.char-count{font-size:12px;color:var(--muted);text-align:right;margin-top:4px;}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}
.hp input{font-size:16px;}
.form-error{
  display:none;margin:0 0 14px;padding:11px 14px;border-radius:var(--radius);
  background:#FDECEC;border:1px solid #F3C2C2;color:#8A1F1F;font-size:.92rem;
}
.form-error.is-on{display:block;}
.form-success{display:none;text-align:center;padding:8px 0 4px;}
.form-success.is-on{display:block;}
.form-success svg{color:var(--green);margin:0 auto 12px;display:block;}
.form-success h3{margin-bottom:.4em;}
.form-success p{color:var(--muted);margin-bottom:.4em;}
.form-note{font-size:.84rem;color:var(--muted);margin:14px 0 0;}

/* ---------- CTA band ---------- */
.cta-band{background:var(--ink);color:#fff;padding:64px 0;text-align:center;}
.cta-band h2{color:#fff;}
.cta-band p{color:#AFC0B2;max-width:54ch;margin:0 auto 26px;font-size:1.075rem;}
.cta-band .btn-row{justify-content:center;}

/* ---------- footer ---------- */
.site-footer{background:var(--ink);color:#9FB2A2;padding:56px 0 26px;font-size:.94rem;
  border-top:1px solid rgba(255,255,255,.08);}
.site-footer a{color:#C6D4C8;}
.site-footer a:hover{color:#fff;}
.footer-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:36px;}
@media (max-width:760px){.footer-grid{grid-template-columns:1fr;gap:28px;}}
.site-footer img.f-logo{height:52px;width:auto;margin-bottom:14px;}
.site-footer h4{color:#fff;font-size:.78rem;text-transform:uppercase;letter-spacing:.14em;margin:0 0 14px;}
.site-footer ul{list-style:none;margin:0;padding:0;display:grid;gap:9px;}
.footer-bottom{
  margin-top:38px;padding-top:20px;border-top:1px solid rgba(255,255,255,.1);
  display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between;align-items:center;
  font-size:.85rem;color:#7E9082;
}
.credit{color:#7E9082;}
.credit a{color:#9FB2A2;text-decoration:underline;}
.credit a:hover{color:#C6D4C8;}

/* ---------- chat widget ---------- */
.chat-toggle{
  position:fixed;right:18px;bottom:18px;z-index:90;width:62px;height:62px;border-radius:50%;
  border:3px solid #fff;background:var(--ink);cursor:pointer;padding:0;
  box-shadow:var(--shadow-lg);display:flex;align-items:center;justify-content:center;
}
/* The photo does its own circular clipping. The button itself must NOT clip, or
   the online dot sitting on its edge gets sliced in half. */
.chat-toggle img{width:100%;height:100%;object-fit:cover;border-radius:50%;}
.chat-toggle .x{display:none;color:#fff;font-size:26px;line-height:1;font-weight:400;}
.chat-toggle.is-open img{display:none;}
.chat-toggle.is-open .x{display:block;}

/* "We're about" dot. The emblem is itself green, so it is the white ring, not
   the colour, that makes this read as a separate object sat on top. */
.chat-dot{
  position:absolute;right:0;bottom:2px;width:15px;height:15px;border-radius:50%;
  background:#31C74A;border:2.5px solid #fff;box-shadow:0 1px 3px rgba(10,13,10,.45);
}
.chat-toggle.is-open .chat-dot{display:none;}

.chat-badge{
  position:fixed;right:88px;bottom:33px;z-index:89;background:#fff;color:var(--ink);
  font-size:.87rem;font-weight:650;padding:8px 13px;border-radius:20px;box-shadow:var(--shadow);
  border:1px solid var(--line);white-space:nowrap;
}
.chat-badge.is-hidden{display:none;}
.chat-panel{
  position:fixed;right:18px;bottom:92px;z-index:91;width:min(360px,calc(100vw - 36px));
  max-height:min(620px,calc(100vh - 120px));overflow-y:auto;background:#fff;
  border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);border:1px solid var(--line);
  display:none;
}
.chat-panel.is-open{display:block;}
.chat-head{background:var(--ink);color:#fff;padding:16px 18px;display:flex;gap:12px;align-items:center;}
.chat-head img{width:42px;height:42px;border-radius:50%;object-fit:cover;flex:none;border:2px solid rgba(255,255,255,.35);}
.chat-head strong{display:block;font-size:.98rem;}
.chat-head span{font-size:.8rem;color:#AFC0B2;}
.chat-body{padding:18px;}
.chat-body > p:first-child{
  background:var(--chalk);border:1px solid var(--line);border-radius:12px;
  padding:12px 14px;font-size:.93rem;margin-bottom:16px;
}
.chat-body .field{margin-bottom:12px;}
.chat-body .char-count{margin-top:-4px;}
@media (max-width:400px){
  .chat-toggle{width:56px;height:56px;right:14px;bottom:14px;}
  .chat-dot{width:13px;height:13px;border-width:2px;bottom:1px;}
  .chat-panel{right:12px;bottom:80px;width:calc(100vw - 24px);}
  .chat-badge{display:none;}
}

/* ---------- baked reviews ---------- */
.reviews-head{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:24px;}

/* ---------- service-area map ----------
   A single self-hosted image: no map JS, no third-party request at page load,
   so nothing to consent to and nothing to block. Tiles were stitched once at
   build time (tools note in the repo history); OSM data is ODbL, so the
   attribution below the image has to stay. */
.map{margin:30px 0 0;}
.map img{
  width:100%;height:auto;display:block;
  border:1px solid var(--line);border-radius:var(--radius-lg);background:var(--chalk);
}
.map figcaption{font-size:.82rem;line-height:1.55;color:var(--muted);margin-top:10px;}

/* ---------- chat panel: the whole form, no inner scrollbar ----------
   A form you have to scroll inside reads as broken and gets abandoned, so the
   panel is sized to hold every field at once. The chat copy of the form is
   tightened up (smaller gaps, a shorter textarea) purely so it fits; the real
   contact form on the page keeps its full spacing. overflow-y:auto stays as a
   last-resort fallback for very short windows - it should never be reached. */
.chat-panel{max-height:min(780px,calc(100vh - 96px));bottom:84px;}
.chat-head{padding:13px 16px;}
.chat-body{padding:16px;}
.chat-body > p:first-child{padding:10px 13px;font-size:.88rem;line-height:1.5;margin-bottom:13px;}
.chat-body .field{margin-bottom:10px;}
.chat-body .field label{margin-bottom:4px;font-size:.8rem;}
.chat-body .field input,.chat-body .field textarea{padding:10px 12px;}
.chat-body .field textarea{min-height:70px;}
@media (max-height:800px){
  .chat-body > p:first-child{display:none;}
  .chat-body .field textarea{min-height:56px;}
}
@media (max-height:720px){
  .chat-body{padding:13px;}
  .chat-body .field{margin-bottom:7px;}
  .chat-body .field input,.chat-body .field textarea{padding:9px 12px;}
  .chat-head{padding:10px 14px;}
}
/* 360x640-class phones: the 720px tier still left the panel 17px short of
   fitting, so it grew an inner scrollbar. One more step down clears it. */
@media (max-height:660px){
  .chat-body{padding:11px;}
  .chat-body .field{margin-bottom:5px;}
  .chat-body .field label{margin-bottom:2px;font-size:.76rem;}
  .chat-body .field input,.chat-body .field textarea{padding:8px 11px;}
  .chat-body .field textarea{min-height:46px;}
  .chat-head{padding:8px 13px;}
  .chat-head span{display:none;}
}

/* =====================================================================
   MOTION
   Everything here is transform and opacity only, so it runs on the
   compositor and never reflows the page. The whole block is wrapped in
   prefers-reduced-motion:no-preference - anyone who has asked their
   system for less motion gets a completely static site, not a slower one.
   The .js-anim gate means the stylesheet never hides anything by itself:
   if main.js fails to load, every element stays plainly visible.
   ===================================================================== */
@media (prefers-reduced-motion:no-preference){

  /* --- scroll reveal --- */
  .js-anim .reveal{
    opacity:0;transform:translateY(20px);
    transition:opacity .6s ease,transform .6s cubic-bezier(.22,.61,.36,1);
    will-change:opacity,transform;
  }
  .js-anim .reveal.is-in{opacity:1;transform:none;will-change:auto;}

  /* --- hero: the copy lifts in on load, the photo eases out of a slow
         push-in. The photo animation runs once and stops; nothing here
         loops, so the page settles and stays settled. --- */
  .hero__inner > *{animation:rise .7s cubic-bezier(.22,.61,.36,1) both;}
  .hero__inner > :nth-child(1){animation-delay:.05s;}
  .hero__inner > :nth-child(2){animation-delay:.14s;}
  .hero__inner > :nth-child(3){animation-delay:.23s;}
  .hero__inner > :nth-child(4){animation-delay:.32s;}
  .hero__inner > :nth-child(5){animation-delay:.41s;}
  .hero__bg{animation:pushin 14s ease-out both;}
  @keyframes rise{from{opacity:0;transform:translateY(22px);}to{opacity:1;transform:none;}}
  @keyframes pushin{from{transform:scale(1.07);}to{transform:scale(1);}}

  /* --- header lifts off the page once it has moved ---
         Shadow only, deliberately. Shrinking a sticky header changes how much
         flow space it occupies, which shunts the whole page up by that many
         pixels mid-scroll - a jump you feel every time. A shadow costs no
         layout at all. */
  .site-header{transition:box-shadow .25s ease;}
  .site-header.is-stuck{box-shadow:0 6px 22px rgba(10,13,10,.30);}

  /* --- cards lift, their photo eases in behind the crop --- */
  .card{transition:transform .28s cubic-bezier(.22,.61,.36,1),box-shadow .28s ease;}
  .card__img{transition:transform .5s cubic-bezier(.22,.61,.36,1);}
  .card:hover{transform:translateY(-5px);box-shadow:var(--shadow-lg);}
  .card:hover .card__img{transform:scale(1.05);}

  /* --- the same idea on the gallery and the big page photos --- */
  .gallery figure{transition:transform .28s cubic-bezier(.22,.61,.36,1),box-shadow .28s ease;}
  .gallery figure:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg);}
  .split img,.svc img,.map img{transition:transform .5s cubic-bezier(.22,.61,.36,1),box-shadow .3s ease;}
  .split img:hover,.svc img:hover,.map img:hover{transform:scale(1.015);box-shadow:var(--shadow-lg);}

  /* --- buttons and links get a small push, and settle on click --- */
  .btn,.call-btn{transition:background .15s,color .15s,border-color .15s,transform .15s ease,box-shadow .15s ease;}
  .btn:hover,.call-btn:hover{transform:translateY(-2px);}
  .btn:active,.call-btn:active{transform:translateY(0);}
  .card__link{transition:gap .2s ease;}
  .nav a{transition:background .18s ease,color .18s ease;}

  /* --- step numbers pop rather than fade --- */
  .js-anim .steps li.reveal::before{transform:scale(.6);opacity:0;transition:transform .45s cubic-bezier(.34,1.56,.64,1) .12s,opacity .3s ease .12s;}
  .js-anim .steps li.reveal.is-in::before{transform:scale(1);opacity:1;}

  /* --- the chat bubble arrives after the page has, and the panel opens
         from its own corner instead of just appearing --- */
  /* The bubble floats continuously to draw the eye to the corner. Listed AFTER
     'rise' in the shorthand: when two animations drive the same property the
     later one wins, which is what lets the float take over once the entry
     animation has finished. */
  .chat-toggle{
    animation:rise .5s ease both 1.1s, chat-float 3.2s ease-in-out 1.6s infinite;
    /* all three, because this declaration replaces the transition list set
       on .chat-toggle earlier in the file rather than adding to it */
    transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;
  }
  .chat-badge{animation:rise .5s ease both 1.3s;}
  /* Hover cancels the loop outright rather than competing with it: both drive
     transform, and a running animation beats a plain declaration, so the
     scale would simply never apply while the hop was going. */
  .chat-toggle:hover{animation:none;}
  .chat-toggle.is-open{animation:none;}
  /* The bubble hovers in the corner - a slow, continuous drift up and down,
     like it is floating rather than sitting on the page. Transform only, so it
     runs on the compositor and never reflows anything around it. */
  @keyframes chat-float{
    0%,100%{transform:translateY(0);}
    50%{transform:translateY(-9px);}
  }
  .chat-panel.is-open{animation:panel .28s cubic-bezier(.22,.61,.36,1) both;}
  @keyframes panel{from{opacity:0;transform:translateY(14px) scale(.97);}to{opacity:1;transform:none;}}

  /* --- the online dot breathes ---
         The only looping animation on the site, and a deliberate exception to
         the "nothing loops" rule above: a still dot reads as decoration, a
         moving one reads as somebody being there. Kept slow and shallow so it
         registers in the corner of your eye and never pulls it. The ring is a
         pseudo-element so only it scales - scaling the dot itself would drag
         its white border with it. */
  .chat-dot{animation:dot-breathe 2.8s ease-in-out infinite;}
  .chat-dot::after{
    content:"";position:absolute;inset:-2.5px;border-radius:50%;
    border:2px solid #31C74A;animation:dot-ring 2.8s ease-out infinite;
  }
  @keyframes dot-breathe{0%,100%{transform:scale(1);}50%{transform:scale(1.14);}}
  @keyframes dot-ring{
    0%{transform:scale(1);opacity:.55;}
    70%{transform:scale(2);opacity:0;}
    100%{transform:scale(2);opacity:0;}
  }

  /* --- form fields and the success tick --- */
  .field input,.field textarea{transition:border-color .18s ease,box-shadow .18s ease;}
  .form-success.is-on{animation:rise .45s ease both;}
}

/* ---------- contact: two columns, one bottom edge ----------
   Side by side, the details-and-map column and the form card almost never
   come out the same length, and the ragged bottom edge is the first thing
   you notice. Both columns are stretched to the taller one's height, then
   whichever is short takes up the slack: the form spreads the gaps between
   its fields, and the map grows a little. The map is capped and floored so
   it can never stretch far enough for object-fit to crop a suburb name off
   the edge. Below 860px the grid is a single column and none of this
   applies. */
@media (min-width:861px){
  .contact-grid{align-items:stretch;}
  .contact-grid > div:first-child{display:flex;flex-direction:column;}
  .contact-grid > div:first-child > .map{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;}
  .contact-grid > div:first-child > .map img{flex:1 1 auto;min-height:240px;max-height:460px;object-fit:cover;}
  .form-panel{display:flex;flex-direction:column;}
  .form-panel > form{flex:1 1 auto;display:flex;flex-direction:column;justify-content:space-between;}
  .form-panel > form > .field{flex:0 0 auto;}
  .form-note{margin-bottom:0;}
}

/* =====================================================================
   DARK PASS
   Appended deliberately, and it is the LAST block in this file so it
   outranks the light rules above it on source order. To go back to the
   light site, delete everything below this banner - nothing above it was
   modified, and css/site.css.light-backup is a copy of the file as it
   stood beforehand.

   The logo is chrome-lime on near-black, so dark is the palette it was
   drawn for. Two things drive the whole pass:
     1. --ink does double duty in the light design: the dark band colour
        AND the heading colour. On a dark site those two diverge, so --ink
        keeps its job as the deep ground and every "dark text" rule is
        overridden to light instead.
     2. --green (#2E7D14) was picked for 4.9:1 on WHITE. On near-black it
        is almost invisible, so every place it means "accent" or "link"
        moves to the logo lime (9.6:1 on --ink). It stays as the primary
        BUTTON fill, where white text sits on top of it and it still works.
   ===================================================================== */

:root{
  color-scheme:dark;
  --body:#D5E0D6;          /* body copy on the dark ground */
  --muted:#9DB0A0;         /* captions, small print */
  --white:#1C231B;         /* was the card/panel surface - now a raised dark */
  --chalk:#151B14;         /* alternating band + image placeholder */
  --mint:#16210F;          /* tinted proof panels */
  --line:rgba(255,255,255,.13);
  --shadow:0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.42);
  --shadow-lg:0 2px 6px rgba(0,0,0,.55), 0 24px 60px rgba(0,0,0,.5);
}

/* --white is now a surface, so the page ground has to be set on its own.
   It sits just above --ink so the header, footer, hero and .section--dark
   still read as deeper bands rather than blending into the page. */
body{background:#0F140E;}

h1,h2,h3,h4{color:#fff;}
a{color:var(--green-br);}
a:hover{color:#A2E85C;}
.eyebrow{color:var(--green-br);}

/* every small accent mark that pointed at the button green */
.trustbar svg,.ticks svg,.detail svg,.gallery-note svg,.form-success svg,
.gallery figcaption b{color:var(--green-br);}

/* things that used --ink to mean "dark text on a light surface" */
.detail p,.detail a{color:#fff;}
.detail a:hover{color:var(--green-br);}
.btn--ghost{color:#fff;border-color:rgba(255,255,255,.35);}
.btn--ghost:hover{color:#fff;border-color:#fff;background:rgba(255,255,255,.1);}

/* .btn--dark was a dark fill on a light page; on a dark page it vanishes,
   so it becomes a raised surface with a visible edge. Deliberately not the
   lime - that would compete with the primary button beside it. */
.btn--dark{background:#1C231B;color:#fff;border-color:rgba(255,255,255,.25);}
.btn--dark:hover{background:#28311F;color:#fff;border-color:rgba(255,255,255,.45);}

/* trust bar: tinted rather than mint, with the text lifted off it */
.trustbar{background:var(--mint);border-bottom:1px solid rgba(124,214,47,.22);}
.trustbar li{color:#C8E9A6;}

/* Form controls stay LIGHT on the dark site - a pale field on a dark panel
   reads as "type here" at a glance, where a dark field tends to disappear into
   the card behind it. color-scheme:light is set on the controls themselves so
   the browser's own autofill and spinner chrome matches them rather than
   following the page's dark scheme. */
.field input,.field textarea{
  color-scheme:light;
  background:#F4F7F2;color:#232823;border-color:rgba(255,255,255,.22);
}
.field input::placeholder,.field textarea::placeholder{color:#5C6B57;opacity:1;}
.field input:focus,.field textarea:focus{
  border-color:var(--green-br);box-shadow:0 0 0 3px rgba(124,214,47,.25);
}
.form-error{background:#2A1414;border-color:#5C2626;color:#F3B4B4;}

/* the three hard-coded white surfaces */
.skip:focus{background:#1C231B;color:#fff;}
.chat-badge{background:#1C231B;color:#fff;border-color:rgba(255,255,255,.16);}
.chat-panel{background:#1C231B;}

.card--tagline h3 em{color:var(--green-br);}

/* The service-area map is a baked light-tiled image. Knocked back slightly so
   it does not glare against everything around it; not so far that the suburb
   labels stop being readable. */
.map img{filter:brightness(.92) contrast(1.02);}

/* ---------- "Message us" reveals on hover ----------
   The badge used to sit beside the avatar permanently. It now appears only
   when the chat profile is hovered or keyboard-focused, so the corner stays
   quiet until someone goes looking.

   Two things this depends on:
   - The badge must come AFTER the button in the markup. It is an adjacent
     sibling selector; it cannot reach backwards. The markup was reordered
     for this.
   - `animation:none` is required, not optional. The entry animation in the
     motion block uses `both`, so its fill would hold opacity:1 and the
     badge would never hide.

   Gated on a real pointer. On touch there is no hover, so the badge stays
   permanently visible there exactly as before - otherwise phone and tablet
   users would simply lose the prompt. */
@media (hover:hover) and (pointer:fine){
  .chat-badge{
    animation:none;
    opacity:0;transform:translateX(8px);pointer-events:none;
    transition:opacity .18s ease,transform .18s cubic-bezier(.22,.61,.36,1);
  }
  .chat-toggle:hover + .chat-badge,
  .chat-toggle:focus-visible + .chat-badge{opacity:1;transform:none;}
}
@media (prefers-reduced-motion:reduce){
  .chat-badge{transition:none;}
}

/* ---------- inner-page banner photo ----------
   The top band of every inner page carries a photo relevant to that page,
   behind the same dark treatment as the homepage hero so the heading stays
   readable.

   Two gradients are stacked. The first fades the bottom edge into the page
   ground (#0F140E) so the band melts into the section below instead of
   ending on a hard line - every banner is followed by a plain .section, so
   that colour is the right one to fade to on all five pages. The second is
   the hero's own 105deg wash: heaviest on the left where the heading and
   its line of copy sit, lifting towards the right so the photo still shows.

   On phones the band is much taller relative to its width and the text runs
   the full width, so it switches to a straight top-to-bottom fade. */
.banner{position:relative;overflow:hidden;}
.banner__bg{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center 58%;
}
.banner::after{
  content:"";position:absolute;inset:0;
  background:
    linear-gradient(180deg,rgba(15,20,14,0) 58%,#0F140E 100%),
    linear-gradient(105deg,rgba(10,13,10,.94) 0%,rgba(10,13,10,.82) 48%,rgba(10,13,10,.40) 100%);
}
.banner .wrap{position:relative;z-index:2;}
@media (max-width:700px){
  .banner::after{
    background:linear-gradient(180deg,rgba(10,13,10,.88) 0%,rgba(10,13,10,.8) 62%,#0F140E 100%);
  }
}

/* ---------- chat bubble hover, last word ----------
   Defined at the very end of the file so source order settles it. An earlier
   copy sat higher up and would not take effect; there is exactly one place
   this behaviour lives now, which is easier to reason about anyway.

   The scale is the smallest part of it - 12% of a 62px button is about seven
   pixels, which on its own reads as nothing happening. The lime edge and the
   ring are what say "this does something". The ring is a box-shadow rather
   than an outline so it follows the circle.

   Colour is a literal, not var(--green-br), so the hover survives even if the
   dark pass at the top of this section is ever deleted. */
.chat-toggle{transition:transform .2s ease, box-shadow .2s ease, border-color .2s ease;}
.chat-toggle:hover,
.chat-toggle:focus-visible{
  border-color:#7CD62F;
  box-shadow:0 0 0 5px rgba(124,214,47,.32), 0 10px 30px rgba(0,0,0,.5);
}
@media (prefers-reduced-motion:no-preference){
  .chat-toggle:hover,
  .chat-toggle:focus-visible{transform:scale(1.12);}
}
