/* ==========================================================================
   site.css — Eastview Landscaping, Huntsville AL. warm-craft / kiln-brick-
   cream / sidebar-anchor, "the line runs true" (brief §2-§7).
   HARD RULE (house-tech-spec §3): tokens only, no raw hex/size/font
   literals. Escape hatches: house breakpoints, `@allow-literal:` comments.
   The head-rule (brief §3, §7) is the one recurring device: a 2px accent
   bar at every section boundary; the settled hero-string signature IS that
   stroke — literalising the trade's own convention of a taut line.
   ========================================================================== */

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

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

/* --section-gap: the only inter-section spacing value (brief §4); hero's
   padding-block-end is its other call site. */
.section {
  padding-block: var(--section-gap);
}

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

body {
  background-color: var(--color-bg);
}

/* Eyebrow (hero, thanks.html). Natural case in HTML, uppercased here
   (house-tech-spec §8: never typed in caps). */
.eyebrow {
  margin: 0;
  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-snug);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

/* Head-rule (brief §3, §7): static 2px accent bar at every section
   boundary. Stated minimum 3rem cannot bind within the house responsive
   range, so nothing further is owed. */
.headrule {
  margin: 0 0 var(--space-md);
  border: 0;
  border-block-start: var(--border-trim) var(--border-style) var(--color-accent);
  min-inline-size: var(--headrule-min);
}

/* --- Header & nav. Sticky, below the preview bar (brief §4.1; recipe:
   css/preview.css). Fixed 56px + 1px border. Phone sits OUTSIDE the
   collapsible nav so it stays visible at 375px without opening the menu. */

.site-header {
  position: sticky;
  inset-block-start: var(--expiry-height, 0px);
  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-2xs);
  width: 100%;
}

.site-header__brand {
  font-family: var(--font-display);
  font-size: var(--text-wordmark);
  font-weight: var(--font-weight-display);
  text-decoration: none;
  letter-spacing: var(--tracking-tight);
  color: var(--color-ink);
  order: 1;
}

.site-header__phone {
  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;
  order: 2;
  margin-inline-start: auto;
}

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

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-3xs) var(--space-2xs);
  border: var(--border-hairline) var(--border-style) var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  order: 3;
}

.site-nav {
  order: 4;
}

.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;
  min-height: var(--layout-tap-min);
  font-family: var(--font-body);
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink);
  text-decoration: none;
}

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

/* Collapsed state exists only once JS has armed it (JS-off: plain list). */
[data-nav-ready] .site-nav[data-nav-open='false'] {
  display: none;
}

[data-nav-ready] .site-nav {
  flex-basis: 100%;
}

@media (min-width: 48em) {
  [data-nav-ready] .site-nav[data-nav-open='false'] {
    display: block;
  }

  [data-nav-ready] .site-nav {
    flex-basis: auto;
  }

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

/* Anchor scroll offset under the sticky stack — see --sticky-offset,
   tokens.css, which falls back gracefully once preview.css is gone. */
section[id] {
  scroll-margin-block-start: var(--sticky-offset);
}

/* --- Buttons ---------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding-inline: var(--space-md);
  min-height: var(--layout-tap-min);
  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 {
  min-height: var(--hero-cta-height);
  background-color: var(--color-accent);
  color: var(--color-accent-ink);
}

.btn--primary:hover,
.btn--primary:focus-visible {
  background-color: var(--color-ink);
}

.btn--quiet {
  border-color: var(--color-border);
  color: var(--color-ink);
}

.btn--quiet:hover,
.btn--quiet:focus-visible {
  border-color: var(--color-ink);
}

/* --- Hero (brief §4-§7). DOM order: eyebrow -> h1 -> CTA -> field ->
   facts. eyebrow/h1/CTA sit on the section's own flat --color-bg, never on
   the field's gradient (brief §2's hard, zero-exception constraint). */

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

.hero__eyebrow {
  margin: 0;
}

.hero__h1 {
  margin: var(--hero-eyebrow-gap) 0 0;
  max-inline-size: var(--hero-h1-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);
}

/* Accent closing line (brief §4/§8): colour is the only emphasis device,
   never a synthesized italic (brief §3). On flat --color-bg (5.09:1, brief
   §2 row 5), never the gradient. */
.hero__h1 strong {
  font-weight: var(--font-weight-display);
  color: var(--color-accent);
}

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

/* Bounded 340px living-hero plate (brief §2, §5, §6 slot 1, §7). Gradient
   is the pre-decode/fallback ground; the photo covers it once painted —
   everything else sits on that opaque photo, never raw gradient pixels. */
.hero__field {
  position: relative;
  margin: var(--hero-cta-field-gap) 0 0;
  block-size: var(--hero-field-height);
  overflow: hidden;
  background: radial-gradient(120% 90% at 30% 20%, var(--color-atmo-1) 0%, var(--color-atmo-3) 100%);
  border-radius: var(--radius-sm);
}

.hero__field picture,
.hero__field img {
  position: absolute;
  z-index: var(--z-field);
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: 50% 45%;
}

/* Honesty caption, ink-on-surface, brief §2 row 2 (12.74:1) — bottom-
   left, never on raw gradient. Same string as <img> alt; <img> ships
   alt="" so it announces once (figure/figcaption, README.md). */
.hero__caption {
  position: absolute;
  z-index: var(--z-content);
  inset-block-end: var(--space-sm);
  inset-inline-start: var(--space-sm);
  inset-inline-end: var(--space-sm);
  max-inline-size: var(--hero-caption-max);
  margin: 0;
  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-micro);
  line-height: var(--leading-snug);
  border-radius: var(--radius-sm);
}

/* Signature (brief §7): SVG string sags then snaps taut once via `d`
   (same command shape both keyframes, so the browser interpolates the
   control point). Overshoot is --ease-signature's own bezier (>1 values).
   Settles as a straight 2px line — same stroke as .headrule. */
.hero__string {
  position: absolute;
  z-index: var(--z-content);
  inset-block-end: 28%;
  inset-inline-start: 6%;
  inline-size: 88%;
  block-size: 30%;
  overflow: visible;
  pointer-events: none;
}

.hero__string path {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: var(--border-trim);
  stroke-linecap: round;
  d: path('M 8 30 Q 164 66 320 46');
  animation: string-settle var(--duration-signature) var(--ease-signature) both;
}

@keyframes string-settle {
  0%   { d: path('M 8 30 Q 164 66 320 46'); }
  100% { d: path('M 8 38 Q 164 38 320 38'); }
}

/* Two independent dust motes + one wood shaving (brief §7), aria-hidden,
   decorative, transform/opacity only, in the field's lower register near
   the string (brief §6's framing clause). */
.hero__satellites {
  position: absolute;
  z-index: var(--z-content);
  inset: 0;
  pointer-events: none;
}

.hero__mote {
  position: absolute;
  inline-size: var(--mote-size);
  block-size: var(--mote-size);
  border-radius: var(--radius-pill);
  background-color: var(--color-atmo-1);
  opacity: 0;
}

.hero__mote--a {
  inset-block-end: 40%;
  inset-inline-start: 22%;
  animation: mote-drift-a var(--duration-drift-a) var(--ease-drift) infinite;
}

.hero__mote--b {
  inset-block-end: 58%;
  inset-inline-start: 68%;
  animation: mote-drift-b var(--duration-drift-b) var(--ease-drift) infinite;
}

@keyframes mote-drift-a {
  0%   { transform: translate(0, 0); opacity: 0; }
  15%  { opacity: 0.65; }
  50%  { transform: translate(calc(var(--mote-drift-x) * 0.5), calc(var(--mote-drift-y) * -0.6)); opacity: 0.85; }
  85%  { opacity: 0.2; }
  100% { transform: translate(var(--mote-drift-x), calc(var(--mote-drift-y) * -1)); opacity: 0; }
}

@keyframes mote-drift-b {
  0%   { transform: translate(0, 0); opacity: 0; }
  20%  { opacity: 0.6; }
  50%  { transform: translate(calc(var(--mote-drift-x) * -0.4), calc(var(--mote-drift-y) * -0.5)); opacity: 0.8; }
  80%  { opacity: 0.15; }
  100% { transform: translate(calc(var(--mote-drift-x) * -0.8), calc(var(--mote-drift-y) * -1)); opacity: 0; }
}

.hero__shaving {
  position: absolute;
  inset-block-end: 20%;
  inset-inline-start: 42%;
  inline-size: var(--shaving-width);
  block-size: var(--shaving-height);
  border-radius: var(--radius-pill);
  background-color: var(--color-atmo-1);
  opacity: 0.55;
  transform-origin: 50% 50%;
  animation: shaving-drift var(--duration-drift-c) var(--ease-drift) infinite;
}

@keyframes shaving-drift {
  0%   { transform: translate(0, 0) rotate(0deg); }
  25%  { transform: translate(calc(var(--shaving-drift-x) * 0.8), calc(var(--shaving-drift-y) * -0.4)) rotate(8deg); }
  50%  { transform: translate(calc(var(--shaving-drift-x) * 0.3), calc(var(--shaving-drift-y) * -1)) rotate(-6deg); }
  75%  { transform: translate(calc(var(--shaving-drift-x) * -0.6), calc(var(--shaving-drift-y) * -0.4)) rotate(4deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}

/* Reduced motion (§8, §7 — no exceptions): string holds taut/straight;
   motes/shaving hold at rest, unrotated — the REST STATE each animation
   would otherwise be caught mid-cycle in. */
@media (prefers-reduced-motion: reduce) {
  .hero__string path {
    animation: none;
    d: path('M 8 38 Q 164 38 320 38');
  }

  .hero__mote,
  .hero__shaving {
    animation: none;
    opacity: 0;
    transform: none;
  }
}

.hero__facts {
  /* brief §5's arithmetic: facts row top = field bottom, exactly — zero
     gap, "facts follows field directly." */
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}

/* Sidebar-anchor body (brief §4): two columns >=48em (sticky rail +
   scrolling about); single column <48em (chip strip, then about). */

.body-grid {
  padding-block: var(--section-gap);
  display: grid;
  gap: var(--space-2xl);
}

@media (min-width: 48em) {
  .body-grid {
    grid-template-columns: var(--rail-width) minmax(0, 1fr);
    align-items: start;
    gap: var(--space-xl);
  }
}

main h2 {
  font-size: var(--text-h2);
}

/* Flow spacing for plain-paragraph sections — named wrappers below
   already carry their own margin. */
main h2 + p,
main h2 + .measure {
  margin-block-start: var(--space-md);
}

main .container > p + p {
  margin-block-start: var(--space-sm);
}

/* Base position (containing block for .rail::before below); >=48em
   overrides to sticky. Must precede that media query so it wins at equal
   specificity (source order, not media-query "priority"). */
.rail {
  position: relative;
}

@media (min-width: 48em) {
  .rail {
    position: sticky;
    inset-block-start: var(--sticky-offset);
  }
}

/* <48em: wrapped chip strip (brief §4). Copy unchanged at every
   breakpoint — only the packaging (chip vs. row) changes. */
.rail__list {
  margin: var(--space-md) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  counter-reset: rail;
}

.rail__item {
  counter-increment: rail;
  flex: 1 1 var(--rail-width);
  display: flex;
  align-items: flex-start;
  gap: var(--space-2xs);
  padding: var(--space-xs);
  background-color: var(--color-surface);
  border-radius: var(--radius-sm);
}

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

  .rail__item {
    padding: var(--space-xs) 0;
    background-color: transparent;
    border-radius: 0;
    border-block-end: var(--border-hairline) var(--border-style) var(--color-border);
  }

  .rail__item:last-child {
    border-block-end: 0;
  }
}

/* Rail index (brief §3, row 6): Bitter 700, accent, 01-08. */
.rail__item::before {
  content: counter(rail, decimal-leading-zero);
  flex: 0 0 auto;
  font: var(--font-weight-display) var(--text-lead)/var(--leading-tight) var(--font-display);
  color: var(--color-accent);
}

/* Sod-accent bullet (brief §2b/§3): fixed-size, never width-relative. */
.rail__bullet {
  flex: 0 0 auto;
  inline-size: var(--sod-bullet-size);
  block-size: var(--sod-bullet-size);
  border-radius: var(--radius-sm);
  background-color: var(--color-sod);
}

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

.rail__text b {
  font-weight: var(--font-weight-body-strong);
}

/* Rail texture (brief §6 slot 2): low-opacity CSS background, never a
   content <img>. webp-then-avif image-set(): a browser rejecting avif
   drops that declaration, keeps webp (precedent: legacy-lawn-and-
   landscape-providence-ut). .rail's own position: relative is declared
   above, ahead of the sticky media query. */
.rail::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  background-image: image-set(url('../assets/img/brick-mortar-coursing.webp') type('image/webp') 1x);
  background-image: image-set(url('../assets/img/brick-mortar-coursing.avif') type('image/avif') 1x);
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
  opacity: var(--rail-texture-opacity);
  pointer-events: none;
}

.about__row {
  margin-block-start: var(--space-md);
  display: grid;
  gap: var(--space-md);
  align-items: start;
}

@media (min-width: 48em) {
  .about__row {
    grid-template-columns: minmax(0, 1fr) var(--about-image-width);
  }
}

.about__text {
  margin: 0;
  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);
}

.about__image {
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.about__image img {
  width: 100%;
  height: auto;
}

/* Reviews [LOAD-BEARING] (§4, §8): hairline rule between quotes only
   (§3: an ornamented sub-list gets no counter). The oversized quote-glyph
   is the sole emphasis device alongside colour — no italic (§3). */

.reviews__row {
  margin-block-start: var(--space-md);
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}

@media (min-width: 48em) {
  .reviews__row {
    grid-template-columns: minmax(0, 1fr) var(--reviews-image-width);
  }
}

.reviews__list {
  display: grid;
  gap: 0;
}

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

.review:first-child {
  padding-block-start: 0;
  border-block-start: 0;
}

/* Opening-quote glyph (brief §3) — one ornament per quote, distinct
   from copy.md's own literal quotation marks (plain running text). */
.review__quote {
  position: relative;
  margin: 0;
  padding-inline-start: var(--space-lg);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
  color: var(--color-ink);
}

.review__quote::before {
  content: '\201C';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: var(--quote-glyph-offset);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-display);
  line-height: 1;
  color: var(--color-accent);
}

.review__attr {
  display: block;
  margin-block-start: var(--space-sm);
  font-family: var(--font-body);
  font-style: normal;
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--color-ink-muted);
}

.review__attr b {
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink);
}

.reviews__image {
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.reviews__image img {
  width: 100%;
  height: auto;
}

/* --- Process: distinct numeral system from the rail (brief §3) — plain
   decimal, Karla, ink-muted vs. the rail's Bitter/accent leading-zero. */

.process__list {
  margin: var(--space-md) 0 0;
  padding-inline-start: var(--space-lg);
  display: grid;
  gap: var(--space-md);
  list-style-type: decimal;
}

.process__list li::marker {
  font-family: var(--font-body);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-lead);
  color: var(--color-ink-muted);
}

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

.process__text b {
  font-weight: var(--font-weight-body-strong);
}

/* --- Form. One per site (§6). POSTs to main.js's endpoint constant;
   plain HTML POST with JS off. */

.form {
  margin-block-start: var(--space-md);
  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-small);
  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-sm);
}

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

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

.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-small);
  color: var(--color-ink-muted);
}

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

/* --- Footer. NAP, hours, copyright, footer/service-area landform image
   (brief §6 slot 5) as a low-opacity CSS backdrop — no alt owed. */

.site-footer {
  position: relative;
  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-small);
  color: var(--color-ink);
  overflow: hidden;
}

.site-footer::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  background-image: image-set(url('../assets/img/north-alabama-landform.webp') type('image/webp') 1x);
  background-image: image-set(url('../assets/img/north-alabama-landform.avif') type('image/avif') 1x);
  background-size: cover;
  background-position: 50% 60%;
  background-repeat: no-repeat;
  opacity: var(--footer-band-opacity);
  pointer-events: none;
}

.site-footer .container {
  position: relative;
}

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

.site-footer__nap {
  font-style: normal;
  line-height: var(--leading-body);
}

.site-footer__nap strong {
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h3);
}

/* --- thanks.html. No living-hero field here (§0: one spectacular moment
   per site, already spent on index.html). */

main .measure h1 {
  margin-block-start: var(--space-sm);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h2);
  line-height: var(--leading-tight);
  color: var(--color-ink);
}

main .measure p {
  margin-block-start: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--color-ink);
}

main .measure p:first-of-type {
  margin-block-start: var(--space-2xs);
}
