/* ==========================================================================
   GoSpringLawn.com
   Rebuilt from a 1536px-wide comp.

   Two sizing systems, on purpose:
   - The masthead and hero sit on a photograph, so they must track it exactly.
     They're sized in `--s`, one comp pixel (= 1px at 1536px wide), which keeps
     the copy in the same spot relative to the sign at every desktop width.
   - Everything below the photo has nothing to align to, so it uses the comp's
     pixel values directly. Scaling it with `--s` would drop the card copy to
     ~12px on a 1280 laptop.
   Both systems agree exactly at 1536px, which is where the comp was measured.
   ========================================================================== */

:root {
  --s: 0.0651041667vw;          /* 100 / 1536 — one comp pixel */

  --green-deep:  #123c1c;       /* hero headline */
  --ink:         #23302a;       /* hero body copy */
  --accent:      #2b7a15;       /* emphasis in the hero lede */
  --lime:        #9bcf3f;       /* card titles */
  --card-ink:    #e9f4ec;

  --band-top:    #b6dcf7;       /* masthead band over sky, mobile only */
  --band-bot:    #f1f6f8;
  --sky:         #eef4f7;       /* top row of the mobile hero photo */

  --svc-core:    #02220f;       /* darkest point, behind the cards */
  --svc-edge:    #0f3a1c;       /* section edges */
  --footer-bg:   #062617;
  --rule:        rgba(190, 230, 190, .28);

  --font: 'Figtree', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
}

@media (min-width: 2200px) { :root { --s: 1.43px; } }   /* stop runaway scaling */

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

/* any gap below a short page reads as footer, not a white band */
html { -webkit-text-size-adjust: 100%; background: var(--footer-bg); }

body {
  margin: 0;
  font-family: var(--font);
  color: var(--ink);
  background: #fff;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
h1, h2, p, ul { margin: 0; }
ul { list-style: none; padding: 0; }

/* ==========================================================================
   Masthead — floats over the comp's translucent band, which is baked into the
   top 97px of hero-wide.jpg (sky and roofline read straight through it).
   ========================================================================== */

.masthead {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 5;
  height: calc(97 * var(--s));
}

.masthead__logo {
  position: absolute;
  left: calc(186 * var(--s));
  top: calc(8 * var(--s));
  display: block;
}

.masthead__logo img {
  height: calc(87 * var(--s));
  width: auto;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  aspect-ratio: 1536 / 606;
}

.hero__media,
.hero__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.hero__media img {
  object-fit: cover;
  object-position: center top;
}

.hero__copy {
  position: absolute;
  left: calc(187 * var(--s));
  top: calc(213 * var(--s));
  width: calc(505 * var(--s));
  text-align: center;
  z-index: 2;
}

.hero__title {
  font-size: calc(48 * var(--s));
  line-height: 1.02;
  font-weight: 700;
  /* the comp's face is narrower than Figtree; this lands line 1 on its 483px */
  letter-spacing: -0.033em;
  color: var(--green-deep);
}

.hero__leaf {
  display: inline;
  height: calc(44 * var(--s));
  width: auto;
  margin-left: calc(13 * var(--s));
  vertical-align: calc(-12 * var(--s));
}

.hero__lede {
  /* narrower than the headline, so it keeps the comp's two-line shape */
  max-width: calc(492 * var(--s));
  margin: calc(17 * var(--s)) auto 0;
  font-size: calc(21 * var(--s));
  line-height: 1.43;
  font-weight: 400;
}

.hero__lede strong { font-weight: 700; color: var(--accent); }

/* --------------------------------------------------------------------------
   Hero call to action — the pair sits on the photographed lawn, so both
   buttons carry their own contrast rather than relying on what is behind
   them: the sunlit stripes run to ~85% white in places.
   -------------------------------------------------------------------------- */

/* the row is sized to its buttons, not to the copy column, so the pair always
   stays on one line — it is centred on the copy and has room either side
   before the sign begins */
.hero__cta {
  width: max-content;
  max-width: calc(640 * var(--s));
  margin: max(20px, calc(27 * var(--s))) auto 0;
  display: flex;
  justify-content: center;
  gap: max(12px, calc(16 * var(--s)));
}

/* Every size has a pixel floor. Left to scale on `--s` alone the buttons fall
   to ~13px type and a 1.4px border on a 1100px laptop, which is where they
   stopped reading against the photo. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: max(8px, calc(10 * var(--s)));
  padding: max(14px, calc(16 * var(--s))) max(22px, calc(29 * var(--s)));
  border: max(2px, calc(2.5 * var(--s))) solid transparent;
  border-radius: max(8px, calc(10 * var(--s)));
  font: inherit;
  font-size: max(16px, calc(19 * var(--s)));
  line-height: 1.15;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform .18s ease, box-shadow .18s ease, background-color .18s ease;
}

.btn__icon {
  width: max(18px, calc(22 * var(--s)));
  height: max(18px, calc(22 * var(--s)));
  flex: none;
}

/* both buttons sit on sunlit grass that runs to ~85% white, so both are solid:
   a translucent fill let the mown stripes read straight through the edge */
.btn--text {
  color: #fff;
  background: linear-gradient(180deg, #35921b, #1e7005);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .22),
    0 3px 16px rgba(4, 26, 8, .45);
}

.btn--call {
  color: #fff;
  border-color: #fff;
  background: #16401e;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .14),
    0 3px 16px rgba(4, 26, 8, .45);
}

.btn:hover { transform: translateY(-1px); }
.btn--text:hover { background: linear-gradient(180deg, #3ba01f, #227a06); }
.btn--call:hover { background: #1d5528; }
.btn:active { transform: translateY(1px); }

.btn:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 3px;
}

/* ==========================================================================
   Services
   ========================================================================== */

.services {
  position: relative;
  /* the inline padding is sized to hold the leaves, which hang ~48px past the
     grid — any less and they push a horizontal scrollbar on narrow desktops */
  padding: 52px 72px;
  /* the comp reads as a wide, shallow vignette: deep green pooling behind the
     cards and lifting towards all four edges, plus a sheen on the photo seam */
  background:
    linear-gradient(180deg, rgba(150, 225, 120, .05), rgba(150, 225, 120, 0) 70px),
    linear-gradient(90deg, rgba(2, 34, 15, .45), transparent 8%, transparent 92%, rgba(2, 34, 15, .45)),
    radial-gradient(ellipse 46% 40% at 50% 50%, var(--svc-core) 0%, var(--svc-core) 45%, rgba(2, 34, 15, 0) 100%),
    var(--svc-edge);
}

/* shrink-wraps the grid so the leaves can anchor to the outer cards' corners */
.services__inner {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

.services__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 350px));
  gap: 23.5px;
}

.card {
  position: relative;
  /* the copy is centred, so this narrow inline padding only sets the wrap
     width — it keeps Mulching's long line on two lines like the comp */
  padding: 56px 10px 15px;
  text-align: center;
  border: 1px solid rgba(154, 214, 90, .34);
  border-radius: 18px;
  background: rgba(140, 220, 110, .04);
}

.card__badge {
  position: absolute;
  left: 50%;
  top: -46px;
  width: 94px;
  height: 94px;
  transform: translateX(-50%);
  display: grid;
  place-items: center;
  border-radius: 50%;
  /* the comp's disc holds a flat deep green, brightening only near the bottom */
  background: linear-gradient(180deg, #216c02 0%, #216c02 52%, #3f8305 100%);
  border: 2.5px solid #fff;
  /* thin white ring, then a lime halo hugging it before the soft falloff —
     without the solid ring the shadow reads as one thick white donut */
  box-shadow:
    0 0 0 3px rgba(150, 240, 70, .6),
    0 0 10px 1px rgba(140, 235, 60, .5),
    0 0 20px rgba(110, 205, 45, .3);
}

/* each glyph is drawn at the size it occupies in the comp, not normalised */
.card__badge img { width: auto; }
.card:nth-child(1) .card__badge img { height: 60px; }
.card:nth-child(2) .card__badge img { height: 57px; }
.card:nth-child(3) .card__badge img { height: 62px; }

.card__title {
  font-size: 24px;
  line-height: 1.2;
  font-weight: 700;
  color: var(--lime);
  margin-bottom: 3px;
}

.card__text {
  font-size: 16.5px;
  line-height: 1.28;
  color: var(--card-ink);
}

/* the two sprigs tucked into the outer corners of the first and last card.
   Positioned by centre so the rotated bounding box can't drift. */
.services__leaf {
  position: absolute;
  z-index: 2;
  top: 93.5px;
  width: 40px;
  height: auto;
  pointer-events: none;
}

.services__leaf--left  { left: -26.5px;  transform: translate(-50%, -50%) scaleX(-1) rotate(-6deg); }
.services__leaf--right { right: -26.5px; transform: translate(50%, -50%) rotate(-6deg); }

/* ==========================================================================
   Service area — one wide panel under the three cards. It borrows the cards'
   border, radius and badge so it reads as part of the same set, but it is
   narrower than the grid and laid out in a row, so it settles under them
   rather than looking like a fourth card that lost its siblings.
   ========================================================================== */

.area {
  position: relative;
  /* shrink-wrapped, not stretched to the grid: with one town a full-width
     panel is two thirds empty, and it grows on its own as towns are added */
  width: fit-content;
  max-width: 100%;
  margin: 56px auto 0;
  padding: 18px 34px;
  display: flex;
  align-items: center;
  gap: 24px;
  border: 1px solid rgba(154, 214, 90, .34);
  border-radius: 18px;
  /* the glow pools behind the badge, so the panel reads as lit from the pin
     outwards instead of as a flat second card */
  background:
    radial-gradient(120% 190% at 6% 50%, rgba(140, 235, 60, .11), rgba(140, 235, 60, 0) 62%),
    rgba(140, 220, 110, .04);
  /* the hairline along the top edge is what gives it a raised, glassy edge;
     the drop shadow underneath lifts it off the section's dark core */
  box-shadow:
    inset 0 1px 0 rgba(200, 245, 160, .11),
    0 14px 34px rgba(2, 18, 8, .3);
}

/* same disc as the service cards, deliberately — one badge language */
.area__badge {
  flex: none;
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  color: #fff;
  border-radius: 50%;
  background: linear-gradient(180deg, #216c02 0%, #216c02 52%, #3f8305 100%);
  border: 2.5px solid #fff;
  box-shadow:
    0 0 0 3px rgba(150, 240, 70, .6),
    0 0 10px 1px rgba(140, 235, 60, .5),
    0 0 20px rgba(110, 205, 45, .3);
}

.area__pin { width: 38px; height: 38px; }

.area__title {
  font-size: 24px;
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--lime);
  white-space: nowrap;
}

/* fades out at both ends so it separates the label from the towns without
   drawing a hard bar across the panel */
.area__rule {
  flex: none;
  width: 1px;
  height: 46px;
  background: linear-gradient(180deg,
    rgba(155, 207, 63, 0), rgba(155, 207, 63, .55), rgba(155, 207, 63, 0));
}

.area__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 26px;
}

/* the bullet is drawn, not typed, so it stays out of the accessible name and
   is not read out once the list holds more than one town */
.area__item {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-size: 18px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--card-ink);
}

.area__item::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lime);
  box-shadow: 0 0 8px rgba(155, 207, 63, .75);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.colophon {
  background: var(--footer-bg);
  color: #fff;
  padding: 18.5px 0 21px;
}

.colophon__inner {
  width: min(1240px, 100% - 48px);
  margin-inline: auto;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
}

.colophon__brand { display: flex; justify-content: center; }
.colophon__brand img { height: 60px; width: auto; }

.colophon__about {
  text-align: center;
  padding: 0 105px;
  border-left: 1px solid var(--rule);
  border-right: 1px solid var(--rule);
}

.colophon__name { font-size: 19px; font-weight: 700; line-height: 1.35; }
.colophon__tag  { font-size: 15px; line-height: 1.35; color: #e6f3ea; }

.colophon__phone { font-size: 15px; line-height: 1.35; margin-top: 4px; color: #e6f3ea; }

.colophon__phone a {
  font-weight: 700;
  color: var(--lime);
  text-decoration: none;
  border-bottom: 1px solid rgba(155, 207, 63, .45);
}

.colophon__phone a:hover { color: #b6e061; }

.colophon__rule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 6px;
  height: 11px;
}

.colophon__rule i {
  height: 1px;
  width: 60px;
  background: linear-gradient(90deg, rgba(155, 207, 63, 0), rgba(155, 207, 63, .75));
}

.colophon__rule i:last-child { transform: scaleX(-1); }
.colophon__rule img { height: 11px; width: auto; transform: rotate(-28deg); }

.colophon__legal { text-align: center; font-size: 14.4px; line-height: 1.39; color: #eaf5ee; }

/* ==========================================================================
   Estimate prompt — a centred dialog, narrow layout only, opened by script.js
   once the hero's own buttons are off screen.
   ========================================================================== */

.promo {
  display: none;               /* the wide layout keeps its buttons in view */
  position: fixed;
  inset: 0;
  z-index: 40;
  align-items: center;
  justify-content: center;
  padding: 20px;
  /* `visibility` is what takes the links out of the tab order while it is
     closed, and it waits for the fade to finish */
  visibility: hidden;
  transition: visibility 0s .32s;
}

@media (max-width: 1099px) {
  .promo { display: flex; }
}

.promo.is-open {
  visibility: visible;
  transition: visibility 0s;
}

.promo__scrim {
  position: absolute;
  inset: 0;
  background: rgba(4, 22, 12, .62);
  opacity: 0;
  transition: opacity .32s ease;
}

.promo.is-open .promo__scrim { opacity: 1; }

.promo__card {
  position: relative;
  width: min(360px, 100%);
  max-height: calc(100vh - 40px);
  overflow: auto;
  padding: 28px 22px 22px;
  border-radius: 20px;
  background: #fff;
  text-align: center;
  box-shadow: 0 18px 50px rgba(3, 20, 10, .45);
  opacity: 0;
  transform: translateY(18px) scale(.96);
  transition: opacity .34s ease, transform .34s cubic-bezier(.22, .8, .3, 1);
}

.promo.is-open .promo__card { opacity: 1; transform: none; }

/* the card takes focus on open so the keyboard lands inside it; the dialog
   appearing is the cue, and a ring around the whole box only reads as an error */
.promo__card:focus { outline: none; }

.promo__close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(18, 60, 28, .08);
  color: var(--green-deep);
  cursor: pointer;
}

.promo__close:hover { background: rgba(18, 60, 28, .16); }

.promo__close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.promo__leaf { height: 30px; width: auto; margin: 0 auto 10px; }

.promo__title {
  font-size: 22px;
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--green-deep);
}

.promo__text {
  margin-top: 8px;
  font-size: 15.5px;
  line-height: 1.4;
  color: var(--ink);
  text-wrap: balance;    /* two even lines rather than a one-word orphan */
}

/* stacked: a modal this narrow gives each one a full-width tap target */
.promo__cta {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 18px;
}

.promo__cta .btn { width: 100%; }

.promo__num { margin-top: 13px; font-size: 14px; color: #52635a; }

.promo__num a {
  font-weight: 700;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(43, 122, 21, .35);
}

@media (prefers-reduced-motion: reduce) {
  .promo, .promo.is-open, .promo__scrim, .promo__card { transition: none; }
}

/* The comp's card line breaks are authored, not width-driven, so they only hold
   while the cards are at their full 350px. Narrower than that, let them reflow. */
@media (max-width: 1240px) {
  .card__text br { display: none; }
}

/* ==========================================================================
   Narrow screens — below ~1100 the comp's side-by-side hero would shrink the
   lede past readability, so the copy stacks above a sign-focused crop and the
   masthead carries its own band.
   ========================================================================== */

@media (max-width: 1099px) {
  .masthead {
    position: static;
    height: auto;
    padding: 14px 20px;
    background: linear-gradient(180deg, var(--band-top), var(--band-bot));
  }

  .masthead__logo { position: static; }
  .masthead__logo img { height: clamp(42px, 6vw, 58px); }

  .hero {
    display: flex;
    flex-direction: column;
    aspect-ratio: auto;
  }

  .hero__media { order: 2; position: static; }
  .hero__media img { position: static; height: auto; }

  /* the band must span the full width to bridge the masthead into the photo, so
     it stays stretched and the text inside is what gets capped. (An auto inline
     margin here would shrink-wrap the flex item and leave gutters.) */
  .hero__copy {
    order: 1;
    position: static;
    width: 100%;
    max-width: none;
    padding: clamp(20px, 4vw, 40px) 20px clamp(24px, 5vw, 44px);
    background: linear-gradient(180deg, var(--band-bot), var(--sky));
  }

  .hero__title {
    max-width: 760px;
    margin-inline: auto;
    font-size: clamp(28px, 4.6vw, 46px);
    line-height: 1.08;
    letter-spacing: -0.5px;
  }

  .hero__leaf { height: clamp(24px, 4vw, 42px); margin-left: 8px; vertical-align: -0.22em; }
  .hero__lede { max-width: 560px; margin-top: 12px; font-size: clamp(15px, 1.8vw, 19px); }
  .hero__lede br { display: none; }   /* the authored break only fits the comp */

  /* off the photo now — the copy block has its own pale band behind it, so the
     white border would vanish and the row is free to wrap */
  .hero__cta {
    width: auto;
    max-width: none;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: clamp(18px, 3vw, 26px);
  }

  .btn {
    padding: 14px 22px;
    border-width: 2px;
    border-radius: 9px;
    font-size: clamp(15px, 1.7vw, 18px);
    gap: 9px;
  }

  .btn__icon { width: 20px; height: 20px; }

  .btn--text { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22), 0 3px 12px rgba(6, 32, 10, .24); }

  .btn--call {
    background: #17431f;
    border-color: #17431f;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), 0 3px 12px rgba(6, 32, 10, .22);
  }

  .btn--call:hover { background: #1d5528; border-color: #1d5528; }
}

/* one row of two buttons stops fitting well below ~380px */
@media (max-width: 380px) {
  .hero__cta { flex-direction: column; align-items: stretch; }
  .btn { width: 100%; }
}

@media (max-width: 860px) {
  .services { padding: 62px 20px 44px; }

  /* inner goes full width so the leaves can sit in the section's corners as
     decoration; the stacked grid is what gets capped instead */
  .services__inner { width: 100%; max-width: none; }

  .services__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 68px;
    max-width: 420px;
    margin-inline: auto;
  }

  .card { padding: 56px 22px 20px; }
  .card__text { font-size: 16px; }
  .card__text br { display: none; }   /* let the copy reflow to the card width */

  /* stacked, and the badge lifts onto the top edge — the same figure the three
     cards make above it, so the column keeps one rhythm all the way down */
  .area {
    flex-direction: column;
    gap: 6px;
    /* stacked it must line up with the cards, so it takes their width back */
    width: 100%;
    max-width: 420px;
    margin-top: 68px;
    padding: 56px 22px 20px;
    text-align: center;
  }

  .area__badge {
    position: absolute;
    left: 50%;
    top: -46px;
    transform: translateX(-50%);
    width: 94px;
    height: 94px;
  }

  .area__pin { width: 48px; height: 48px; }
  .area__title { font-size: 22px; }

  .area__rule {
    width: 74px;
    height: 1px;
    margin: 6px 0 2px;
    background: linear-gradient(90deg,
      rgba(155, 207, 63, 0), rgba(155, 207, 63, .55), rgba(155, 207, 63, 0));
  }

  .area__list { justify-content: center; gap: 6px 22px; }
  .area__item { font-size: 17px; }

  .services__leaf { top: -30px; width: 38px; }
  .services__leaf--left  { left: 24px; }
  .services__leaf--right { right: 24px; }

  .colophon { padding: 30px 0; }

  .colophon__inner {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 22px;
    width: min(420px, 100% - 40px);
  }

  .colophon__about {
    padding: 22px 0;
    border-left: 0;
    border-right: 0;
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    width: 100%;
  }

  .colophon__brand img { height: 58px; }
  .colophon__name { font-size: 19px; }
  .colophon__tag, .colophon__legal { font-size: 15px; }
}

/* ==========================================================================
   Motion
   ========================================================================== */

/* gated on the `js` class the document sets for itself: if the script never
   runs, nothing observes these and the cards must not stay invisible */
.js .reveal {
  opacity: 0;
  transform: translateY(14px);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .7s ease, transform .7s cubic-bezier(.22, .8, .3, 1);
}

.hero__copy > * {
  animation: rise .9s cubic-bezier(.22, .8, .3, 1) both;
}

.hero__lede { animation-delay: .12s; }
.hero__cta  { animation-delay: .24s; }

@keyframes rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal, .js .reveal.is-in, .hero__copy > * {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }
}
