/* ============================================================
   Kelpie Landscape & Build - foundation
   Tokens taken verbatim from the client brand document.
   Static build. Ports to a WordPress theme style.css as-is.
   ============================================================ */

/* ---------- 1. Tokens ---------- */
:root {
  /* Client palette, exact */
  --green-900: #0B1A13; /* Deep Forest Green, dominant */
  --green-800: #0F2219; /* derived, section alternation within the dark family */
  --green-700: #162E23; /* Muted Sage, cards and containers */
  --green-600: #1E3B2D; /* derived, hairlines and hover surfaces */
  --gold:      #C5A880; /* Champagne Gold, client value. Hairlines, icons, eyebrows, hover. */
  --gold-tint: #EADFC9; /* Subtle Gold Tint, borders and badge fills */

  /* Solid CTA fills only. #C5A880 is correct as a line and as an icon, but as a large
     block against #0B1A13 it desaturates and reads beige rather than metallic. This is
     the same hue pushed warmer and cleaner so a filled button still reads as gold.
     The client's own guidance is that gold works through restraint, so the champagne
     value stays untouched everywhere it is used as an accent. */
  --gold-cta:       #D0A85C;
  --gold-cta-hover: #DFBB78;
  --paper:     #F8FAF9; /* Soft Light Neutral, alternating light sections */
  --white:     #FFFFFF;

  /* Text roles */
  --ink-on-dark:      #F8FAF9;
  --ink-on-dark-mute: rgba(248, 250, 249, 0.62);
  --ink-on-paper:      #0B1A13;
  --ink-on-paper-mute: rgba(11, 26, 19, 0.66);

  /* Lines */
  --line-on-dark:  rgba(248, 250, 249, 0.12);
  --line-on-paper: rgba(11, 26, 19, 0.12);
  --line-gold:     rgba(197, 168, 128, 0.34);

  /* Shape. Client rule: 4px maximum, no heavy drop shadows. */
  --r: 4px;

  /* Type */
  --font-display: "Montserrat", "Segoe UI", system-ui, sans-serif;
  --font-body: "Inter", "Segoe UI", system-ui, sans-serif;
  --font-accent: "Cormorant Garamond", Georgia, serif;

  /* Rhythm. Space is where the premium feel actually comes from. */
  --wrap: 1320px;
  --gutter: clamp(22px, 4.5vw, 72px);
  --sec-y: clamp(58px, 6.5vw, 104px);

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --t: 0.45s var(--ease);
}

/* ---------- 2. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* Any class that sets its own display (.btn, .row, .g) beats the user agent rule for
   the hidden attribute, which silently breaks every step and accordion toggle. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--green-900);
  color: var(--ink-on-dark);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
/* height:auto neutralises the width/height HTML attributes. Without it the attribute
   presentational hint pins the rendered height and every aspect-ratio rule below is
   silently ignored, which stretched images into tall portraits on phones. */
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* Anchor jump targets sit under the fixed nav */
[id] { scroll-margin-top: 104px; }

/* ---------- 3. Layout ---------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }

.sec { padding-block: var(--sec-y); position: relative; }
.sec--dark  { background: var(--green-900); color: var(--ink-on-dark); }
.sec--deep  { background: var(--green-800); color: var(--ink-on-dark); }
.sec--mid   { background: var(--green-700); color: var(--ink-on-dark); }
.sec--paper { background: var(--paper);     color: var(--ink-on-paper); }

.sec--paper .lede,
.sec--paper .body { color: var(--ink-on-paper-mute); }

/* ---------- 4. Type scale ---------- */
.h-hero {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.4rem, 5.4vw, 4.6rem);
  line-height: 1.04;
  letter-spacing: -0.03em;
}
.h-1 { font-size: clamp(1.95rem, 3.5vw, 3.05rem); letter-spacing: -0.028em; }
.h-2 { font-size: clamp(1.5rem, 2.4vw, 2.2rem); }
.h-3 { font-size: clamp(1.12rem, 1.4vw, 1.32rem); letter-spacing: -0.012em; }

/* The one permitted accent flourish: a single italic serif word.
   Client named Cormorant Garamond. Used once in the hero only. */
.accent {
  font-family: var(--font-accent);
  font-style: italic;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--gold);
  line-height: 1.1;
  padding-bottom: 0.06em;
  display: inline-block;
}

.lede { font-size: clamp(1rem, 1.1vw, 1.1rem); line-height: 1.66; color: var(--ink-on-dark-mute); max-width: 54ch; }
.body { font-size: 0.955rem; line-height: 1.68; color: var(--ink-on-dark-mute); max-width: 62ch; }

/* Eyebrow. A gold hairline plus a quiet label, not a floating gold word.
   Same device as the hero kicker and the quote kicker, so the three read as
   one system. The rule carries the accent, the text stays calm. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.3rem;
  font-family: var(--font-display);
  font-size: 0.69rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1;
  color: var(--ink-on-dark-mute);
}
.eyebrow::before {
  content: "";
  width: clamp(30px, 3.4vw, 50px);
  height: 1px;
  background: var(--gold);
  flex: none;
}
.sec--paper .eyebrow { color: var(--ink-on-paper-mute); }
.sec--paper .eyebrow::before { background: var(--gold-cta); }

/* ---------- 5. Buttons ----------
   Three layers, all of them compositor safe:
   1. ::before is the fill, which rises from the bottom edge rather than sliding
      in from the side. Rising reads as the surface filling up, which suits a
      groundworks brand better than a wipe.
   2. ::after is a narrow diagonal sheen that crosses the face once on hover.
      Only the primary button gets it, so it stays an event rather than a tic.
   3. The label and the arrow separate slightly, so the button feels like it
      opens rather than merely recolouring.
   Nothing here animates a layout property. */
.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.02rem 1.65rem;
  border-radius: var(--r);
  border: 1px solid transparent;
  font-family: var(--font-display);
  font-size: 0.79rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition:
    color 0.42s var(--ease),
    border-color 0.42s var(--ease),
    letter-spacing 0.42s var(--ease),
    transform 0.14s var(--ease);
}

/* 1. The rising fill. */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.52s var(--ease);
}
.btn:hover::before,
.btn:focus-visible::before { transform: scaleY(1); }

/* 2. The sheen. Parked off the left edge, crosses once, then waits. */
.btn::after {
  content: "";
  position: absolute;
  top: -60%;
  bottom: -60%;
  left: -30%;
  width: 26%;
  z-index: -1;
  transform: translateX(-260%) rotate(18deg);
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.34) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  opacity: 0;
  pointer-events: none;
}
.btn:hover::after,
.btn:focus-visible::after {
  opacity: 1;
  transition: transform 0.85s var(--ease) 0.1s;
  transform: translateX(560%) rotate(18deg);
}

/* 3. Label and arrow open apart. */
.btn i {
  font-size: 0.92em;
  transition: transform 0.42s var(--ease);
}
.btn:hover { letter-spacing: 0.125em; }
.btn:hover i { transform: translateX(5px); }
.btn:active { transform: translateY(1px) scale(0.985); }

/* Primary. Gold at rest, the lighter gold rises into it.
   Contrast: #0B1A13 on #D0A85C = 8.9:1, and on #DFBB78 = 11.2:1. Both pass AAA. */
.btn--gold {
  background: var(--gold-cta);
  color: var(--green-900);
  border-color: var(--gold-cta);
}
.btn--gold::before { background: var(--gold-cta-hover); }
.btn--gold:hover,
.btn--gold:focus-visible { border-color: var(--gold-cta-hover); }

/* Secondary on dark. Hairline at rest, gold rises in, the label flips dark. */
.btn--ghost {
  background: transparent;
  color: var(--ink-on-dark);
  border-color: rgba(248, 250, 249, 0.26);
}
.btn--ghost::before { background: var(--gold-cta); }
.btn--ghost::after { display: none; }
.btn--ghost:hover,
.btn--ghost:focus-visible { color: var(--green-900); border-color: var(--gold-cta); }

/* Secondary on paper. Same behaviour, deep green rises in. */
.btn--ink {
  background: transparent;
  color: var(--ink-on-paper);
  border-color: rgba(11, 26, 19, 0.22);
}
.btn--ink::before { background: var(--green-900); }
.btn--ink::after { display: none; }
.btn--ink:hover,
.btn--ink:focus-visible { color: var(--paper); border-color: var(--green-900); }

/* Anyone who has asked for less motion gets the colour change and nothing else. */
@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .btn i { transition: none; }
  .btn::after { display: none; }
  .btn:hover { letter-spacing: 0.1em; }
  .btn:hover::before { transform: scaleY(1); }
}

/* ---------- 6. Surfaces ---------- */
.card {
  background: var(--green-700);
  border: 1px solid var(--line-on-dark);
  border-radius: var(--r);
}
.card--paper {
  background: var(--white);
  border: 1px solid var(--line-on-paper);
  border-radius: var(--r);
}

.rule { height: 1px; background: var(--line-on-dark); border: 0; margin: 0; }
.sec--paper .rule { background: var(--line-on-paper); }

/* Icon treatment. Bare glyph, no plate, no box. The gold does the work.
   Class name kept so every section keeps its existing markup. */
.iplate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  height: auto;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--ink-on-dark);
  font-size: 1.5rem;
  line-height: 1;
  flex: none;
}
.sec--paper .iplate { color: var(--ink-on-paper); }

/* ---------- 7. Section headers ---------- */
/* Stacked, never the split headline-left explainer-right pattern. */
.sec-head { max-width: 62ch; margin-bottom: clamp(2rem, 3vw, 2.9rem); }
.sec-head .h-1 { margin-bottom: 1rem; }

/* ---------- 8. Grids ---------- */
.g { display: grid; gap: clamp(16px, 1.8vw, 26px); }
.g-2 { grid-template-columns: repeat(2, 1fr); }
.g-3 { grid-template-columns: repeat(3, 1fr); }
.g-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1024px) { .g-4 { grid-template-columns: repeat(2, 1fr); } .g-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px)  { .g-2, .g-3, .g-4 { grid-template-columns: 1fr; } }

/* ---------- 9. Forms ---------- */
.field { display: grid; gap: 0.5rem; }
.field > label {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-on-dark);
}
.field input, .field textarea, .field select {
  width: 100%;
  padding: 0.85rem 1rem;
  background: rgba(248, 250, 249, 0.05);
  border: 1px solid var(--line-on-dark);
  border-radius: var(--r);
  color: var(--ink-on-dark);
  font-size: 0.95rem;
  transition: border-color var(--t), background var(--t);
}
/* Placeholder contrast on the dark surface: 4.7:1 */
.field input::placeholder, .field textarea::placeholder { color: rgba(248, 250, 249, 0.48); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none;
  border-color: var(--gold);
  background: rgba(248, 250, 249, 0.08);
}
.field .help { font-size: 0.8rem; color: var(--ink-on-dark-mute); }
.field .err  { font-size: 0.8rem; color: #E9B7A6; display: none; }
.field.is-invalid .err { display: block; }
.field.is-invalid input, .field.is-invalid textarea { border-color: #E9B7A6; }

/* Visible keyboard focus everywhere */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: var(--r); }

/* ---------- 10. Motion ----------
   Only opacity and transform animate, so every reveal runs on the compositor.
   Short travel and a long ease reads as confident rather than bouncy.
   will-change is set only while the element is still waiting, then dropped
   in JS once it has played, so we never hold hundreds of layers alive. */
.reveal {
  opacity: 0;
  transform: translate3d(0, 34px, 0);
  transition: opacity 0.75s var(--ease), transform 0.75s var(--ease);
  will-change: opacity, transform;
}
.reveal.is-in { opacity: 1; transform: none; }
.reveal.is-done { will-change: auto; }
.reveal[data-d="1"] { transition-delay: 0.06s; }
.reveal[data-d="2"] { transition-delay: 0.12s; }
.reveal[data-d="3"] { transition-delay: 0.18s; }
.reveal[data-d="4"] { transition-delay: 0.24s; }
.reveal[data-d="5"] { transition-delay: 0.3s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

/* ---------- 11. Utilities ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.stack-sm { display: grid; gap: 0.55rem; }
.stack    { display: grid; gap: 1rem; }
.row      { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; }
.mt-lg    { margin-top: clamp(2rem, 3.4vw, 3rem); }

/* ---------- 12. Mobile slider utility ----------
   One shared component so the services list and the process steps behave
   identically on a phone. Native scroll snap does the work, so momentum,
   rubber banding and accessibility all come from the browser. No JS drives
   the movement, JS only reflects which slide is showing. */
.mslide {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Bleed to the screen edges so a slide can sit flush while the page keeps its gutter. */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
}
.mslide::-webkit-scrollbar { display: none; }
.mslide > * {
  scroll-snap-align: start;
  scroll-snap-stop: always;
  flex: 0 0 clamp(240px, 82%, 420px);
  min-width: 0;
}

/* Progress indicator: a counter plus a track of segments. */
.mnav {
  display: none;
  align-items: center;
  gap: 14px;
  margin-top: 18px;
}
.mnav-count {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--ink-on-dark-mute);
  flex: none;
  font-variant-numeric: tabular-nums;
}
.sec--paper .mnav-count { color: var(--ink-on-paper-mute); }

.mnav-track { display: flex; gap: 6px; flex: 1 1 auto; }
.mnav-track span {
  flex: 1 1 auto;
  height: 2px;
  background: rgba(248, 250, 249, 0.18);
  border-radius: 2px;
  transform: scaleY(1);
  transition: background 0.4s var(--ease), transform 0.4s var(--ease);
}
.sec--paper .mnav-track span { background: rgba(11, 26, 19, 0.16); }
.mnav-track span.is-on { background: var(--gold-cta); transform: scaleY(2); }

/* Sliders only exist on narrow screens. */
@media (max-width: 900px) {
  .mnav { display: flex; }
}
@media (min-width: 901px) {
  .mslide { display: block; margin-inline: 0; padding-inline: 0; overflow: visible; }
  .mslide > * { flex: none; }
}

/* ---------- 13. Stacked closing sections ----------
   From the qualifier down, each dark section pins to the top of the viewport and
   the next one rides up over it, so the page closes by dealing cards rather than
   by scrolling past four more blocks. The footer is the last panel and does not
   pin, since nothing follows it.

   This is position: sticky, not an animation. There is no scroll listener and no
   library, so it cannot stutter. Each panel needs an opaque background, which the
   sec--dark, sec--deep and sec--mid surfaces already provide.

   Only above 900px. On a phone the panels are far taller than the viewport, and a
   pinned panel you cannot see the end of is a trap rather than an effect. */
@media (min-width: 901px) {
  .sec--stack {
    position: sticky;
    /* A panel shorter than the window pins at the top, as designed. One that is
       taller than the window pins by its bottom edge instead, so its last field
       is always reachable. On a short laptop window the quote form was otherwise
       cut off with no way to scroll to the submit button. --stack-h is the panel's
       own height, set by the script; without JS this falls back to the old top: 0. */
    top: min(0px, calc(100vh - var(--stack-h, 100vh)));
    /* Later panels must paint over earlier ones. Set per section below. */
    z-index: 1;
    /* A hairline catches the light at the leading edge, so the eye reads the
       panel as sliding over the one behind rather than the page simply cutting. */
    border-top: 1px solid var(--line-on-dark);
  }

  /* An inner sticky column inside a pinned panel has nowhere left to travel and
     just fights its parent, so those revert to static while stacking is on. */
  .sec--stack .q-intro,
  .sec--stack .faq-aside,
  .sec--stack .faq-left { position: static; }
}

/* ---------- 14. Sticky anchor rail offset ----------
   Service pages pin a rail of section links under the header. The header itself
   slides away on scroll, so the rail has to follow it up rather than leaving a
   gap. JS keeps --rail-top in step with the header's state. */
:root { --rail-top: 76px; }
@media (max-width: 1120px) { :root { --rail-top: 66px; } }
