/* ==========================================================================
   site.css — per-site layout & components, built FROM tokens
   (house-tech-spec §3). Tokens only — no raw hex/px/font literals here; the
   two escape hatches are the house breakpoints (48em) and an explicit
   `@allow-literal: reason` comment on the preceding line.

   Archetype: asymmetric-collage, "the plates keep their own time" (brief
   §4, §7). Family: quiet-premium. This file carries the skeleton's own
   shell components (container, header/nav, buttons, form, footer)
   unmodified, then extends them with the hero's living-hero device and the
   six content sections in brief §4.4's order.
   ========================================================================== */

/* --- 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 and wires 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);
  /* House chrome, not a §2.6 role — sized at the eyebrow's own ceiling so
     the wordmark reads as a small display-face label rather than the
     larger --text-h3 role (the Materials cards' own specimen labels). */
  font-size: var(--text-eyebrow);
  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: closed is the plain CSS default below 48em, with no JS
   gate, so there is nothing left to collapse post-paint. JS-off visitors get
   the nav back via the <noscript> stylesheet override in
   @shared:head-boilerplate, which ships last in source order. */
.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--primary:hover,
.btn--primary:focus-visible {
  background-color: var(--color-accent-hover);
}

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

/* --- Display-face register: italic, everywhere (brief §2.1) ---------------
   Bodoni Moda carries the ENTIRE display register in italic — there is no
   upright posture anywhere on this site. base.css's h1-h4 rule sets the
   family/weight/leading but not style, so it is set once here for every
   display-face usage: the H1, every section head, and the wordmark. */
h1, h2, h3, h4,
.site-header__brand {
  font-style: italic;
}

/* --- Eyebrow (hero + thanks.html only — no mid-page section eyebrows exist
   in copy.md, brief §4.4) ---------------------------------------------------
   Mulish 700, tracked caps, accent colour. Natural case in the HTML,
   uppercased here (house-tech-spec §8) — never typed in caps. */
.eyebrow {
  margin-block-end: 0;
  font-family: var(--font-body);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-eyebrow);
  line-height: var(--leading-caps);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-accent);
}

/* Section heads: plain h2, no eyebrow kicker (copy.md gives none outside
   the hero). Services/Materials carry a decorative accent image beside the
   heading (brief §6 slots 1/2, alt=""); Trust/Reviews/Contact do not. */
.section__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-md);
  margin-block-end: var(--space-lg);
}

/* Every section head on the page (Services/Materials/Contact's
   .section__title, Trust-strip's .trust__title, Reviews' .reviews__title)
   shares one size role — brief §2.6's --text-h2. */
.section__title,
.trust__title,
.reviews__title {
  margin: 0;
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
}

.section__head-text .measure {
  margin-block-start: var(--space-xs);
  color: var(--color-ink-muted);
}

.section__head-accent {
  flex: 0 0 auto;
  overflow: hidden;
  border-radius: var(--radius-none);
}

.section__head-accent img {
  width: 100%;
  /* @allow-literal: an intrinsic accent-image cap, not a type/space value */
  max-width: 220px;
}

/* ==========================================================================
   HERO — asymmetric-collage, brief §4.1-§4.3, §5, §7. DOM order fixed at
   every width: eyebrow -> H1 -> CTA row -> place-line -> field. The
   living-hero moment is the three-stop radial atmosphere plus the two
   plates, swaying independently, never overlapping at any width.
   ========================================================================== */

.hero {
  position: relative;
  background: radial-gradient(130% 100% at 80% 0%,
    var(--color-atmos-hi) 0%, var(--color-atmos-mid) 55%, var(--color-atmos-lo) 100%);
  padding-block-start: var(--space-sm);
  padding-block-end: var(--section-gap);
}

@media (min-width: 48em) {
  .hero {
    padding-block-start: var(--space-2xl);
  }
}

.hero__grid {
  display: flex;
  flex-direction: column;
}

@media (min-width: 48em) {
  .hero__grid {
    flex-direction: row;
    align-items: flex-start;
    gap: var(--space-lg);
  }

  /* Text column at 55%, field column at 45% of (inner - gap) — flexbox's own
     proportional-shrink algorithm reproduces this exactly (brief §4.2): both
     basis percentages sum to 100%, the gap forces a negative free space, and
     shrink is distributed proportionally to each item's own basis. */
  .hero__text {
    flex: 0 1 55%;
    min-width: 0;
  }

  .hero__field {
    flex: 0 1 45%;
    min-width: 0;
    /* Field column starts 24px lower than the text column's own top
       (brief §4.1 rule 2's "intentional imbalance"). */
    margin-block-start: var(--space-md);
  }
}

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

@media (min-width: 48em) {
  .hero__eyebrow {
    margin-block-end: var(--space-sm);
  }
}

.hero__title {
  font-size: var(--text-h1);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  margin-block-end: var(--space-md);
  color: var(--color-ink);
}

@media (min-width: 48em) {
  .hero__title {
    margin-block-end: var(--space-lg);
  }
}

/* The CTA card — brief §3.2 (--color-surface used for "the two material
   plates, the CTA card, form fields"), §3.2 R3 (radius-sm on the plates and
   the CTA card only), §5 (exact padding figures). An opaque sibling block
   over the gradient (§3.5) — never a ground text sits on except the eyebrow
   and H1 above it. */
.hero__cta-card {
  background-color: var(--color-surface);
  border-radius: var(--radius-sm);
  padding-block: var(--hero-cta-pad-top) var(--hero-cta-pad-bottom);
  padding-inline: var(--space-md);
}

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

.hero__cta-row .btn {
  /* 48px tall at every breakpoint (brief §5) — one step above the house
     44px touch-target floor. */
  min-height: var(--hero-cta-height);
}

.hero__place-line {
  margin: 0;
  margin-block-start: var(--hero-cta-row-gap);
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* --- The field: two plates, never overlapping at any width (brief §4.3) ---
   Normal-flow flex row, not absolute positioning: each plate sizes to its
   OWN content (height: auto), so real copy never clips inside a height
   the brief's worked example only estimated. Non-overlap holds: two flex
   siblings on percentage bases with a percentage gap never share an
   x-range. `align-items: flex-start` + plate-2's own margin-block-start
   gives the stagger as real flow height, not a fixed inset. */

.hero__field {
  display: flex;
  align-items: flex-start;
  /* @allow-literal: field-container-relative inset/gap, not a type/space value */
  gap: 24%;
  padding-inline-start: 2%;
  margin-block-start: var(--hero-card-field-gap);
}

@media (min-width: 48em) {
  .hero__field {
    margin-block-start: 0;
  }
}

.plate {
  /* Percentage geometry (brief §4.3): width 37% of the field container,
     never a fixed pixel — the 24% gap this leaves between the two plates is
     therefore proportional and cannot close at any field width >= 0. */
  flex: 0 0 37%;
  background-color: var(--color-surface);
  border: var(--border-hairline) var(--border-style) var(--color-plate-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-plate);
  padding-block: var(--plate-pad-block);
  padding-inline: var(--plate-pad-inline);
  transform-origin: 50% 50%;
}

.plate--1 {
  transform: rotate(var(--plate-1-rest));
  animation: plate-sway-1 var(--duration-plate-1) var(--ease-in-out) infinite;
}

.plate--2 {
  /* The vertical stagger (brief §4.3) — real flow margin, not a top inset,
     so it adds to the field's own auto height instead of eating into it. */
  margin-block-start: var(--hero-plate-stagger);
  transform: rotate(var(--plate-2-rest));
  animation: plate-sway-2 var(--duration-plate-2) var(--ease-in-out) infinite;
}

.plate__tag {
  display: block;
  margin: 0;
  font-family: var(--font-body);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-eyebrow);
  line-height: var(--leading-caps);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-accent);
}

.plate__body {
  margin: 0;
  margin-block-start: var(--space-3xs);
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

@keyframes plate-sway-1 {
  0%, 100% { transform: rotate(calc(var(--plate-1-rest) - var(--plate-1-sway))); }
  50%      { transform: rotate(calc(var(--plate-1-rest) + var(--plate-1-sway))); }
}

@keyframes plate-sway-2 {
  0%, 100% { transform: rotate(calc(var(--plate-2-rest) - var(--plate-2-sway))); }
  50%      { transform: rotate(calc(var(--plate-2-rest) + var(--plate-2-sway))); }
}

/* prefers-reduced-motion (house-tech-spec §8, brief §7): freeze both plates
   at their REST angle and stop the loop. base.css's kill switch collapses
   the shared --duration-* tokens, not these plate-specific ones. */
@media (prefers-reduced-motion: reduce) {
  .plate--1,
  .plate--2 {
    animation: none;
  }

  .plate--1 { transform: rotate(var(--plate-1-rest)); }
  .plate--2 { transform: rotate(var(--plate-2-rest)); }
}

/* SERVICES (#services) — brief §4.4 step 3. A plain numbered index
   (ref-011's editorial index-list typography), not a card grid. LOAD-BEARING
   (playbook §2 row 2). */

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

.services-list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: service;
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
}

.services-list__item {
  counter-increment: service;
  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-border);
}

.services-list__item::before {
  content: counter(service, decimal-leading-zero);
  flex: 0 0 auto;
  /* @allow-literal: fixed numeral column so item text aligns, not a token value */
  min-width: 2ch;
  font-family: var(--font-body);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-caps);
  color: var(--color-accent);
}

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

/* ==========================================================================
   MATERIALS, NAMED (#materials) — brief §4.4 step 4. The static reuse of the
   plate register: three flat specimens, no motion, no card boundary (the
   two hero plates and the CTA card are the only cards on the site — brief
   §3.2 R3). Plain grid items on the page ground.
   ========================================================================== */

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

.materials-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
}

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

.materials-grid__title {
  margin: 0;
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-ink);
}

.materials-grid__body {
  margin: 0;
  margin-block-start: var(--space-2xs);
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* ==========================================================================
   TRUST-STRIP (#trust) — brief §4.4 step 5. The three V-tier facts this
   dossier's reputation floor permits. Sunken band ground, the local-
   character band image as a low-opacity backdrop (brief §6 slot 3).
   ========================================================================== */

.trust {
  position: relative;
  overflow: hidden;
  background-color: var(--color-surface-sunken);
}

.trust__band {
  position: absolute;
  inset: 0;
  z-index: var(--z-atmosphere);
  background-image: url('../assets/img/san-antonio-treeline-band.webp');
  background-image: image-set(
    url('../assets/img/san-antonio-treeline-band.avif') type('image/avif'),
    url('../assets/img/san-antonio-treeline-band.webp') type('image/webp')
  );
  background-size: cover;
  background-position: center;
  /* @allow-literal: opacity is a unitless ratio, not a size/colour/font value */
  opacity: 0.22;
}

.trust .container {
  position: relative;
  z-index: 2;
}

.trust__title {
  color: var(--color-ink);
}

.trust-list {
  margin: 0;
  margin-block-start: var(--space-lg);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-md);
}

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

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

/* ==========================================================================
   REVIEWS (#reviews) — brief §4.4 step 6. Four verbatim quotes, never an
   aggregate. Section ground is the same sunken band as Trust-strip (brief
   §3.3 rows 3/6 name both together); each quote sits in a plain white inset
   block (no radius, no shadow, no border — not a "card," §3.2 R3 reserves
   that treatment for the two plates and the CTA card only).
   ========================================================================== */

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

.reviews__title {
  color: var(--color-ink);
}

.reviews__intro {
  margin: 0;
  margin-block-start: var(--space-xs);
  color: var(--color-ink-muted);
}

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

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

.review {
  margin: 0;
  background-color: var(--color-surface);
  padding: var(--space-md);
}

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

.review__attribution {
  margin: 0;
  margin-block-start: var(--space-sm);
  color: var(--color-ink-muted);
  font-size: var(--text-small);
  font-style: normal;
}

/* ==========================================================================
   FINAL CTA / CONTACT (#contact) — brief §4.4 step 7. Quote-form primary
   (shape 1: name/phone/message), phone secondary. Uses the skeleton's own
   .section/.form/.field components unmodified.
   ========================================================================== */

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

/* Two columns at desktop (ask, then form) instead of one narrow stacked
   column left-hugging a wide container. */
.contact__grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

@media (min-width: 48em) {
  .contact__grid {
    flex-direction: row;
  }

  .contact__ask,
  .section--contact .form {
    flex: 1 1 0;
    max-width: none;
  }
}

.contact__intro {
  margin: 0;
  margin-block-start: var(--space-xs);
  color: var(--color-ink-muted);
}

.contact__phone-row {
  margin-block-start: var(--space-md);
}

.section--contact .form {
  margin-block-start: var(--space-lg);
}

@media (min-width: 48em) {
  .section--contact .form {
    margin-block-start: 0;
  }
}

/* ==========================================================================
   THANKS.HTML — brief §4.7. Eyebrow (a brief-required addition to the
   generic skeleton's thanks section, copy.md §4) -> H1 -> body -> next step
   -> CTA row. Treated as a single .section, not a second hero (§0 permits
   exactly one spectacular moment per site, already spent on index.html).
   ========================================================================== */

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