/* ==========================================================================
   site.css — Crystal Peak Landscaping, Spokane WA. warm-craft / granite-moss
   / full-bleed-immersive, "the water finds level" (brief §5, §6, §8).

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

   NO CARDS, NO BOXES, NO SHADOWS ANYWHERE (brief §5.1 rule 5). Structure is
   carried by --color-border hairlines, the recurring atmosphere gradient,
   and space. The header carries no nav-toggle and no nav list: brief
   §"Sticky chrome" gives the header exactly two things, the wordmark and the
   tel: link — nothing collapses, so there is nothing for a toggle to do
   (precedent: yard-master-services-logan-ut, the sibling full-bleed-immersive
   build). main.js's initNav() no-ops safely when it finds neither.
   ========================================================================== */

/* --- 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 the site (brief §5.4); the hero's own padding-block-end is its second
   and only other call site. */
.section {
  padding-block: var(--section-gap);
}

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

/* The recurring ornament (brief §6.6): the hero atmosphere gradient, reused
   as every section's background — "the one continuous visual idea the whole
   page shares." Body carries it once; every section and the hero paint
   through it rather than repeating the gradient declaration. */
body {
  /* A solid background-color fallback, not just the gradient image: some
     tooling (and any browser painting before the gradient decodes) reads
     backgroundColor directly, and --color-surface is the gradient's own
     darkest stop (= --color-atmo-3) — the brief's own §4.3 worst case,
     already confirmed AA on every text role at this exact colour. */
  background-color: var(--color-surface);
  background-image: radial-gradient(120% 90% at 30% 12%, var(--color-atmo-1) 0%, var(--color-atmo-3) 100%);
  background-attachment: fixed;
}

/* --- Header & navigation ---------------------------------------------------
   Sticky, per brief §"Sticky chrome" and css/preview.css's documented
   sticky-stack recipe. Wordmark left (Spectral Italic, accent colour, sized
   by --text-wordmark), tel: right (Karla 600, eyebrow-sized, accent
   colour). The phone is present at every scroll depth on every page. */

.site-header {
  position: sticky;
  inset-block-start: var(--expiry-height);
  z-index: var(--z-header);
  min-height: var(--header-height);
  display: flex;
  align-items: center;
  padding-block: 0;
  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);
  width: 100%;
}

.site-header__brand {
  font-family: var(--font-display-italic);
  font-style: italic;
  font-size: var(--text-wordmark);
  font-weight: var(--font-weight-display);
  color: var(--color-accent);
  text-decoration: none;
}

.site-header__nav {
  display: flex;
  align-items: center;
}

.site-header__tel {
  display: inline-flex;
  align-items: center;
  min-height: var(--layout-tap-min);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  color: var(--color-accent);
  text-decoration: none;
}

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

/* Anchor-link scroll offset under the sticky stack (css/preview.css's
   documented recipe: expiry bar height + this brief's own header height). */
section[id] {
  scroll-margin-block-start: calc(var(--expiry-height) + var(--header-height) + var(--border-hairline));
}

/* --- Buttons ----------------------------------------------------------------
   One role on this site: the primary CTA, accent fill, --radius-sm (the
   only rounded corner anywhere), Karla 600. Height is a brief-specific pair
   (52/56px) via --hero-cta-height, since the primary CTA is always the
   hero's or a restatement of it (brief §I, §IV, §VI all reuse the same
   string and role). No second button role exists on this site (brief §4's
   ban on a centred-hero-plus-two-pills pattern; §I "no second button"). */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding-inline: var(--space-md);
  min-height: var(--hero-cta-height);
  border: var(--border-hairline) var(--border-style) transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  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--primary:hover,
.btn--primary:focus-visible {
  background-color: var(--color-ink);
}

/* --- Hero — full-bleed-immersive (brief §5.1, §5.2, §5.5) ------------------
   The field IS the background layer; the ask sits inside it, not beside it.
   DOM order inside .hero, matching brief §5.5 exactly (v2 — .hero__corner-
   rule dropped, brief §5.1 point 1 / §6.6 — it carried no stated job):
     .hero__content (eyebrow, h1, CTA) -> the field's remaining lower
     register (rivulet+shimmer, tuft) -> lead -> facts line. Lead and facts
     sit OUTSIDE .hero__field, after it, in normal flow (brief §5.1 rule 2). */

.hero {
  position: relative;
  padding-block-end: var(--section-gap);
}

.hero__field {
  position: relative;
  block-size: var(--hero-field-height);
  overflow: hidden;
}

.hero__field picture,
.hero__field img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* Keeps the recipe's own atmosphere band (the upper two-thirds of the
     source frame) under the text at every width — measured, not guessed
     (brief §4.3's mandatory re-measurement against the real asset). Below
     1024 the field is height-constrained by "cover" and object-position-y
     has no visible effect (the source's full height already fits); at
     1024+ the field turns width-constrained and a low Y keeps the crop
     anchored to the source's top (atmosphere), never sliding the stones'
     shadow band up into the text block. */
  object-position: 50% 0%;
}

@media (min-width: 64em) {
  .hero__field picture,
  .hero__field img {
    /* At >=1024 the field turns width-constrained under "cover" and its
       overflow shrinks toward the 1024 end of the clamp (~30px, against
       ~200px at 1440) — too little crop room for object-position alone to
       clear the stones' shadow out of the text block at every width in
       range. A modest zoom anchored to the frame's own top edge (same
       anchor as object-position's 0%) buys back that room without moving
       any type: measured against the actual rendered asset (brief §4.3's
       mandatory re-measurement) at 1024 and 1440, this is the smallest
       scale that clears AA at both. */
    transform-origin: 50% 0%;
    transform: scale(1.2);
  }
}

.hero__content {
  position: relative;
  z-index: var(--z-content);
  padding-block-start: var(--hero-content-pad-start);
  padding-inline: var(--layout-gutter);
  max-inline-size: 100%;
}

@media (min-width: 64em) {
  .hero__content {
    max-inline-size: calc(var(--hero-content-max) + 2 * var(--layout-gutter));
  }
}

.hero__eyebrow {
  position: relative;
  inline-size: fit-content;
  margin: 0;
  max-inline-size: var(--hero-content-max);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  letter-spacing: var(--tracking-wide);
  line-height: var(--leading-eyebrow);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

/* The atmosphere veil (brief v3 §4.3.1) — an extension of the SAME atmosphere-
   gradient ornament already in the closed table (§6.6 row 1), not a new
   device. A full-width, top-anchored fade of --color-atmo-1 (= --color-bg,
   the gradient's own lightest, already-AA-verified stop) sits between the
   hero image and .hero__content, restoring this brief's own originally-
   computed §4.3 legibility floor under the real illustrated asset's fine
   grain. Zero-height (absolutely positioned against .hero__field, which is
   already position:relative/overflow:hidden) — does not move §5.5's fold
   arithmetic. Soft-edged on every side: reads as more of the recipe's own
   "generous, quiet atmosphere," never a boxed ground. */
.hero__field::before {
  content: '';
  position: absolute;
  z-index: var(--z-atmosphere);
  inset-block-start: 0;
  inset-inline: 0;
  block-size: var(--hero-atmo-veil-height);
  background-image: linear-gradient(
    to bottom,
    var(--color-atmo-veil-strong) 0%,
    var(--color-atmo-veil-soft) 55%,
    transparent 100%
  );
  pointer-events: none;
}

.hero__title {
  margin: var(--hero-eyebrow-h1-gap) 0 0;
  max-inline-size: var(--hero-content-max);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  color: var(--color-ink);
  text-wrap: balance;
}

/* The one italic-accent word per h1 (brief §2.3) — marked up as <em>, which is
   exactly what it semantically is, and styled here rather than with a class.
   Covers both pages: the hero h1 and thanks.html's h2-scale h1. */
h1 em {
  font-family: var(--font-display-italic);
  font-style: italic;
  font-weight: var(--font-weight-display);
  color: var(--color-accent);
}

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

/* --- The ambient satellite layer (brief §8.2) — "the water finds level".
   Two independent, non-synchronised, continuous loops: a highlight that
   travels once around the rivulet on a 7s loop, and an ornamental tuft that
   sways independently on an 11s loop. transform/opacity only, GPU-
   composited, aria-hidden, pointer-events: none. Positioned in the field's
   lower register per the style recipe's own framing (stones + rivulet sit
   in the lower third of the frame). */

.hero__satellites {
  position: absolute;
  z-index: var(--z-atmosphere);
  inset-block-end: var(--space-lg);
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 40%;
  pointer-events: none;
}

.hero__shimmer {
  position: absolute;
  inset-block-end: 18%;
  inset-inline-start: 24%;
  inline-size: var(--satellite-shimmer-width);
  block-size: var(--space-3xs);
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, transparent 0%, var(--color-bg) 45%, var(--color-bg) 55%, transparent 100%);
  opacity: 0;
  animation: shimmer-travel var(--loop-shimmer) linear infinite;
}

@keyframes shimmer-travel {
  0%   { transform: translateX(-120%); opacity: 0; }
  8%   { opacity: 0.9; }
  50%  { opacity: 0.9; }
  92%  { opacity: 0; }
  100% { transform: translateX(220%); opacity: 0; }
}

.hero__tuft {
  position: absolute;
  inset-block-end: 6%;
  inset-inline-start: 46%;
  inline-size: var(--satellite-tuft-width);
  block-size: var(--satellite-tuft-height);
  transform-origin: 50% 100%;
  animation: tuft-sway var(--loop-tuft-sway) ease-in-out infinite;
}

.hero__tuft svg {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
}

.hero__tuft path {
  stroke: var(--color-moss);
  stroke-width: 2;
  stroke-linecap: round;
  fill: none;
}

@keyframes tuft-sway {
  0%   { transform: rotate(0deg); }
  25%  { transform: rotate(2.2deg); }
  50%  { transform: rotate(-1.4deg); }
  75%  { transform: rotate(1.6deg); }
  100% { transform: rotate(0deg); }
}

/* Reduced motion (brief §8.3): every loop holds a spectacular STILL frame,
   never a zero-width or mid-sway state. base.css's kill switch already
   collapses every duration token to ~0.01ms; the two rules below fix the
   REST STATE each animation would otherwise be caught mid-cycle in. */
@media (prefers-reduced-motion: reduce) {
  .hero__shimmer {
    animation: none;
    opacity: 0.9;
    transform: translateX(0);
  }

  .hero__tuft {
    animation: none;
    transform: rotate(0deg);
  }
}

.hero__lead {
  margin: var(--space-lg) auto 0;
  max-inline-size: var(--hero-content-max);
  padding-inline: var(--layout-gutter);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-lead);
  color: var(--color-ink-muted);
}

.hero__facts {
  margin: var(--space-md) auto 0;
  max-inline-size: var(--hero-content-max);
  padding: var(--space-md) var(--layout-gutter) 0;
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-facts);
  color: var(--color-ink);
}

/* --- thanks.html (brief §5.5) -----------------------------------------------
   No living-hero field on this page (§0 permits exactly one spectacular
   moment per site, and it is index.html's). The ask has already been made,
   so the primary control is the tel: button. */

.thanks {
  padding-block-start: var(--hero-content-pad-start);
  padding-block-end: var(--section-gap);
}

.thanks__eyebrow {
  margin: 0;
  max-inline-size: var(--hero-content-max);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  letter-spacing: var(--tracking-wide);
  line-height: var(--leading-eyebrow);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

.thanks__title {
  margin: var(--hero-eyebrow-h1-gap) 0 0;
  max-inline-size: var(--hero-content-max);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
  color: var(--color-ink);
  text-wrap: balance;
}

.thanks__lead {
  margin: var(--space-md) 0 0;
  max-inline-size: var(--hero-content-max);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-lead);
  color: var(--color-ink-muted);
}

.thanks__cta {
  margin: var(--hero-h1-cta-gap) 0 0;
}

/* --- Section heads (brief §5.1 rule 4) --------------------------------------
   h3, Spectral Italic, never a numbered kicker. The recurring ornament is
   the atmosphere gradient itself; no datum rule is reused here. */

.section__head {
  margin: 0 0 var(--space-md);
  font-family: var(--font-display-italic);
  font-style: italic;
  font-weight: var(--font-weight-display);
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
  color: var(--color-ink);
}

/* --- The ruled index (brief §II/§III) — --color-border-separated lines, no
   cards, no chips, no thumbnails (explicitly refused, brief §1 ref-008). */

.index {
  margin: 0;
  padding: 0;
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
}

.line {
  padding-block: var(--space-xs);
  border-block-end: var(--border-hairline) var(--border-style) var(--color-border);
}

.line__label {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--color-ink);
}

.line b {
  font-weight: var(--font-weight-body-bold);
}

.line__desc,
.line__tag {
  margin: var(--space-3xs) 0 0;
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  line-height: var(--leading-eyebrow);
  color: var(--color-ink-muted);
}

.section__lead {
  margin: 0 0 var(--space-md);
  max-inline-size: var(--layout-measure);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-lead);
  color: var(--color-ink-muted);
}

.section__foot {
  margin-block-start: var(--space-lg);
}

/* --- Plate figures (brief §7.2 slots 2-4) — full content width, lazy. No
   frame, no shadow: the image itself is the field-guide plate. */

.plate-figure {
  margin: var(--space-lg) 0 0;
}

.plate-figure img {
  inline-size: 100%;
  block-size: auto;
}

.plate-figure figcaption {
  margin-block-start: var(--space-2xs);
  font-family: var(--font-body);
  font-size: var(--text-micro);
  color: var(--color-ink-muted);
}

/* --- Trust section (brief §IV) --------------------------------------------- */

.testimonial {
  margin: 0;
  padding: var(--space-md) 0;
  border-block: var(--border-hairline) var(--border-style) var(--color-border);
}

.testimonial p {
  margin: 0;
  max-inline-size: var(--layout-measure);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-lead);
  color: var(--color-ink);
}

.testimonial cite {
  display: block;
  margin-block-start: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  font-style: normal;
  color: var(--color-ink-muted);
}

.trust__line {
  margin: var(--space-md) 0 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  color: var(--color-ink);
}

.trust__cta {
  margin: var(--space-md) 0 0;
}

/* --- Service area (brief §V) — one --color-border-separated line. --------- */

.towns {
  margin: 0;
  padding: var(--space-sm) 0;
  border-block: var(--border-hairline) var(--border-style) var(--color-border);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink);
}

.area__sentence {
  margin: var(--space-md) 0 0;
  max-inline-size: var(--layout-measure);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
  color: var(--color-ink-muted);
}

/* --- Contact (brief §VI) ---------------------------------------------------- */

.contact {
  display: grid;
  gap: var(--space-xl);
}

@media (min-width: 64em) {
  .contact {
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--contact-aside-width));
    align-items: start;
  }
}

.contact__intro {
  margin: 0 0 var(--space-md);
  max-inline-size: var(--layout-measure);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
  color: var(--color-ink-muted);
}

/* --- Form -------------------------------------------------------------------
   POSTs to the endpoint constant in main.js; plain HTML POST with JS off;
   auto-hides when no endpoint is configured. --radius-none per brief §VI. */

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

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

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

.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);
  font-family: var(--font-body);
  font-size: var(--text-body);
  border: var(--border-hairline) var(--border-style) var(--color-border);
  border-radius: var(--radius-none);
}

.field__control:focus-visible {
  outline-offset: var(--space-3xs);
}

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

.field__hint {
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  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-family: var(--font-body);
  font-size: var(--text-eyebrow);
  color: var(--color-ink-muted);
}

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

.contact__aside {
  padding-block-start: var(--space-md);
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
}

@media (min-width: 64em) {
  .contact__aside {
    padding-block-start: 0;
    padding-inline-start: var(--space-xl);
    border-block-start: none;
    border-inline-start: var(--border-hairline) var(--border-style) var(--color-border);
  }
}

.contact__nap {
  margin: 0;
  font-style: normal;
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-facts);
  color: var(--color-ink);
}

.contact__nap a {
  color: var(--color-accent);
}

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

.site-footer {
  padding-block: var(--space-xl);
  border-top: var(--border-hairline) var(--border-style) var(--color-border);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  line-height: var(--leading-facts);
  color: var(--color-ink-muted);
}

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

.site-footer__nap {
  font-style: normal;
  color: var(--color-ink);
}

.site-footer__nap strong {
  font-family: var(--font-display-italic);
  font-style: italic;
  font-size: var(--text-lead);
  font-weight: var(--font-weight-display);
  color: var(--color-accent);
}

.site-footer p {
  margin: var(--space-2xs) 0 0;
}
