/* ==========================================================================
   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);
  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);
}

.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);
}

/* --- Eyebrow (house pattern) ------------------------------------------------
   Natural-case copy, uppercased in CSS (spec §8 — assistive tech spells a
   typed-caps string letter by letter; text-transform keeps the source case
   intact for anyone copying it out). */

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

/* --- Hero — "The Index" living-hero moment (brief §6, §5.1) ----------------
   Single editorial column at every width (brief §2's geometry ruling); the
   generated specimen and the live verified-tick are absolute overlays inside
   the hero's own padding box, never block-level, so they cannot displace the
   in-flow order idx-mark -> eyebrow -> rule -> h1 -> CTA -> lead. */

.hero {
  position: relative;
  overflow: clip;
  padding-block-start: var(--space-sm);
  padding-block-end: var(--section-gap);
  background:
    radial-gradient(130% 90% at 85% 8%, color-mix(in srgb, var(--color-accent) 10%, transparent), transparent 55%),
    radial-gradient(120% 100% at 10% 100%, color-mix(in srgb, var(--color-verify) 9%, transparent), transparent 55%),
    var(--color-bg);
}

.hero__inner {
  max-inline-size: var(--measure-hero);
}

.hero__idx {
  margin: 0 0 var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-index-num);
  line-height: 1;
  color: var(--color-ink-muted);
}

.hero__eyebrow {
  margin-block-end: var(--space-2xs);
}

.hero__rule {
  block-size: var(--border-hairline);
  inline-size: 100%;
  max-inline-size: var(--space-2xl);
  margin-block-end: var(--space-sm);
  background-color: var(--color-border-strong);
}

.hero__title {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-h1);
  font-weight: var(--font-weight-display);
}

.hero__cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  margin: 0 0 var(--space-md);
}

.hero__phone-link {
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink);
  text-underline-offset: var(--space-3xs);
}

/* The lead is the one in-flow hero element whose row range can coincide with
   the absolute specimen field's bottom-right corner (brief §5.1's own worked
   table has the same overlap in its row ranges). It reserves the field's own
   footprint so the text column never runs under the image, per §6's "never
   overlapping the text column's own measure" -- measured and confirmed at
   375/640/900/1440 rather than assumed. */
.hero__lead {
  margin: 0;
  max-inline-size: calc(100% - var(--hero-field-w) - var(--space-md));
  font-size: var(--text-lead);
  line-height: var(--leading-body);
  color: var(--color-ink);
}

/* QA round-1 FIX (clarity/dissonance): below 900px the lockup (idx-mark
   through the primary CTA — the content already confirmed above the 375px
   fold) is the living-hero device's own containing block, so the device
   anchors to ITS bottom edge, not the hero section's total (copy-length-
   dependent) height. Reverted to a plain static wrapper at >=900px below,
   where the device anchors to the hero itself again, per brief §6. */
.hero__lockup {
  position: relative;
}

/* The specimen overlay — absolute inside its containing block, sized per
   breakpoint (brief §5.1/§6). It never participates in document flow, so it
   cannot compete with the primary CTA for the first screen. */
.hero__field {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-end: 0;
  margin-block-start: var(--space-sm);
  inline-size: var(--hero-field-w);
  block-size: var(--hero-field-h);
  margin-inline: 0;
  margin-block-end: 0;
}

.hero__field img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  object-position: bottom;
}

/* The whole specimen image sways as a rigid body from a fixed base — a
   single flat raster image, not individually-animated paths (brief §6). */
.specimen {
  transform-origin: 50% 100%;
  animation: sprigSway var(--duration-sprig-sway) var(--ease-in-out) infinite;
}

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

/* The live verified-tick — never AI-generated, a plain drawn ring and check,
   independently positioned at the hero's own bottom-left corner (brief §6);
   below 900px that corner is the lockup's, not the hero's (see the FIX
   comment on .hero__lockup above). Margin is tighter than the field's own
   (--space-2xs, not --space-sm): the lead paragraph starts almost immediately
   below the lockup at this width, and the smaller gap is what keeps the
   tick's own box from dipping into the lead's line box. */
.verify-tick {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
  margin-block-start: var(--space-2xs);
  inline-size: var(--verify-tick-size);
  block-size: var(--verify-tick-size);
  animation: tickPulse var(--duration-tick-pulse) var(--ease-in-out) infinite;
}

@keyframes tickPulse {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}

/* Reduced motion — absolute, no exception (house-tech-spec §8, brief §6).
   These two loops run on brief-specific duration tokens rather than the
   house --duration-* scale base.css already collapses, so they get their own
   kill switch here. */
@media (prefers-reduced-motion: reduce) {
  .specimen,
  .verify-tick {
    animation: none;
  }

  .specimen {
    transform: rotate(0deg);
  }

  .verify-tick {
    opacity: 1;
  }
}

/* @allow-literal: brief §5.1/§6 states the field's geometry change at exactly
   900px, a brief-specified breakpoint distinct from the two house defaults. */
@media (min-width: 56.25em) {
  .hero {
    min-block-size: var(--hero-min-block-lg);
  }

  /* QA round-1 FIX: at >=900px the hero comfortably clears the fold already
     (Critic's own measurement: 100% visible) — revert the device to the
     brief §6 anchor, the hero's own bottom-right/bottom-left corner, rather
     than the lockup's. */
  .hero__lockup {
    position: static;
  }

  .hero__field {
    inset-block-start: auto;
    inset-block-end: var(--space-sm);
    inset-inline-end: var(--space-sm);
    margin-block-start: 0;
    inline-size: var(--hero-field-w-lg);
    block-size: var(--hero-field-h-lg);
  }

  .verify-tick {
    inset-block-start: auto;
    inset-block-end: var(--space-sm);
    inset-inline-start: var(--space-sm);
    margin-block-start: 0;
  }

  .hero__lead {
    max-inline-size: calc(var(--measure-hero) - var(--hero-field-w-lg) - var(--space-md));
  }
}

/* Short-viewport floor (brief §5.1): the field is the element permitted to
   shrink before any text element is touched; the verify-tick never shrinks.
   The brief names the floor's DIMENSIONS (64x88) but not the trigger height;
   500px is this build's engineering default for "very short (landscape
   tablet)" and is flagged back to the Art Director for confirmation. */
/* @allow-literal: unspecified trigger height for brief §5.1's short-viewport
   floor -- see the comment above. */
@media (max-height: 31.25em) {
  .hero__field {
    inline-size: var(--hero-field-w-floor);
    block-size: var(--hero-field-h-floor);
  }
}

/* --- Roster-Index (brief §6.5, §6.6) ----------------------------------------
   Three numbered rows, restarting the numeral count at "01" -- a separate,
   non-adjacent device from the hero's own idx-mark (brief §6's two-counters
   note): different colour role AND weight, per the craft margin the brief
   states even though no nesting occurs. */

.roster__head {
  max-inline-size: var(--measure-hero);
  margin-block-end: var(--space-lg);
}

.roster__intro {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-lead);
  color: var(--color-ink-muted);
}

.roster__list {
  display: grid;
  gap: var(--space-lg);
  max-inline-size: var(--measure-hero);
  margin: 0 0 var(--space-lg);
  padding: 0;
}

.roster__row {
  background-color: var(--color-surface);
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
}

/* The static reuse of the verify-tick: flattened, no animation, fixed 14px --
   the corner mark that opens every Roster-Index row AND the Request-a-Quote
   section (brief §6's "static reuse" device, so the page reads as one
   continuous "checked record" object). Shared by any container marked
   .tick-row, not scoped to the roster alone. */
.tick-row {
  position: relative;
  padding: var(--space-md);
  padding-inline-start: calc(var(--verify-tick-size) + var(--space-md) * 2);
}

.tick-row .verify-tick {
  position: absolute;
  inset-block-start: var(--space-md);
  inset-block-end: auto;
  inset-inline-start: var(--space-md);
  animation: none;
  opacity: 1;
}

.roster__num {
  display: block;
  margin: 0 0 var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display-strong);
  font-size: var(--text-index-num);
  line-height: 1;
  color: var(--color-accent);
}

.roster__title {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-h3);
}

.roster__lead {
  margin: 0 0 var(--space-2xs);
  color: var(--color-ink-muted);
}

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

/* Row 03's named-client callout -- Literata Italic, the one recurring role
   for the emphasis face (brief §2, §6.5, ref-006's pull-quote band). */
.roster__clients {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-display-italic);
  font-style: italic;
  font-size: var(--text-h3);
  color: var(--color-ink);
}

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

.roster__closing {
  max-inline-size: var(--measure-hero);
  font-size: var(--text-lead);
}

/* --- Full-bleed dividers (brief §6.6, §7) -----------------------------------
   The earth/turf-edge generated image, full-bleed per brief §7's placement.
   It ships OUTSIDE any `.container` in the markup, so `inline-size: 100%`
   already reaches the viewport edge with no breakout hack needed; the
   image's own aspect ratio keeps the band shallow. */

.divider {
  inline-size: 100%;
  overflow: hidden;
}

.divider img {
  inline-size: 100%;
  block-size: auto;
  object-fit: cover;
}

/* The sky-linework closing accent -- small and quiet, in normal flow inside
   the single editorial column (brief §2's geometry ruling holds sitewide),
   never full-bleed and never beside the form as a second column. */
.contact__accent {
  inline-size: 100%;
  max-inline-size: var(--space-3xl);
  margin: var(--space-lg) 0 0;
}

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

.contact__secondary {
  margin-block-start: var(--space-md);
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}

/* --- 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;
}
