/* Oakwood Marketing Services base styles.
   Simple, modern, white + green. */

:root {
  --green: #1f7a4d;
  --green-dark: #15603b;
  --green-light: #e8f3ec;
  --ink: #141816;
  --muted: #545c58;
  --white: #ffffff;
  --bg-soft: #f6f9f7;
  /* The one grass tone. This is what a tinted tree or hill actually renders as
     on the home page, so anything that has to sit flush against grass is filled
     with this flat value instead of stacking its own opacity. Two separately
     tinted layers that overlap composite to a darker patch; one flat colour
     cannot. */
  --grass: #c5ddd0;
  --border: #e2ebe5;
  --radius: 10px;
  --shadow: 0 6px 24px rgba(31, 122, 77, 0.08);
  --max: 1080px;
  --font-display: "Sora", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* The header is sticky, so jumping to an anchor would park the target
   underneath it. Stop short by the header height plus a little breathing room. */
[id] { scroll-margin-top: 96px; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--white);
  line-height: 1.6;
}

h1, h2, h3, .brand, .stat .num {
  font-family: var(--font-display);
}

a { color: var(--green); text-decoration: none; }
a:hover { text-decoration: underline; }

.container {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 20px;
}

/* ---------- Header / Nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 68px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--ink);
}
.brand:hover { text-decoration: none; }

.brand .logo-mark {
  width: 34px;
  height: 34px;
  display: inline-block;
  vertical-align: middle;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 26px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  color: var(--muted);
  font-weight: 500;
  font-size: 0.98rem;
}
.nav-links a:hover,
.nav-links a.active {
  color: var(--green);
  text-decoration: none;
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  padding: 12px 22px;
  border-radius: var(--radius);
  font-weight: 600;
  font-size: 0.98rem;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.05s ease, background 0.15s ease, color 0.15s ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--green);
  color: #fff;
}
.btn-primary:hover { background: var(--green-dark); color: #fff; }

.btn-outline {
  background: #fff;
  color: var(--green);
  border-color: var(--green);
}
.btn-outline:hover { background: var(--green-light); }

/* ---------- Hero ---------- */
.hero {
  padding: 84px 0 64px;
  /* Not clipped, and lifted above the section below it, so the glow behind the
     skyline feathers past the section edge instead of being cut off in a
     straight line along it. The cardinal only ever travels off to the left,
     which cannot pull a scrollbar in this direction, so nothing here needs the
     clip that used to be doing both jobs. */
  position: relative;
  z-index: 2;
  background:
    radial-gradient(1100px 380px at 85% -15%, var(--green-light), transparent 62%),
    linear-gradient(180deg, #ffffff 0%, var(--bg-soft) 100%);
}
.hero-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 48px;
  align-items: center;
}
.hero h1 {
  font-size: 2.9rem;
  line-height: 1.12;
  margin: 0 0 16px;
  letter-spacing: -0.8px;
  text-wrap: balance;
}
.hero .lead {
  font-size: 1.15rem;
  color: var(--muted);
  margin: 0 0 28px;
}
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; }

.hero-img {
  width: 100%;
  border-radius: 14px;
  box-shadow: var(--shadow);
  display: block;
  transition: transform 0.35s ease, box-shadow 0.35s ease;
}
.hero-img:hover {
  transform: translateY(-5px);
  box-shadow: 0 16px 40px rgba(31, 122, 77, 0.16);
}
.img-caption {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 8px;
  text-align: center;
}

.eyebrow {
  position: relative;
  display: inline-block;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--green);
  background: var(--green-light);
  padding: 6px 12px;
  border-radius: 999px;
  margin-bottom: 18px;
  box-shadow: inset 0 0 0 1px rgba(31, 122, 77, 0.14);
}

/* ---------- Sections ---------- */
section { padding: 56px 0; }
.section-soft { background: var(--bg-soft); }

.section-head { text-align: center; max-width: 640px; margin: 0 auto 40px; }
.section-head h2 { font-size: 2rem; margin: 0 0 12px; letter-spacing: -0.6px; }
.section-head p { color: var(--muted); margin: 0; font-size: 1.05rem; }

/* ---------- Cards ---------- */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px 22px;
  position: relative;
  overflow: hidden;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-3px);
  border-color: #cfe0d6;
}
/* Green accent line that draws in across the top on hover */
.card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--green), #3aa76d);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
}
.card:hover::before { transform: scaleX(1); }
/* Line icons rather than emoji: emoji render differently on every platform and
   never match the brand colour. These inherit currentColor, so the tile can
   invert cleanly on hover. */
.card .icon {
  width: 46px; height: 46px;
  border-radius: 12px;
  background: var(--green-light);
  color: var(--green);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  /* Transform only. The colour swap below lands instantly, which is how this
     has always looked: a later rule used to reset this shorthand and drop the
     colour transition. Folded in as the winning value rather than "fixed", so
     hover is unchanged. */
  transition: transform 0.2s ease;
}
.card .icon svg { width: 23px; height: 23px; display: block; }
.card:hover .icon {
  background: var(--green);
  color: #fff;
  transform: scale(1.08);
}
.card h3 { margin: 0 0 8px; font-size: 1.2rem; }
.card p { margin: 0; color: var(--muted); font-size: 0.98rem; }

/* ---------- Dashboard promo ---------- */
/* A full-bleed green band. The CTA further down is a green card floating on a
   soft background, so these two read as different objects rather than one long
   stretch of green. */
.promo-section {
  background: linear-gradient(135deg, #1f7a4d 0%, #17643e 100%);
  color: #fff;
  /* Holds the stamp, which hangs off the left edge and is clipped by it. */
  position: relative;
  overflow: hidden;
  /* Bows the top and bottom out from the centre. Deliberately not border-radius:
     corner ellipses always turn vertical where they meet the side, so the bend
     is flat across the middle and then hooks down hard at both ends. Clipping
     to one ellipse wider than the section instead means only its gentle middle
     stretch is ever on screen, so the curve carries at the same rate the whole
     way across. The width multiplier sets how even it reads (higher is flatter
     and more constant); the height stays at 50% so the band keeps its full
     height at the centre. */
  clip-path: ellipse(115% 50% at 50% 50%);
}
.promo-section > .container { position: relative; z-index: 1; }
/* The mark pressed into the left edge of the band, most of it off the page.
   Kept faint so it reads as a watermark behind the heading rather than
   competing with it, the same way the page-header leaf clusters do. */
.promo-stamp {
  position: absolute;
  left: -104px;
  top: 50%;
  transform: translateY(-50%);
  width: 340px;
  height: auto;
  opacity: 0.11;
  pointer-events: none;
  z-index: 0;
}
.promo-grid {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: 48px;
  align-items: center;
}
.promo-copy h2 {
  margin: 0 0 14px;
  font-size: 2rem;
  letter-spacing: -0.6px;
  text-wrap: balance;
}
.promo-copy p {
  margin: 0 0 26px;
  color: #d7e9de;
  font-size: 1.05rem;
}
/* Same white-on-green button the CTA band uses, so the two greens agree. */
.promo-copy .btn-primary { background: #fff; color: var(--green); }
.promo-copy .btn-primary:hover { background: var(--green-light); color: var(--green-dark); }

/* Holds its 16:9 shape whether it contains the placeholder or a real embed, so
   dropping the video in never shifts the section's height. */
.promo-video {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 14px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.08);
  box-shadow: 0 14px 38px rgba(9, 46, 29, 0.28);
}
.promo-video > iframe,
.promo-video > video {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
/* Comes out with the placeholder once a real embed goes in. */
.promo-video-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border: 1px dashed rgba(255, 255, 255, 0.35);
  border-radius: 14px;
  color: #d7e9de;
  font-size: 0.9rem;
}
.promo-video-empty .play {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.promo-video-empty .play svg { width: 22px; height: 22px; margin-left: 3px; }

/* ---------- CTA band ---------- */
.cta-band {
  background: linear-gradient(135deg, #238755 0%, #16603c 100%);
  color: #fff;
  border-radius: 16px;
  padding: 44px;
  text-align: center;
  box-shadow: 0 12px 32px rgba(31, 122, 77, 0.18);
}
.cta-band h2 { margin: 0 0 10px; font-size: 1.8rem; }
.cta-band p { margin: 0 0 24px; color: #dcece3; font-size: 1.05rem; }
.cta-band .btn-primary { background: #fff; color: var(--green); }
.cta-band .btn-primary:hover { background: var(--green-light); }

/* ---------- Pricing ---------- */
/* Builds and care plans are priced in different units, so they get their own
   blocks rather than sharing one grid where $500 could be read as monthly. */
.price-group-title {
  font-size: 1.35rem;
  margin: 0 0 4px;
  text-align: center;
}
.price-group-note {
  text-align: center;
  color: var(--muted);
  margin: 0 0 26px;
  font-size: 0.98rem;
}
/* Access is not a tier feature, so it sits above the cards as one promise
   that applies to all of them. */
.price-promise {
  max-width: 660px;
  margin: 0 auto 26px;
  padding: 14px 20px;
  background: var(--green-light);
  border: 1px solid #cfe4d8;
  border-radius: var(--radius);
  text-align: center;
  font-size: 0.95rem;
  color: var(--ink);
}
.price-promise strong { color: var(--green); }

.price-cards {
  display: grid;
  gap: 20px;
  margin-bottom: 48px;
  align-items: start;
}
.price-cards.two   { grid-template-columns: repeat(2, 1fr); max-width: 780px; margin-inline: auto; margin-bottom: 54px; }
/* Both build cards carry a badge that hangs over the top edge, so the row needs
   room for it. */
.price-cards.two .price-card { margin-top: 12px; }
.price-cards.three { grid-template-columns: repeat(3, 1fr); }

.price-card {
  position: relative;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.price-card h4 {
  font-family: var(--font-display);
  font-size: 1.25rem;
  margin: 0 0 6px;
}
.price-for {
  color: var(--muted);
  font-size: 0.94rem;
  margin: 0 0 18px;
  min-height: 2.6em;
}
.price {
  font-family: var(--font-display);
  font-size: 2.2rem;
  font-weight: 700;
  color: var(--green);
  line-height: 1;
  margin-bottom: 20px;
  font-variant-numeric: tabular-nums;
}
.price .from,
.price .to,
.price .per {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--muted);
}
.price .from { margin-right: 7px; }
.price .to { margin: 0 6px; }

/* The struck list price sitting beside the offer. Deliberately smaller and in
   the body face, so the number people actually pay stays the loud one. <s>
   rather than a styled span, so it is announced as no longer current instead of
   read out as a second price. */
.price-was {
  font-family: var(--font-body);
  font-size: 1.1rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration-thickness: 2px;
  margin-right: 10px;
  vertical-align: middle;
}
/* Sits under the price, so the offer has a reason and an end rather than just
   being a lower number. */
.price-note {
  margin: -12px 0 20px;
  font-size: 0.86rem;
  color: var(--muted);
}

.price-list {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
  flex: 1;
}
.price-list li {
  position: relative;
  padding: 6px 0 6px 26px;
  font-size: 0.95rem;
  color: var(--ink);
}
.price-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 13px;
  width: 11px;
  height: 6px;
  border-left: 2px solid var(--green);
  border-bottom: 2px solid var(--green);
  transform: rotate(-45deg);
}
.price-card .btn { width: 100%; text-align: center; }

/* The middle plan is the one to steer toward, so it gets the weight */
.price-card.featured {
  border: 2px solid var(--green);
  box-shadow: var(--shadow);
}
.price-badge {
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--green);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 5px 13px;
  border-radius: 999px;
  white-space: nowrap;
}

.price-footnote {
  text-align: center;
  color: var(--muted);
  font-size: 0.92rem;
  max-width: 560px;
  margin: 0 auto;
}

@media (max-width: 820px) {
  .price-cards.two,
  .price-cards.three { grid-template-columns: 1fr; }
  .price-for { min-height: 0; }
  .price-card.featured { margin-top: 10px; }
}

/* ---------- Page header (subpages) ---------- */
.page-header {
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border);
  padding: 42px 0;
  text-align: center;
}
.page-header h1 { margin: 0 0 10px; font-size: 2.3rem; letter-spacing: -0.6px; }
.page-header p { margin: 0; color: var(--muted); font-size: 1.1rem; }

/* Leaf clusters peeking in from both edges, echoing the canopy on the home
   page. Clipped by the section so only part of each cluster shows. */
.page-header { position: relative; overflow: hidden; }
.page-header > .container { position: relative; z-index: 1; }
/* Sized off the section's height so the cluster spans it top to bottom, and
   the width follows from the artwork's own proportions. */
.leaf-cluster {
  position: absolute;
  top: 0;
  height: 100%;
  width: auto;
  opacity: 0.15;
  pointer-events: none;
  z-index: 0;
}
.leaf-cluster.left  { left: 0;  transform: translateX(-34%); }
.leaf-cluster.right { right: 0; transform: translateX(34%) scaleX(-1); }

@media (max-width: 700px) {
  .leaf-cluster { opacity: 0.1; }
  .leaf-cluster.left  { transform: translateX(-48%); }
  .leaf-cluster.right { transform: translateX(48%) scaleX(-1); }
}

/* ---------- About ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  margin-top: 40px;
}
.stat { text-align: center; }
.stat .num { font-size: 2rem; font-weight: 800; color: var(--green); }
.stat .label { color: var(--muted); font-size: 0.95rem; }

/* ---------- Founder ---------- */
/* Tighter top padding and a top-aligned photo, so the portrait is visible
   without scrolling instead of being pushed down by the taller text column. */
.founder-section { padding-top: 40px; }
.founder-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 46px;
  align-items: start;
}

/* The photo sits in a frame offset behind it, so the portrait reads as placed
   rather than dropped in. */
.founder-photo {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  border: 3px solid var(--green);
  background: linear-gradient(160deg, var(--green-light), #dcece3);
  /* A definite height, so the photo's height:100% actually resolves. With only
     a min-height the frame had no definite size, the photo fell back to its own
     3:2 ratio, and the fallback gradient showed as a strip under it. */
  aspect-ratio: 4 / 3;
  min-height: 260px;
  display: flex;
  box-shadow: 14px 14px 0 rgba(31, 122, 77, 0.14);
}
.founder-photo img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Sits behind the photo, so it only shows if the file is not there yet */
.photo-fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 20px;
  text-align: center;
  color: var(--green);
  font-size: 0.88rem;
}
.photo-fallback svg { width: 34px; height: 34px; }
.photo-fallback code {
  font-size: 0.82rem;
  background: rgba(255, 255, 255, 0.7);
  padding: 2px 6px;
  border-radius: 5px;
}

.founder-label {
  display: inline-block;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--green);
  background: var(--green-light);
  padding: 6px 12px;
  border-radius: 999px;
  margin-bottom: 14px;
}
.founder-copy h2 {
  margin: 0 0 2px;
  font-size: 2.1rem;
  letter-spacing: -0.5px;
}
.founder-role {
  margin: 0 0 18px;
  color: var(--muted);
  font-weight: 600;
  font-size: 0.98rem;
}
.founder-copy p { color: var(--ink); font-size: 1.02rem; }

.founder-contact {
  list-style: none;
  margin: 22px 0 24px;
  padding: 0;
  display: grid;
  gap: 11px;
}
.founder-contact li {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 0.98rem;
  color: var(--ink);
}
.founder-contact svg {
  width: 19px;
  height: 19px;
  color: var(--green);
  flex-shrink: 0;
}
.founder-contact a { color: var(--ink); }
.founder-contact a:hover { color: var(--green); }

/* The three words close the section, separated by a rule rather than a gap so
   they read as a summary of what is above rather than a new section. The rule
   is a short centred pseudo-element, not a border on the box: a border runs the
   full container width and cuts straight across the trees standing at either
   edge. */
.founder-stats {
  position: relative;
  margin-top: 52px;
  padding-top: 38px;
}
.founder-stats::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 260px;
  max-width: 58%;
  height: 1px;
  transform: translateX(-50%);
  background: var(--border);
}

@media (max-width: 820px) {
  .founder-grid { grid-template-columns: 1fr; gap: 30px; }
  .founder-photo { box-shadow: 9px 9px 0 rgba(31, 122, 77, 0.14); }
  .founder-stats { margin-top: 38px; padding-top: 30px; }
}

/* ---------- Portfolio ---------- */
/* Three columns on desktop, two on tablet, one on phone. The screenshot sits
   in its own framed box so a real image can drop straight in later without
   any layout change. */
.work-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 26px;
}
/* The default card border is a near-white green that vanishes against the
   grass field behind this section, leaving the cards looking like they bleed
   into the background. A firmer edge plus a resting shadow lifts them off it,
   and keeps them looking deliberate on the plain white pages too. */
.work-card {
  background: #fff;
  border: 1px solid #cbdfd4;
  border-radius: var(--radius);
  overflow: hidden;
  padding: 0 0 22px;
  box-shadow: 0 6px 18px rgba(18, 78, 49, 0.10);
  transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.work-card:hover {
  box-shadow: 0 14px 30px rgba(18, 78, 49, 0.16);
  transform: translateY(-3px);
  border-color: var(--green);
}

/* Fixed 8:5 frame so every card lines up whether the artwork inside is a
   placeholder mockup or a real screenshot.

   Inset rather than bled to the card edge. Full bleed put the card's own pale
   border directly against the dark artwork inside, and a pale hairline on dark
   green reads as a white seam. Held off the edge, the border only ever meets
   white, and the screenshot reads as framed. */
.work-shot {
  aspect-ratio: 8 / 5;
  margin: 18px 18px 0;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.work-shot svg,
.work-shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* Text shares the screenshot's 18px gutter so every edge inside the card
   lines up on one column. */
.work-card h3 {
  margin: 18px 18px 4px;
  font-size: 1.15rem;
}
.work-meta {
  margin: 0 18px 10px;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--green);
}
.work-card p:not(.work-meta) {
  margin: 0 18px;
  color: var(--muted);
  font-size: 0.98rem;
}

@media (max-width: 960px) {
  .work-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .work-grid { grid-template-columns: 1fr; }
}

/* Home page only. The portfolio begins on the grass colour itself, so the hill
   above appears to run straight into it, then the ground fades out over the
   top half of the section.

   --grass is what the grass actually renders as, not what it is declared as:
   the hill is #1f7a4d drawn inside .section-accents at opacity 0.20, sitting
   on the scene gradient's end colour #eef6f1. Compositing those gives that
   value, and matching it exactly is what makes the seam disappear. If any of
   those three change, the token has to be recomputed with them. */
.work-field {
  background: linear-gradient(180deg, var(--grass) 0%, var(--bg-soft) 55%);
  /* Hides the cardinal while it is still off to the right, which also stops
     the run-up from widening the page. */
  overflow: hidden;
}
/* .section-accents is hidden at this width, so there is no hill to meet and
   the scene ends on its bare gradient instead. */
@media (max-width: 900px) {
  .work-field { background: linear-gradient(180deg, #eef6f1 0%, var(--bg-soft) 55%); }
}

/* ---------- Cardinal on the portfolio ---------- */
/* The same bird and the same three tracks as the hero, untouched. The only
   addition is scaleX(-1) on this wrapper, which mirrors the whole coordinate
   system: the run-up starts off the right edge instead of the left, the swoop
   reads the same way round, and the bird faces the way it is travelling.
   Anchored to the grid rather than to a card, because the cards clip their own
   overflow and would cut the flight off. */
.work-field .work-grid { position: relative; }
.cardinal-perch {
  position: absolute;
  right: 46px;
  bottom: 100%;
  line-height: 0;
  pointer-events: none;
  transform: scaleX(-1);
  z-index: 2;
}
/* .cardinal-flight carries the animation; the perch handles placement. */
.cardinal-perch .cardinal-flight {
  position: static;
  display: block;
}
/* Held on the first frame until the section is actually on screen, otherwise
   the flight is over before anyone scrolls down to it. */
.cardinal-perch .cardinal-flight,
.cardinal-perch .cardinal,
.cardinal-perch .bird { animation-play-state: paused; }
.cardinal-perch.visible .cardinal-flight,
.cardinal-perch.visible .cardinal,
.cardinal-perch.visible .bird { animation-play-state: running; }

/* One column at this width, so there is no rightmost card to aim at and not
   much room for the run-up. */
@media (max-width: 640px) {
  .cardinal-perch { display: none; }
}

/* ---------- Egg on the CTA ---------- */
/* One egg set half into the band's top edge, lit from behind. The glow breathes
   slowly and the egg itself only stirs once every several seconds, so it reads
   as something alive rather than as an ornament that jitters. */
.cta-band { position: relative; }
.egg-mark {
  position: absolute;
  bottom: 100%;
  left: 50%;
  width: 96px;
  height: 96px;
  margin-left: -48px;
  margin-bottom: -34px;   /* a little over a third sits over the band */
  z-index: 2;
  pointer-events: none;
}
.egg-glow {
  position: absolute;
  inset: -58%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 216, 145, 0.60) 0%,
    rgba(255, 205, 120, 0.26) 44%,
    rgba(255, 205, 120, 0) 70%);
  filter: blur(16px);
  animation: eggGlow 5s ease-in-out infinite alternate;
}
.egg {
  position: relative;
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 4px 10px rgba(12, 48, 32, 0.22));
  transform-origin: 50% 82%;
  animation: eggStir 8s ease-in-out infinite;
}
@keyframes eggGlow {
  from { opacity: 0.55; transform: scale(0.93); }
  to   { opacity: 1;    transform: scale(1.07); }
}
@keyframes eggStir {
  0%, 88%, 100% { transform: rotate(0deg); }
  91% { transform: rotate(-2.2deg); }
  94% { transform: rotate(1.6deg); }
  97% { transform: rotate(-0.7deg); }
}

/* ---------- About: founder section shares the home page landscape ---------- */
/* Same .scene backdrop, trees and hill as the home page, so the two read as one
   world. Trees sit in .section-accents at z-index 0 and the section content at
   z-index 1, which is what keeps them behind the photo and the copy. */
/* Enough room under the stats that they clear the 140px hill band completely,
   rather than being cut through the middle by it. */
.about-scene .founder-section { padding-bottom: 190px; }
/* Back on the plain backdrop above the hill, so the brand green reads fine. */

/* ---------- Cardinal landing on the founder label ---------- */
/* A separate, longer flight from the hero's. Same shape of motion, dip then
   stall then straight down, just a longer run-in and more time to cover it.
   The hero's timings are untouched. */
.founder-label { position: relative; display: inline-block; }
/* The bird flies in from the left, so it crosses the portrait in the other
   column on its way to the label. The portrait is position:relative and this
   column was static, and a positioned element paints over a non-positioned one
   whatever the DOM order, so the photo's frame sliced the cardinal in half
   mid-flight. Lifting the whole column puts the flight path in front. */
.founder-copy { position: relative; z-index: 2; }
.cardinal-flight-long {
  position: absolute;
  right: 16px;
  bottom: 100%;
  line-height: 0;
  pointer-events: none;
  animation: flyAcrossLong 4.4s linear 0.6s both;
}
.cardinal-flight-long .cardinal {
  display: block;
  width: 40px;
  height: auto;
  animation: flyArcLong 4.4s linear 0.6s both;
}
.cardinal-flight-long .bird {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: pitchLong 4.4s linear 0.6s both;
}
/* Held on the first frame until the column is actually on screen, the same way
   the portfolio perch is. The flight is keyed to page load, so without this it
   is over before anyone scrolls down and the bird is simply there, already
   perched, instead of arriving. .founder-copy carries .reveal, so it picks up
   .visible from the same observer that fades the column in. */
.cardinal-flight-long,
.cardinal-flight-long .cardinal,
.cardinal-flight-long .bird { animation-play-state: paused; }
.founder-copy.visible .cardinal-flight-long,
.founder-copy.visible .cardinal-flight-long .cardinal,
.founder-copy.visible .cardinal-flight-long .bird { animation-play-state: running; }
@keyframes flyAcrossLong {
  0%   { transform: translateX(calc(-640px - 26vw)); animation-timing-function: cubic-bezier(0.225, 0.488, 0.42, 1); }
  80%  { transform: translateX(0); }
  100% { transform: translateX(0); }
}
@keyframes flyArcLong {
  0%   { transform: translateY(-92px); animation-timing-function: cubic-bezier(0.35, 0.22, 0.55, 1); }
  38%  { transform: translateY(36px);  animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  80%  { transform: translateY(-26px); animation-timing-function: cubic-bezier(0.48, 0, 0.35, 1); }
  100% { transform: translateY(0); }
}
@keyframes pitchLong {
  0%   { transform: rotate(16deg);  animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  38%  { transform: rotate(0deg);   animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  80%  { transform: rotate(-15deg); animation-timing-function: cubic-bezier(0.48, 0, 0.35, 1); }
  100% { transform: rotate(0deg); }
}

/* ---------- Grove: the section sits inside one big oak ---------- */
/* The heading sits inside the canopy, the services flank the trunk, and the
   trunk runs down into the hill. Nothing is clipped, so the canopy can break a
   little above the section edge; the copy keeps z-index 1 and the tree 0, so
   the tree always passes behind. */
.grove { position: relative; }
.grove-top { position: relative; }
.grove > section,
.grove-top > section { position: relative; z-index: 1; background: transparent; }

/* Canopy, trunk, hill and the CTA below it are all filled with the same flat
   --grass, at full opacity. Nothing here is tinted, so the places they overlap
   (trunk through canopy, trunk into hill, hill into the CTA) are the same
   colour on both sides and the joins simply do not exist. Tinting each layer
   separately is what produced the dark patch where they crossed: two 26% layers
   over each other read as 45%. */
.grove-tree {
  position: absolute;
  left: 50%;
  top: -34px;
  bottom: 34px;
  width: min(1040px, 96vw);
  transform: translateX(-50%);
  z-index: 0;
  pointer-events: none;
}
.grove-canopy {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: auto;
  fill: var(--grass);
}
/* Starts up inside the canopy, where it is hidden, so only the length between
   the leaves and the hill is ever seen. Thick enough to carry a canopy this
   size: a thin stem under a round crown reads as a shrub. */
.grove-trunk {
  position: absolute;
  left: 50%;
  top: 50%;
  bottom: 0;
  width: 96px;
  transform: translateX(-50%);
  background: var(--grass);
  /* Flares out at the base, the way the CTA trees do. */
  clip-path: polygon(32% 0, 68% 0, 100% 100%, 0 100%);
}

/* Tall enough to hold the canopy with the copy across its middle, where it is
   widest, and to leave a length of trunk standing between the leaves and the
   hill. */
.custom-services { padding: 170px 0 235px; }

/* Type inside the canopy. This copy sits on green rather than white, so the
   muted grey the rest of the site uses for body text goes dingy on it; these
   are the tones the CTA band already uses on grass. The heading runs bigger and
   tighter than a normal section head because it has a lot of open leaf around
   it, and both lines are balanced so the wrap follows the round shape instead
   of leaving one orphan word. */
/* A narrow measure on purpose: a wide block of centred text fights a round
   shape, and at 640px the second line broke in the middle of "trying to do". */
.custom-services .section-head { max-width: 450px; margin-bottom: 0; }
.custom-services .section-head h2 {
  font-size: 2.35rem;
  line-height: 1.12;
  letter-spacing: -1px;
  text-wrap: balance;
}
.custom-services .section-head p {
  margin-top: 14px;
  color: #3f4d46;
  font-size: 1.02rem;
  line-height: 1.55;
  text-wrap: pretty;
}

/* Services either side of the trunk. The middle track is empty on purpose:
   it is the lane the trunk runs down. */
.grove-cols {
  display: grid;
  /* The middle track is the lane the trunk runs down. Kept tight: any wider and
     the two columns stop reading as one list. */
  grid-template-columns: 1fr 110px 1fr;
  gap: 16px 20px;
  /* Narrower than the canopy is wide, so no list item ever pokes out past the
     leaves. */
  max-width: 780px;
  margin: 46px auto 0;
  align-items: start;
}
.grove-col { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; }
.grove-col.left { text-align: right; }
.grove-col li {
  position: relative;
  font-size: 1.02rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink);
  padding: 0 4px;
}
/* The same tick the pricing lists use, on the side facing the trunk. It was a
   pair of mirrored leaf shapes, but mirroring pointed the two columns opposite
   ways and at this size the shape read as a sliver rather than a leaf. */
.grove-col li::before {
  content: "";
  position: absolute;
  top: 0.5em;
  width: 11px;
  height: 6px;
  border-left: 2px solid var(--green);
  border-bottom: 2px solid var(--green);
  transform: rotate(-45deg);
}
.grove-col.left li  { padding-right: 26px; }
.grove-col.left li::before  { right: 2px; }
.grove-col.right li { padding-left: 26px; }
.grove-col.right li::before { left: 2px; }

/* The canopy shrinks with the viewport, so the room the copy needs inside it
   shrinks too. */
@media (max-width: 1080px) {
  .custom-services { padding: 145px 0 195px; }
}
@media (max-width: 900px) {
  .custom-services { padding: 115px 0 175px; }
}

@media (max-width: 820px) {
  .grove-cols { grid-template-columns: 1fr; max-width: 420px; }
  .grove-col.left { text-align: left; }
  .grove-col.left li { padding: 0 0 0 26px; }
  .grove-col.left li::before { right: auto; left: 2px; }
  .grove-lane { display: none; }
  .grove-tree { display: none; }
  /* No tree to sit inside, so the section goes back to normal spacing. */
  .custom-services { padding: 56px 0 24px; }
}

.grove-hill { display: block; width: 100%; height: 130px; fill: var(--grass); }

/* The same flat --grass the hill is drawn in, so the slope runs straight into
   the CTA with nothing to mark where one ends and the other starts. */
.grove > .grove-cta {
  background: var(--grass);
  padding: 8px 0 78px;
}
.cta-band.bare {
  background: none;
  box-shadow: none;
  border-radius: 0;
  padding: 10px 20px 0;
}
/* Dark type, not white: the hill tone is far too light to carry white text. */
.cta-band.bare h2 { color: var(--ink); }
.cta-band.bare p  { color: #3f4d46; }

/* ---------- Contact ---------- */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: start;
}
.form-group { margin-bottom: 18px; }
.form-group label {
  display: block;
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 6px;
}
.form-group input,
.form-group textarea,
.form-group select {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 1rem;
  font-family: inherit;
  background: #fff;
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
  outline: none;
  border-color: var(--green);
  box-shadow: 0 0 0 3px var(--green-light);
}
.contact-info .info-item {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-bottom: 20px;
}
/* Line icons in a tile rather than emoji: emoji render differently on every
   platform, sit on their own baseline, and never match the brand colour. */
.contact-info .info-item .icon {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--green-light);
  color: var(--green);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.contact-info .info-item .icon svg { width: 19px; height: 19px; display: block; }
.form-note {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 10px;
}
.form-note a { color: var(--green); font-weight: 600; }
/* Draws the eye to the fallback email when someone submits while the form is
   still in demo mode, instead of the click seeming to do nothing. */
.form-note-flash { animation: noteFlash 0.9s ease; }
@keyframes noteFlash {
  0%, 100% { color: var(--muted); }
  30%      { color: var(--green); }
}

.optional { font-weight: 400; color: var(--muted); }

.form-status {
  margin-top: 14px;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 12px 14px;
  border-radius: 10px;
}
.form-success {
  color: #14603c;
  background: var(--green-light);
  border: 1px solid #bcd9c8;
}
.form-error {
  color: #8a3116;
  background: #fdf0ea;
  border: 1px solid #f3d3c4;
}
.form-error a { color: #8a3116; text-decoration: underline; }

/* ---------- Footer ---------- */
.site-footer {
  background: var(--ink);
  color: #cdd6d0;
  padding: 40px 0 28px;
  margin-top: 0;
}
.footer-grid {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}
.site-footer a { color: #cdd6d0; }
.site-footer a:hover { color: #fff; }
.footer-links { display: flex; gap: 20px; list-style: none; margin: 0; padding: 0; }
.footer-contact {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
  font-size: 0.9rem;
  color: #cfd8d2;
  text-align: right;
}
.footer-contact a { color: #cfd8d2; }
.footer-contact a:hover { color: #fff; }
@media (max-width: 820px) {
  .footer-contact { text-align: left; }
}
/* The brand mark would inherit the dark ink colour on the dark footer. */
.site-footer .brand { color: #fff; }
/* Honeypot: pushed off-screen rather than display:none, which bots skip. */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.12);
  margin-top: 24px;
  padding-top: 18px;
  font-size: 0.85rem;
  color: #9fb0a6;
  text-align: center;
}

/* ---------- Responsive ---------- */
@media (max-width: 820px) {
  .nav-links {
    display: none;
    position: absolute;
    top: 68px;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: #fff;
    border-bottom: 1px solid var(--border);
    padding: 8px 0;
  }
  .nav-links.open { display: flex; }
  .nav-links li { width: 100%; }
  .nav-links a { display: block; padding: 12px 20px; }
  .nav-toggle { display: inline-block; }

  .hero-grid { grid-template-columns: 1fr; }
  .promo-grid { grid-template-columns: 1fr; gap: 32px; }
  /* One column here, so the copy runs the full width and the stamp would sit
     under the text rather than beside it. */
  .promo-stamp { display: none; }
  .cards { grid-template-columns: 1fr; }
  .stats { grid-template-columns: 1fr; gap: 28px; }
  .contact-grid { grid-template-columns: 1fr; }
  .hero h1 { font-size: 2.1rem; }
  .cta-band { padding: 32px 20px; }
}

/* ---------- Process steps ---------- */

/* One continuous scene shared by "What we do" and "How we work".
   The trees span both sections so the two read as a single landscape. */
/* Deliberately not clipped. The clip lives on .section-accents instead, which
   covers the same box, so the trees still cannot widen the page while anything
   inside a section is free to cross the scene's edges: the About cardinal
   climbs about 90px above the founder section on its run-in, and an
   overflow:hidden here sheared the top off it. */
.scene {
  position: relative;
  background: linear-gradient(180deg, var(--bg-soft) 0%, #eef6f1 100%);
}
.scene > section {
  position: relative;
  z-index: 1;
  background: transparent;
}
.how-it-works { padding-bottom: 150px; }
.how-it-works > .container { position: relative; z-index: 1; }
/* One group, one opacity, so same-colored overlaps merge seamlessly */
.section-accents {
  position: absolute;
  inset: 0;
  /* The trees hang half a width past both edges, so this is what keeps them
     from widening the page. It used to be on .scene, which also clipped the
     cardinal. */
  overflow: hidden;
  opacity: 0.20;
  pointer-events: none;
  z-index: 0;
}
.tree-accent {
  position: absolute;
  top: 0;
  height: 100%;
  width: auto;
}
.tree-accent.left { left: 0; transform: translateX(-50%); }
.tree-accent.right { right: 0; transform: translateX(50%); }

/* Leaves sway gently, pivoting where the canopy meets the trunk */
@keyframes sway {
  from { transform: rotate(-2.6deg); }
  to   { transform: rotate(2.6deg); }
}
.canopy {
  transform-box: fill-box;
  transform-origin: 50% 88%;
  animation: sway 7s ease-in-out infinite alternate;
}
/* Offset the right-hand tree so the pair never moves in lockstep */
.right .canopy {
  animation-duration: 9.2s;
  animation-delay: -3.1s;
}
.hill-accent {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 140px;
}
@media (max-width: 900px) {
  .section-accents { display: none; }
}

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 30px;
  max-width: 900px;
  margin: 0 auto;
}
.step { text-align: center; padding: 0 8px; }
.step .step-num {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  box-shadow: 0 6px 16px rgba(31, 122, 77, 0.22);
}
.step h3 { margin: 0 0 8px; font-size: 1.2rem; }
.step p { margin: 0; color: var(--muted); font-size: 0.98rem; }

/* ---------- Elevated design pass ---------- */

/* Nav "Get a Quote" button: keep white text (override the gray nav-link color) */
.nav-links a.btn-primary,
.nav-links a.btn-primary:hover {
  color: #fff;
}

/* Nav "Client Login": a white box out on the corner, past the sales button.
   .nav-links a sets the muted grey on every link and outranks .btn-outline, so
   the green has to be restated here. Slightly tighter than the quote button so
   the pair reads as one CTA and one quiet door, not two equal offers. */
.nav-links a.nav-login,
.nav-links a.nav-login:hover {
  color: var(--green);
  padding: 10px 16px;
  margin-left: 4px;
}

/* Primary button: lift + soft shadow so it reads as clickable */
.btn-primary {
  box-shadow: 0 6px 16px rgba(31, 122, 77, 0.22);
}
.btn-primary:hover {
  box-shadow: 0 10px 24px rgba(31, 122, 77, 0.30);
  transform: translateY(-1px);
}

/* ---------- Ambient light behind the skyline ---------- */
/* Two soft lights drifting at different speeds and sizes. Because they move
   out of step, the colour where they overlap keeps shifting, so the glow
   changes rather than just pulsing. */
.hero-visual { position: relative; }
.hero-visual::before,
.hero-visual::after {
  content: "";
  position: absolute;
  /* Rectangular, matching the illustration's shape, so the blur feathers out
     into an even halo along its edges instead of an ellipse bulging at the
     middle of each side. */
  /* Reaches further below than above on purpose: that surplus is what crosses
     the section edge and keeps the falloff soft on the other side. */
  inset: -8% -6% -26%;
  border-radius: 30px;
  z-index: 0;
  filter: blur(46px);
  pointer-events: none;
}
/* cool light, brand green */
.hero-visual::before {
  background: rgba(62, 207, 142, 0.26);
  animation: glowCool 15s ease-in-out infinite alternate;
}
/* warm light, picked up from the cardinal's beak */
.hero-visual::after {
  background: rgba(240, 160, 42, 0.15);
  animation: glowWarm 21s ease-in-out infinite alternate;
}
.hero-img,
.hero-visual .img-caption { position: relative; z-index: 1; }

/* Small drifts, so the halo brightens along one edge and then another */
@keyframes glowCool {
  from { transform: translate(-2.5%, -2%) scale(1);    opacity: 0.65; }
  to   { transform: translate(3%, 2.5%)   scale(1.05); opacity: 1; }
}
@keyframes glowWarm {
  from { transform: translate(3%, 2%)     scale(1.04); opacity: 0.5; }
  to   { transform: translate(-2.5%, -2%) scale(0.97); opacity: 0.85; }
}

/* ---------- Cardinal ---------- */
/* The SVG is cropped so its bottom edge is exactly the bird's feet, and the
   box sits directly on top of the pill. Feet on the tag, no guesswork.
   The swoop comes from moving across and up-and-down at the same time:
   horizontal glides in once, vertical dips below the tag and rises back. */
.cardinal-flight {
  position: absolute;
  right: 14px;
  bottom: 100%;
  line-height: 0;
  pointer-events: none;
  animation: flyAcross 3.00s linear 0.5s both;
}
.cardinal {
  display: block;
  width: 42px;
  height: auto;
  animation: flyArc 3.00s linear 0.5s both;
}

/* Values dialled in on the flight rig. Each track runs linear at the top level
   and carries its easing per keyframe, so every segment is shaped on its own.

   Horizontal: one decelerating run, all of it spent by the stall at 76%, which
   is what leaves the landing purely vertical.
   Vertical: down to the low point at 35%, up to the stall, then straight down.
   Pitch: nose down diving, level at the low point, nose up in the flare. */
@keyframes flyAcross {
  /* The tag sits a viewport-dependent distance from the left edge, so the
     run-up is part fixed, part vw. That keeps him starting past the edge on a
     laptop and on a wide monitor alike. */
  0%   { transform: translateX(calc(-330px - 20vw)); animation-timing-function: cubic-bezier(0.225, 0.488, 0.42, 1); }
  76%  { transform: translateX(0); }
  100% { transform: translateX(0); }
}
@keyframes flyArc {
  0%   { transform: translateY(-60px); animation-timing-function: cubic-bezier(0.35, 0.22, 0.55, 1); }
  35%  { transform: translateY(24px);  animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  76%  { transform: translateY(-20px); animation-timing-function: cubic-bezier(0.48, 0, 0.35, 1); }
  100% { transform: translateY(0); }
}
.cardinal .bird {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: pitch 3.00s linear 0.5s both;
}
@keyframes pitch {
  0%   { transform: rotate(15deg);  animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  35%  { transform: rotate(0deg);   animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
  76%  { transform: rotate(-15deg); animation-timing-function: cubic-bezier(0.48, 0, 0.35, 1); }
  100% { transform: rotate(0deg); }
}

/* ---------- CTA trees ---------- */
/* Same tree and the same sway; these just grow up out of the ground when the
   section scrolls into view. */
/* Not clipped: the glow behind the egg reaches above the section edge, and
   overflow:hidden here cut it off in a straight line the same way the hero
   used to. The growing trees sit well inside the section, so nothing needed
   the clip. */
/* Headroom for the egg, which hangs 62px above the band, plus its glow, which
   reaches 56px past that again (inset -58% of a 96px box). 118px is the exact
   floor; below it the glow starts touching the section above, which is a
   dashboard band clipped to an ellipse, so it bows down at its centre exactly
   where the egg is. */
.cta-section { position: relative; padding-top: 120px; }
.cta-section > .container { position: relative; z-index: 1; }
.cta-tree {
  position: absolute;
  bottom: 0;
  /* The artwork is 120x200, so this width renders about 327px tall and the
     trunk carries the tree up through the section rather than sitting in the
     bottom corner. */
  width: 196px;
  height: auto;
  z-index: 0;
  transform-origin: 50% 100%;
  transform: scaleY(0.02) scaleX(0.5);
  transition: transform 1.3s cubic-bezier(0.20, 0.85, 0.30, 1);
}
.cta-tree.visible { transform: none; }
.cta-tree.left  { left: 2%; }
.cta-tree.right { right: 2%; transition-delay: 0.18s; }
.cta-section .cta-band { max-width: 760px; margin: 0 auto; }

@media (max-width: 1080px) {
  .cta-tree { display: none; }
  .cta-section .cta-band { max-width: none; }
}

/* ---------- Scroll reveal ---------- */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, transform;
}
.reveal.visible {
  opacity: 1;
  transform: none;
}
/* Stagger children within a group */
.reveal-group > *:nth-child(1) { transition-delay: 0.00s; }
.reveal-group > *:nth-child(2) { transition-delay: 0.10s; }
.reveal-group > *:nth-child(3) { transition-delay: 0.20s; }
.reveal-group > *:nth-child(4) { transition-delay: 0.30s; }

/* ---------- Page load sequence ---------- */
@keyframes riseIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
.hero .eyebrow,
.hero h1,
.hero .lead,
.hero-actions,
.hero-img,
.hero .img-caption {
  animation: riseIn 0.75s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.hero .eyebrow      { animation-delay: 0.05s; }
.hero h1            { animation-delay: 0.15s; }
.hero .lead         { animation-delay: 0.25s; }
.hero-actions       { animation-delay: 0.35s; }
.hero-img           { animation-delay: 0.30s; }
.hero .img-caption  { animation-delay: 0.45s; }

/* Nav underline sweep */
.nav-links a:not(.btn) {
  position: relative;
}
.nav-links a:not(.btn)::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 100%;
  height: 2px;
  background: var(--green);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.28s ease;
}
.nav-links a:not(.btn):hover::after,
.nav-links a:not(.btn).active::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* Step badge pulse on hover */
.step .step-num { transition: transform 0.25s ease, box-shadow 0.25s ease; }
.step:hover .step-num {
  transform: translateY(-4px) scale(1.06);
  box-shadow: 0 12px 26px rgba(31, 122, 77, 0.32);
}

/* Respect users who prefer less motion */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .cta-tree { transform: none; transition: none; }
  .canopy { animation: none; }
  .egg, .egg-glow { animation: none; }
  .hero-visual::before,
  .hero-visual::after { animation: none; }
  .cardinal-flight,
  .cardinal-flight-long,
  .cardinal,
  .cardinal .bird { animation: none; transform: none; }
  .cardinal { width: 42px; }
  .hero .eyebrow,
  .hero h1,
  .hero .lead,
  .hero-actions,
  .hero-img,
  .hero .img-caption { animation: none; }
  * {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .card:hover { transform: none; }
  .hero-img:hover { transform: none; }
}

/* ---------- Scroll root ---------- */
/* Sits above section backgrounds, not behind them: a tinted section paints its
   own background and would otherwise chop the root into pieces. Safe on top
   because it stays in the gutter, clear of the text column, and takes no
   pointer events. Hidden where there is no gutter to spare. */
.root-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  pointer-events: none;
  z-index: 3;
}
.root-layer svg { display: block; width: 100%; height: 100%; }
@media (max-width: 1200px) {
  .root-layer { display: none; }
}
