/* ==========================================================================
   site.css — per-site layout & components, built FROM tokens.

   HARD RULE (house-tech-spec §3): no raw hex, no px/rem size literals, no
   font-name literals in this file. Tokens only. The Critic greps for it.
   The two escape hatches are the house breakpoints and an explicit
   `@allow-literal: reason` comment on the preceding line.

   WHAT THE SKELETON SHIPS HERE, AND WHY IT SHIPS SO LITTLE
   --------------------------------------------------------
   Only the shell: header/nav, the section rhythm hook, buttons, the form, the
   footer. That is everything a site needs structurally and nothing that
   decides how it looks.

   The skeleton deliberately ships NO hero, NO card grid, NO feature row, NO
   testimonial block and NO section-heading pattern. Those come from the
   approved brief's layout archetype (design-rules §7) and are written per
   site. A skeleton that shipped them would hand every business the same page
   with different colours — which is the exact failure the DNA registry
   exists to prevent, and it would collide head-on with the banned-defaults
   list (design-rules §4: three-card grids, four-stat rows, centred hero with
   two pill buttons, every section the same padding with a centred heading).

   And when a brief's archetype numbers its sections, its sub-lists take a
   visibly different mark — different numeral system, face and colour role —
   or no mark at all (design-rules §4, "two counters in one costume").

   Builder: extend this file with the brief's components. Do not "fill in" a
   hero here from memory — the brief is law (spec §12).
   ========================================================================== */

/* --- Layout primitives ---------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--layout-container);
  margin-inline: auto;
  padding-inline: var(--layout-gutter);
}

/* Vertical rhythm hook. --section-gap is the only inter-section spacing value
   on a site (design-rules §7.1); the brief picks which scale step it is. */
.section {
  padding-block: var(--section-gap);
}

.measure {
  max-width: var(--layout-measure);
}

/* --- Header & navigation --------------------------------------------------
   Progressive enhancement: with JS off the nav list is a plain, always-visible
   list of links and the toggle stays [hidden] (it ships hidden in the HTML;
   main.js reveals it). With JS on, narrow viewports collapse the list behind
   the toggle. Nothing about navigation depends on JavaScript.              */

.site-header {
  position: relative;
  z-index: var(--z-header);
  /* MIN block-size (brief §4.2/§4.3's own 56px target) rather than a fixed
     one: a fixed block-size on a wrapping flex row overflows the header's own
     box the moment content wraps (measured — rendered content spilled into
     the expiry bar above it, ink-on-ink). min-block-size hits the same 56px
     figure whenever content fits one line, and grows honestly rather than
     clipping/overlapping when it does not. */
  min-block-size: var(--header-height);
  display: flex;
  align-items: center;
  border-bottom: var(--border-hairline) var(--border-style) var(--color-border);
  background-color: var(--color-bg);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.site-header__brand {
  font-family: var(--font-display);
  font-size: var(--text-h3);
  font-weight: var(--font-weight-display);
  text-decoration: none;
  letter-spacing: var(--tracking-tight);
}

/* The right-hand cluster — brief §4.3's second flex item (wordmark left,
   this cluster right). The phone link lives here, OUTSIDE the collapsible
   nav, so it never disappears behind the toggle (brief §5: the header phone
   is "repeated at every scroll depth"). Only the two anchor links inside
   .site-nav collapse below 48em. */
.site-header__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

/* Brief §4.3: "no visible number required at this width... satisfied by
   presence not by printed digits." Measured (not assumed): the full label
   alongside the wordmark and the toggle does not fit one line at 375px (335px
   content box) — icon-only below 30em is what makes the one-line claim true
   rather than a wrapped, overlapping header. The accessible name (the
   Copywriter's HEADER_CTA_LABEL text) is unchanged at every width; only its
   visual presentation toggles. */
.site-header__phone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  /* Icon-only below 30em is 18px wide on its own — [data-tap]'s min-height
     covers the tap floor's other axis, this covers the width one (spec §8,
     44x44 minimum), measured by render-audit's a11y/touch check. */
  min-inline-size: var(--layout-tap-min);
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
  text-decoration: none;
}

.site-header__phone:hover,
.site-header__phone:focus-visible {
  text-decoration: underline;
  text-underline-offset: var(--space-3xs);
}

.site-header__phone-icon {
  flex: 0 0 auto;
}

.site-header__phone-text {
  position: absolute;
  /* @allow-literal: the standard visually-hidden clip box, not a design value */
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (min-width: 30em) {
  .site-header__phone-text {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
    white-space: normal;
  }
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-xs);
  /* The nav-toggle's only shape is this hairline, so it is a CONTROL boundary
     (WCAG 1.4.11, >= 3:1), not a decorative one — use --color-border-strong. */
  border: var(--border-hairline) var(--border-style) var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
}

.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
}

.site-nav__link:hover,
.site-nav__link:focus-visible {
  text-decoration: underline;
  text-underline-offset: var(--space-3xs);
}

/* QA r3 FIX perf/cls (this skeleton-level defect was confirmed present,
   verbatim, on every site built from it -- see
   runs/3-elements-landscaping-asheville-nc/qa/critic-round-2.md): the
   collapsed state used to apply only once a deferred `type="module"`
   script called `initNav()` and set `[data-nav-ready]`, so the full nav
   list rendered open at first paint and then collapsed into the hamburger
   AFTER first contentful paint -- a timing race with no fixed outcome that
   shifted the whole header (and everything below it) and scored CLS
   0.066-0.094 in the majority of Lighthouse runs. Inverting the default
   removes the race entirely: closed is now the plain CSS default below
   48em, with no JS gate, so there is nothing left to collapse post-paint.
   The one visitor this would otherwise strand -- JS off, so
   `[data-nav-ready]` never lands -- gets the nav back via the <noscript>
   stylesheet override in `@shared:head-boilerplate` (index.html/buy.html/
   thanks.html), which ships last in source order and wins the
   display:block/display:none tie on cascade order alone, no
   `!important`. The nav-toggle button already ships `hidden` in the HTML
   and only JS ever clears that, so JS-off visitors never see a dead
   control -- nothing to change there. */
.site-nav {
  display: none;
}

[data-nav-ready] .site-nav[data-nav-open='true'] {
  display: block;
  flex-basis: 100%;
}

@media (min-width: 48em) {
  .site-nav {
    display: block;
  }

  [data-nav-ready] .nav-toggle {
    display: none;
  }
}

/* --- Buttons --------------------------------------------------------------
   Two roles, no more. Radius, weight and colour all come from the brief via
   tokens — including --radius-none, which is a legitimate answer.          */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--space-xs) var(--space-md);
  border: var(--border-hairline) var(--border-style) transparent;
  border-radius: var(--radius-sm);
  font-weight: var(--font-weight-body-strong);
  line-height: var(--leading-snug);
  text-align: center;
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.btn--primary {
  background-color: var(--color-accent);
  color: var(--color-accent-ink);
}

.btn--quiet {
  /* Outline button: the border is the whole control, so it takes the 3:1
     control-boundary token, not the decorative hairline (WCAG 1.4.11). */
  border-color: var(--color-border-strong);
  color: var(--color-ink);
}

/* --- Form -----------------------------------------------------------------
   One form per site (spec §6). POSTs to the endpoint constant in main.js;
   works as a plain HTML POST with JS off; auto-hides when no endpoint is
   configured so a spec site never shows a dead form.                       */

.form {
  display: grid;
  gap: var(--space-md);
  max-width: var(--layout-measure);
}

.field {
  display: grid;
  gap: var(--space-3xs);
}

.field__label {
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
}

.field__control {
  width: 100%;
  min-height: var(--layout-tap-min);
  padding: var(--space-2xs) var(--space-xs);
  background-color: var(--color-surface);
  color: var(--color-ink);
  border: var(--border-hairline) var(--border-style) var(--color-border);
  border-radius: var(--radius-sm);
}

textarea.field__control {
  min-height: var(--space-3xl);
  resize: vertical;
}

.field__hint {
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}

/* Honeypot: present in the DOM for bots, unreachable for humans and assistive
   tech. Not display:none — some bots skip those. */
.form__trap {
  position: absolute;
  /* @allow-literal: off-canvas parking position, not a design value */
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}

.form__status {
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}

.form__status[data-state='error'] {
  color: var(--color-ink);
  font-weight: var(--font-weight-body-strong);
}

/* --- Footer ---------------------------------------------------------------- */

.site-footer {
  padding-block: var(--space-xl);
  border-top: var(--border-hairline) var(--border-style) var(--color-border);
  font-size: var(--text-small);
}

.site-footer a {
  text-underline-offset: var(--space-3xs);
}

.site-footer__nap {
  font-style: normal;
}

/* ==========================================================================
   BRIEF COMPONENTS — proof-column archetype (brief §4). Everything below is
   this site's own layout, built from tokens per house-tech-spec §3.
   ========================================================================== */

/* --- Hero — brief §4.2. DOM order: eyebrow -> h1 -> primary CTA -> living-
   hero field -> lead. Single column <64em; two-column split (text left,
   full-height image column right) >=64em, per the brief's own desktop
   first-screen arithmetic. --- */

.hero {
  padding-block-start: var(--section-gap);
  padding-block-end: var(--section-gap);
  background-color: var(--color-bg);
}

.hero__grid {
  display: grid;
  grid-template-columns: 1fr;
}

.hero__eyebrow {
  margin: 0 0 var(--hero-gap-eyebrow-h1);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink-muted);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.hero__h1 {
  margin: 0 0 var(--hero-gap-h1-cta);
  font-size: var(--text-h1);
}

.hero__h1-line {
  display: block;
}

.hero__emphasis {
  font-family: var(--font-emphasis);
  font-style: italic;
  font-weight: var(--font-weight-emphasis);
}

.hero__cta {
  margin: 0 0 var(--hero-gap-cta-band);
}

/* The living-hero field. Full-bleed edge-to-edge below 64em (the registry
   signature's "full-width band of living turf"); at 64em+ it becomes a
   contained, full-height column beside the text (brief §4.2's desktop
   arithmetic: "the image column is full-height beside it"). */
.hero__band {
  position: relative;
  overflow: hidden;
  block-size: var(--hero-band-height);
  /* @allow-literal: 100vw is the full-bleed viewport width, not a design value */
  inline-size: 100vw;
  /* @allow-literal: the standard full-bleed centering formula, not a design value */
  margin-inline-start: calc(50% - 50vw);
  margin-block-end: var(--space-sm);
  background-color: var(--color-surface);
}

.hero__band img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.hero__blades {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  pointer-events: none;
}

/* The signature move — three turf blades on independent, non-synchronised
   loops (brief §7). transform-box/transform-origin anchor the sway at each
   blade's own base so it reads as rooted rather than floating. */
.hero__blade {
  fill: none;
  stroke: var(--color-accent-ink);
  stroke-width: 5;
  stroke-linecap: round;
  opacity: 0.85;
  transform-box: fill-box;
  transform-origin: bottom center;
  animation-name: hero-blade-sway;
  animation-timing-function: var(--ease-in-out);
  animation-iteration-count: infinite;
}

.hero__blade--a { animation-duration: var(--duration-blade-a); }
.hero__blade--b { animation-duration: var(--duration-blade-b); animation-delay: var(--delay-blade-b); }
.hero__blade--c { animation-duration: var(--duration-blade-c); animation-delay: var(--delay-blade-c); }

@keyframes hero-blade-sway {
  0%, 100% { transform: rotate(-5deg); }
  50%      { transform: rotate(5deg); }
}

/* prefers-reduced-motion: reduce (house-tech-spec §8) — base.css's kill
   switch collapses the house --duration-* tokens; the blade loops use their
   own named durations, so this site freezes them explicitly to their
   upright rest pose. The still frame keeps the band's colour, texture and
   the reused head-rule — full atmosphere, loops stopped. */
@media (prefers-reduced-motion: reduce) {
  .hero__blade {
    animation: none;
    transform: none;
  }
}

.hero__lead {
  margin: 0;
  max-width: var(--layout-measure);
  font-size: var(--text-lead);
  color: var(--color-ink-muted);
}

@media (min-width: 64em) {
  .hero__grid {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "eyebrow band"
      "h1      band"
      "cta     band"
      "lead    band";
    align-items: start;
    column-gap: var(--space-xl);
  }

  .hero__eyebrow { grid-area: eyebrow; }
  .hero__h1      { grid-area: h1; }
  .hero__cta     { grid-area: cta; }
  .hero__lead    { grid-area: lead; }

  .hero__band {
    grid-area: band;
    inline-size: 100%;
    block-size: auto;
    min-block-size: 100%;
    margin-inline-start: 0;
    margin-block-end: 0;
    align-self: stretch;
  }
}

/* --- Section rhythm modifiers — brief §2.2's reused head-rule and §2.1's
   surface-ground sections. --- */

.section--ruled {
  border-block-start: var(--border-thick) var(--border-style) var(--color-accent);
}

.section--surface {
  background-color: var(--color-surface);
}

.section__intro {
  margin-block-start: var(--space-sm);
  color: var(--color-ink-muted);
}

/* --- Services — brief §4.1 row 2. Three named cards, one per branded
   pillar; cards 2 and 3 carry a small accent image (imagery plan slots 2/3). --- */

.service-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  margin-block-start: var(--space-lg);
}

@media (min-width: 48em) {
  .service-grid {
    grid-template-columns: repeat(3, 1fr);
    align-items: start;
  }
}

.service-card {
  background-color: var(--color-surface);
  padding: var(--space-md);
}

.service-card__media {
  margin-block-end: var(--space-sm);
}

.service-card__media img {
  inline-size: 100%;
  block-size: auto;
  /* Belt-and-suspenders on top of the width/height attributes (spec §7 CLS
     ≈ 0): measured, from the shipped file itself (700x523), not guessed. */
  aspect-ratio: 700 / 523;
}

.service-card__name {
  margin: 0 0 var(--space-2xs);
  font-size: var(--text-h3);
}

.service-card__body {
  margin: 0;
}

/* --- Season Strip — brief §4.1 row 3. Exactly two honest windows. --- */

.season-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin-block-start: var(--space-lg);
}

@media (min-width: 48em) {
  .season-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.season-window {
  background-color: var(--color-bg);
  padding: var(--space-md);
}

.season-window__label {
  margin: 0 0 var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h3);
}

.season-window__body {
  margin: 0;
}

/* --- Proof-column — brief §4.1 row 4. The single numbered entry; no
   sibling ever joins it (design-rules §5's two-counters rule does not
   trigger anywhere on this site). --- */

.proof-column__entry {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-lg);
  margin-block-start: var(--space-lg);
}

.proof-column__num {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-proof-numeral);
  line-height: 1;
  color: var(--color-accent);
}

.proof-column__quote {
  margin: 0;
  max-width: var(--layout-measure);
}

.proof-column__quote p {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-lead);
}

.proof-column__attribution {
  display: block;
  font-style: normal;
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}
