/* ==========================================================================
   site.css — per-site layout & components, built FROM tokens.
   HARD RULE (house-tech-spec §3): no raw hex/px/rem/font literals in this
   file — tokens only, plus the breakpoint and @allow-literal escape hatches.
   The skeleton ships only the shell (header, buttons, form, footer); the
   brief's components (hero, signature device, services, notes, process,
   quote) are appended below, per runs/duluth-lawn-care-duluth-mn/brief.md.
   ========================================================================== */

/* --- 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);
  padding-block: var(--space-sm);
  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);
}

/* No nav menu (copy.md, header section: "The brief specifies no header
   content beyond sticky chrome and the plank-1 eyebrow/H1/CTA sequence" —
   a one-pager this short reads end to end without one). The skeleton's
   .nav-toggle/.site-nav rules are dropped rather than left as dead CSS:
   index.html and thanks.html carry no nav-toggle button and no .site-nav
   class, so nothing on the page would ever match them. A bare, unclassed
   <nav aria-label="Contact"> wraps the phone control for the landmark
   (spec §8) — it carries no styling of its own and no menu items; the
   header stays a plain two-item row at every width: wordmark, phone. */

.site-header__phone {
  font-family: var(--font-body-strong);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-small);
  color: var(--color-ink-muted);
  text-decoration: none;
}

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

/* --- 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 {
  /* --color-border aliases the brief's decorative-only --color-rule (R4);
     a button's own edge is a control boundary, so it uses --color-ink-muted
     instead — already contrast-verified at 5.68:1 against bg (brief §3.1 F3),
     well past the 3:1 non-text floor. */
  border-color: var(--color-ink-muted);
  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);
  /* A form field's border is a real control boundary, not decoration — it
     uses --color-ink-muted (5.06:1 against surface, brief §3.1 F4), never
     --color-border/--color-rule, which R4 restricts to decorative use only. */
  border: var(--border-hairline) var(--border-style) var(--color-ink-muted);
  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;
}

.site-footer__services {
  color: var(--color-ink-muted);
}

/* ============================================================================
   BRIEF COMPONENTS — runs/duluth-lawn-care-duluth-mn/brief.md
   family: warm-craft · archetype: asymmetric-collage
   signature: "the pinned notes settle" (brief §8)
   ============================================================================ */

/* --- Section eyebrow + title, and the corner mark (brief §8.6) ------------
   The settled pin-and-card silhouette, static, precedes every section <h2>
   below the hero. Never shrinks below --corner-mark-min or moves; the
   heading wraps in its own column instead (§8.6's yield rule) — why
   .section__title is a flex row, mark non-shrinking. */

.eyebrow {
  margin-block-end: var(--hero-gap-sm);
  font-family: var(--font-body-strong);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

.section__title {
  display: flex;
  align-items: flex-start;
  gap: var(--corner-mark-gap);
  margin-block-end: var(--space-md);
  font-size: var(--text-h2);
  line-height: var(--leading-tight);
}

.section__title-text {
  flex: 1 1 auto;
  min-width: 0;
}

.section-mark {
  /* display:inline-flex — not the default inline — so inline-size/block-size
     apply everywhere this mark is used, including the notes cards (§9.4),
     which are not themselves flex containers and would otherwise leave a
     bare inline <span> unsized and the SVG inside it at its own intrinsic
     default box. flex:0 0 … only has an effect inside .section__title's
     flex row; it is a harmless no-op everywhere else. */
  display: inline-flex;
  flex: 0 0 var(--corner-mark-size);
  inline-size: var(--corner-mark-size);
  block-size: var(--corner-mark-size);
  min-inline-size: var(--corner-mark-min);
}

.section-mark svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* --- Hero (brief §7, §9.2) --------------------------------------------------
   DOM order: eyebrow -> h1 -> primary CTA -> field -> lead -> secondary CTA
   (plank 1). Text sits on the flat gradient (brief §3.3), never the field's
   raster plate (R2). */

.hero {
  position: relative;
  padding-block-start: var(--hero-inset-top);
  padding-block-end: var(--section-gap);
  background: linear-gradient(165deg, var(--color-bg) 0%, var(--color-hero-gradient-end) 100%);
  color: var(--color-ink);
}

.hero__eyebrow {
  margin-block-end: var(--hero-gap-sm);
}

.hero__title {
  margin-block-end: var(--hero-gap-lg);
  font-size: var(--text-h1);
}

.hero__title .accent {
  color: var(--color-accent);
}

.hero__cta {
  margin-block-end: var(--hero-gap-lg);
  height: var(--cta-height);
  padding-block: 0;
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
}

.hero__lead {
  margin-block-start: var(--hero-gap-lg);
  max-width: var(--hero-lead-measure);
  font-size: var(--text-body);
}

.hero__cta-secondary {
  display: inline-block;
  margin-block-start: var(--space-xs);
  font-size: var(--text-small);
  color: var(--color-ink-muted);
  text-decoration: underline;
  text-underline-offset: var(--space-3xs);
}

/* DOM order stays fixed at every width (brief §7's plank-1 shape: eyebrow ->
   h1 -> primary CTA -> field -> lead -> secondary CTA) — the two-column
   layout at >=48rem is CSS Grid's own item placement, never a reordering of
   the markup a screen reader or a JS-off visitor would see. Below 48em
   .hero__inner is plain block flow, so the grid rules simply do not apply
   and everything stacks in source order (brief §6.2's single-column case). */
@media (min-width: 48em) {
  .hero__inner {
    display: grid;
    grid-template-columns: 1fr var(--hero-field-inline-size);
    column-gap: var(--space-xl);
    align-items: start;
  }

  .hero__eyebrow,
  .hero__title,
  .hero__cta-row,
  .hero__lead,
  .hero__cta-secondary-row {
    grid-column: 1;
  }

  .hero__field {
    grid-column: 2;
    grid-row: 1 / -1;
  }
}

/* --- The signature device: "the pinned notes settle" (brief §8) -----------
   Paint order per R3: atmosphere plate + verdigris strip share
   --z-atmosphere (the strip is the field's own overlay, painted right after
   the plate); treeline sprigs at --z-treeline; note cards at --z-collage —
   see README.md for the four-vs-five-layer reading. One <svg viewBox="0 0
   116 76"> per card (116:76 and 150:98 are the same ratio to 3 decimals),
   sized by CSS per breakpoint so the artwork never distorts. */

.hero__field {
  position: relative;
  overflow: hidden;
  block-size: var(--field-height);
  background-color: var(--color-surface);
  background-image: url('../assets/img/hero-atmosphere-treeline.webp');
  background-image: image-set(
    url('../assets/img/hero-atmosphere-treeline.avif') type('image/avif'),
    url('../assets/img/hero-atmosphere-treeline.webp') type('image/webp')
  );
  background-size: cover;
  background-position: center;
  border: var(--border-hairline) var(--border-style) var(--color-field-border);
  z-index: var(--z-field);
}

@media (min-width: 48em) {
  .hero__field {
    block-size: var(--field-height-wide);
  }
}

.hero__field-strip {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: var(--field-strip-height);
  background-color: var(--color-accent-2);
  opacity: 0.5;
  z-index: var(--z-atmosphere);
}

.hero__sprig {
  position: absolute;
  inset-block-end: 0;
  z-index: var(--z-treeline);
  transform-origin: bottom center;
}

.hero__sprig--1 { inset-inline-start: 11%; }
.hero__sprig--2 { inset-inline-start: 89%; transform: translateX(-100%); }

.hero__sprig svg {
  display: block;
}

.hero__card-slot {
  position: absolute;
  inline-size: var(--plate-w);
  block-size: var(--plate-h);
  transform: translate(-50%, -50%);
  z-index: var(--z-collage);
}

/* Positions transcribe the C2-winning tile's own built composition
   (tiles/tile-a.html: card1 centred at 110,95 and card2 at 255,128 inside
   its 375x220 mock), expressed as percentages of the field so the same
   relative layout holds at both field sizes. */
.hero__card-slot--1 { inset-inline-start: 29.3%; inset-block-start: 43.2%; }
.hero__card-slot--2 { inset-inline-start: 68%; inset-block-start: 58.2%; }

@media (min-width: 48em) {
  .hero__card-slot {
    inline-size: var(--plate-w-wide);
    block-size: var(--plate-h-wide);
  }
}

.hero__card {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  transform-origin: 50% 20%;
}

@media (prefers-reduced-motion: no-preference) {
  .hero__card--1 {
    animation: hero-settle-1 var(--duration-settle-1) ease-in-out infinite;
  }

  .hero__card--2 {
    animation: hero-settle-2 var(--duration-settle-2) ease-in-out infinite var(--delay-settle-2);
  }

  .hero__sprig--1 {
    animation: hero-sway var(--duration-sway-1) ease-in-out infinite;
  }

  .hero__sprig--2 {
    animation: hero-sway var(--duration-sway-2) ease-in-out infinite var(--delay-sway-2);
  }
}

@keyframes hero-settle-1 {
  0%   { transform: rotate(-7deg) translateY(var(--settle-drift-1)); opacity: 0.85; }
  45%  { transform: rotate(-3.5deg) translateY(0); opacity: 1; }
  100% { transform: rotate(-3.5deg) translateY(0); opacity: 1; }
}

@keyframes hero-settle-2 {
  0%   { transform: rotate(6deg) translateY(var(--settle-drift-2)); opacity: 0.85; }
  45%  { transform: rotate(3deg) translateY(0); opacity: 1; }
  100% { transform: rotate(3deg) translateY(0); opacity: 1; }
}

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

/* Reduced motion, stated positively (brief §8.5): both cards freeze at
   their SETTLED end-state, fully legible, full opacity; the sprigs rest at
   0deg. The atmosphere plate, gradient and palette are already static. */
@media (prefers-reduced-motion: reduce) {
  .hero__card--1 { animation: none; transform: rotate(-3.5deg) translateY(0); opacity: 1; }
  .hero__card--2 { animation: none; transform: rotate(3deg) translateY(0); opacity: 1; }
  .hero__sprig--1, .hero__sprig--2 { animation: none; transform: rotate(0deg); }
}

/* --- Services (#services, brief §9.3) --------------------------------------
   A plain hairline-divided index, no cards, no icons (ref-008). Numerals:
   Display 700/accent per §9.3 and §9.8 (README.md flags the §4.1 role-table
   row this supersedes). */

.services__list {
  border-block-start: var(--border-hairline) var(--border-style) var(--color-rule);
}

.services__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  padding-block: var(--space-sm);
  border-block-end: var(--border-hairline) var(--border-style) var(--color-rule);
}

.services__num {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h2);
  color: var(--color-accent);
}

.services__body {
  display: block;
}

.services__name {
  display: block;
  font-family: var(--font-body-strong);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-body);
}

.services__desc {
  display: block;
  margin-block-start: var(--space-3xs);
  color: var(--color-ink-muted);
  font-size: var(--text-small);
}

/* --- Decorative divider bands (brief §10, slots 2-3) — aria-hidden, no alt
   (copy.md's ruling). width/height on the <img> holds the ratio for CLS. */

.section__divider {
  margin-block: var(--space-lg);
  overflow: hidden;
  border: var(--border-hairline) var(--border-style) var(--color-rule);
}

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

/* --- What neighbours say (#notes, brief §6.1, §9.4) ------------------------
   Two off-axis note cards plus one plain third slot, fixed reading order,
   zero motion of its own (§0's "one spectacular moment" stays the hero's).
   --shadow-notes is the only drop shadow on the site (§5.1). */

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

.notes__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--notes-card-min), 1fr));
  gap: var(--space-lg) var(--space-md);
}

.notes__card {
  padding: var(--space-md);
  background-color: var(--color-bg);
  border: var(--border-hairline) var(--border-style) var(--color-accent);
  box-shadow: var(--shadow-notes);
}

.notes__card .section-mark {
  margin-block-end: var(--space-sm);
}

.notes__card--a {
  transform: rotate(-2.2deg);
}

.notes__card--b {
  transform: rotate(1.6deg);
}

.notes__quote {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-display-regular);
  font-weight: var(--font-weight-display-regular);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.notes__attr {
  margin: 0;
  font-family: var(--font-body-strong);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}

.notes__context {
  display: flex;
  align-items: center;
  padding-inline: var(--space-2xs);
}

.notes__context p {
  margin: 0;
  color: var(--color-ink-muted);
}

/* --- How it works (#how-it-works, brief §9.5) ------------------------------
   Plain Arabic circle badges — a visually distinct system from the services
   index's Display-700 numerals (brief §9.8's nested-counter check). */

.process__list {
  display: grid;
  gap: var(--space-lg);
}

.process__step {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
}

.process__badge {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  inline-size: var(--process-badge-size);
  block-size: var(--process-badge-size);
  border-radius: var(--radius-round);
  background-color: var(--color-ink-muted);
  color: var(--color-bg);
  font-family: var(--font-body-strong);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-body);
}

.process__label {
  margin: 0 0 var(--space-3xs);
  font-size: var(--text-body);
}

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

/* --- Get a quote (#quote, brief §9.6) — Name/Phone required, Email/Message
   optional (brief §12). The form has its own wrapper so an empty
   FORM_ENDPOINT would hide only the <form>, never the always-visible
   phone/email fallback beside it. */

.quote__lead {
  margin-block-end: var(--space-md);
}

.quote__fallback {
  margin-block-start: var(--space-lg);
  margin-block-end: var(--space-2xs);
  font-weight: var(--font-weight-body-strong);
}

.quote__contacts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  margin: 0;
}

/* --- thanks.html (brief §7's three-answer table) ---------------------------
   A thin confirmation page: everything sits near the top, well clear of the
   full inter-section rhythm a content page uses. The house .section class's
   --section-gap padding (96px) would push the confirmation line and the
   tel: CTA far past the brief's stated targets, so this page reuses the
   hero's own tighter rhythm tokens instead of the section scale. */

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

.thanks__title {
  margin-block-end: var(--hero-gap-sm);
  font-size: var(--text-h2);
}

.thanks__body {
  margin-block-end: var(--hero-gap-lg);
}

.thanks__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}
