/* ==========================================================================
   site.css — Lewis Property Maintenance, Champaign IL.
   Archetype: ledger-stack (brief §3). Signature: "the stamp lands" (brief
   §2.8, §5). HARD RULE (house-tech-spec §3): no raw hex, no px/rem size
   literals, no font-name literals in this file. Tokens only. Escape hatches:
   house breakpoints and an explicit `@allow-literal: reason` comment.
   ========================================================================== */

/* --- Layout primitives -----------------------------------------------------
   ledger-stack is a centred, single-column read at every width (brief §2.7)
   — --hero-col-max is "the site's single content-column cap", so .container
   uses it everywhere, not the house's wider --layout-container. */

.container {
  width: 100%;
  max-width: var(--hero-col-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.section {
  padding-block: var(--section-gap);
}

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

.eyebrow {
  display: block;
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-body-strong);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin-block-end: var(--space-xs);
}

/* --- Header & navigation --------------------------------------------------
   brief §3.1: sticky header, 52px content + 2px dashed rule = 54px. 4px
   padding-block (--space-3xs) around a 44px tap-target row nets exactly
   52px. Sticky per house-tech-spec §2's Nic placement ruling. */

.site-header {
  /* Sticky at the true viewport top is the sold-site default. During
     preview, css/preview.css pushes this down by --expiry-height; that
     override is deleted at purchase and this rule is what is left. */
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  padding-block: var(--space-3xs);
  border-block-end: var(--border-header) var(--border-dashed) var(--color-border);
  background-color: var(--color-bg);
}

.site-header__inner {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

/* Compact (<=479px): a small "LPM" monogram, echoing the hero stamp-ring
   shape without repeating its asset — a chip, not a circle, so it reads as
   typography rather than a second stamp. */
.site-header__brand-compact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* @allow-literal: a square monogram chip sized to the header's own tap-min token */
  width: var(--layout-tap-min);
  height: var(--layout-tap-min);
  border-radius: var(--radius-pill);
  background-color: var(--color-accent);
  color: var(--color-accent-ink);
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-display);
  text-decoration: none;
  letter-spacing: var(--tracking-normal);
}

/* Full (>=480px, copy.md §3): Karla 700 — a display face this small loses
   legibility; Yeseva One is reserved for the hero H1 and section heads. */
.site-header__brand-full {
  display: none;
  align-items: center;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: var(--text-brand);
  font-weight: var(--font-weight-body-strong);
  text-decoration: none;
  letter-spacing: var(--tracking-normal);
  color: var(--color-ink);
}

/* @allow-literal: brief §3.1's own compact/full split, independent of the house 48em nav breakpoint */
@media (min-width: 30em) {
  .site-header__brand-compact {
    display: none;
  }

  .site-header__brand-full {
    display: inline-flex;
  }
}

/* Wraps so the nav list (JS-off, or opened on a narrow viewport) drops to
   its own line below the toggle + call control. */
.site-header__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-xs);
  row-gap: var(--space-xs);
}

.nav-toggle {
  order: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-xs);
  /* Its only shape is this hairline, so it is a CONTROL boundary (WCAG
     1.4.11, >=3:1), not decorative — --color-border-strong, not --color-border. */
  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);
  background-color: var(--color-bg);
}

.site-header__call-compact,
.site-header__call-full {
  order: 2;
}

/* Element-qualified: beats .btn's own later, equally-specific padding
   shorthand (both (0,1,0), .btn declared later wins ties otherwise).
   min-width: 0 overrides the flex-item default (min-width: auto), which
   would otherwise grow this box past 44px to fit the 20px icon. */
a.site-header__call-compact {
  padding-inline: 0;
  width: var(--layout-tap-min);
  min-width: 0;
}

/* Element-qualified for the same specificity-tie reason. */
a.site-header__call-full {
  display: none;
}

/* @allow-literal: matches the brand split above */
@media (min-width: 30em) {
  a.site-header__call-compact {
    display: none;
  }

  a.site-header__call-full {
    display: inline-flex;
  }
}

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

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

/* House pattern: closed is the plain CSS default below 48em, no JS gate, so
   nothing collapses post-paint. JS-off gets the nav back via <noscript> in
   @shared:head-boilerplate. */
.site-nav {
  display: none;
  order: 3;
  flex-basis: 100%;
}

[data-nav-ready] .site-nav[data-nav-open='true'] {
  display: block;
}

@media (min-width: 48em) {
  .site-nav {
    display: block;
    flex-basis: auto; /* undoes the mobile full-row wrap */
  }

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

/* --- Buttons --------------------------------------------------------------
   Two roles. Radius, weight and colour all come from tokens. */

/* Inline SVG carries a few px of baseline gap, which grows the icon-only
   call button past its 44px tap floor (measured: 46px). */
.btn svg {
  display: block;
}

.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 {
  /* Accent as a FILL only (brief §2.4). Chore-yellow-on-parchment is
     1.85:1 and clears no non-text floor (WCAG 1.4.11 needs 3:1), so the
     control takes an ink border, same as its own label colour. */
  background-color: var(--color-accent);
  color: var(--color-accent-ink);
  border-color: var(--color-ink);
}

.btn--quiet {
  /* Outline button: the border is the whole control, so it takes the 3:1
     boundary token — the estimate/quote ask, outlined, beside the call. */
  border-color: var(--color-border-strong);
  color: var(--color-ink);
  background-color: transparent;
}

/* --- Hero — "the stamp lands" (brief §2.8, §4, §5) --------------------------
   DOM order: eyebrow, h1, primary CTA, field (ticket), lead, facts. Spacing
   below is brief §3.2's own first-screen target stack, not house scale
   steps — see tokens.css for why each is its own literal. */

.hero {
  padding-block-start: var(--hero-pad-top);
  padding-block-end: calc(var(--hero-pad-bottom) + var(--section-gap));
  /* The ticket system must overflow visibly (brief §2.8) — no ancestor
     between .ticket-wrap and the scrolling container may clip it. */
  overflow: visible;
}

.hero__container {
  overflow: visible;
}

.hero__h1 {
  font-family: var(--font-display);
  font-size: var(--text-h1);
  font-weight: var(--font-weight-display);
  line-height: var(--leading-tight);
  color: var(--color-ink);
  margin-block-start: var(--hero-gap-sm);
  margin-block-end: 0;
}

.hero__cta {
  margin-block-start: var(--hero-gap-lg);
  margin-block-end: 0;
}

.hero__lead {
  max-width: var(--layout-measure);
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
  color: var(--color-ink);
  margin-block-start: var(--hero-gap-lg);
  margin-block-end: 0;
}

.hero__facts {
  max-width: var(--layout-measure);
  font-size: var(--text-small);
  line-height: var(--leading-snug);
  color: var(--color-ink-muted);
  margin-block-start: var(--hero-gap-md);
  margin-block-end: 0;
}

/* --- The ticket-and-satellite system (brief §2.8) --------------------------
   Ticket width = the hero column exactly (100%, border-box). Height fixed
   170px at every breakpoint. Rotation fixed -1.2deg at every breakpoint
   (the rotation's added bounding width never approaches the smallest
   container padding). Overflow visible on .hero, .ticket-wrap, .ticket. */

.ticket-wrap {
  position: relative;
  margin-block: var(--ticket-clearance-top) var(--ticket-clearance-bottom);
  overflow: visible;
}

.ticket {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  height: var(--ticket-height);
  overflow: visible;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  transform: rotate(var(--ticket-rotation));
  /* Ticket + 3 ledger rows fade/settle in, 0-0.6s (brief §5 step 1) — a
     one-shot entrance; the hero is never reveal-gated (house-tech-spec §7). */
  animation: ticket-settle var(--duration-ticket-settle) var(--ease-out) 1 both;
}

.ticket__bg {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.ticket__bg-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% 50%;
}

/* Element-qualified: base.css's ul[role='list'] reset is (0,1,1) and beats
   a class-only (0,1,0) regardless of order — it zeroed this padding, letting
   the city column run under the stamp. ul.ticket__rows ties and, loading
   after base.css, wins. */
ul.ticket__rows {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-2xs);
  margin: 0;
  padding-block: var(--space-sm);
  padding-inline-start: var(--space-md);
  padding-inline-end: calc(var(--stamp-d) + var(--space-lg));
  list-style: none;
}

.ticket__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block-end: var(--space-2xs);
  border-block-end: var(--border-hairline) var(--border-dashed) var(--color-border);
  font-size: var(--text-small);
  color: var(--color-ink);
}

.ticket__row:last-child {
  border-block-end: none;
  padding-block-end: 0;
}

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

.ticket__city {
  color: var(--color-ink-muted);
  white-space: nowrap;
}

/* The rubber-stamp mark. Settled diameter --stamp-d (tokens.css); the ink
   texture image sits under the CSS-drawn ring/text (brief §4 slot 2). */
.ticket__stamp {
  position: absolute;
  z-index: 2;
  inset-block-start: 50%;
  inset-inline-end: var(--space-md);
  width: var(--stamp-d);
  height: var(--stamp-d);
  margin-block-start: calc(var(--stamp-d) / -2);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  border: var(--border-thick) var(--border-style) var(--color-ink);
  /* Brief §2.8: opacity stays 0 through the whole oversized phase (0-38%)
     and only reaches 1 at 46%, by which point scale is already 0.92 — the
     oversized state is never rendered visibly. */
  opacity: 0;
  transform: scale(2.4) rotate(14deg);
  animation: stamp-lands var(--duration-stamp) var(--ease-linear) 1 both;
}

.ticket__stamp-texture {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0.55;
}

.ticket__stamp-text {
  position: relative;
  z-index: 1;
  font-family: var(--font-body);
  font-weight: var(--font-weight-body-strong);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-ink);
  text-align: center;
}

@keyframes ticket-settle {
  0% { opacity: 0; transform: translateY(var(--space-sm)) rotate(var(--ticket-rotation)); }
  100% { opacity: 1; transform: translateY(0) rotate(var(--ticket-rotation)); }
}

/* Brief §2.8/§5, exact percentages: invisible through 38%, lands (opacity 1,
   scale 0.92) at 46%, settles to scale(1) by 58%, holds through 100%. */
@keyframes stamp-lands {
  0%, 38%  { opacity: 0; transform: scale(2.4) rotate(14deg); }
  46%      { opacity: 1; transform: scale(0.92) rotate(var(--stamp-rest-angle)); }
  58%, 100% { opacity: 1; transform: scale(1) rotate(var(--stamp-rest-angle)); }
}

/* Satellites — brief §2.8 clearance geometry, §5 independent loops (within
   the 40px clearance either side). Neither shares a timing origin or
   duration with the other or with the stamp. */

.ticket__keytag,
.ticket__pencil {
  position: absolute;
  z-index: 3;
}

.ticket__keytag {
  inset-inline-end: var(--space-lg);
  inset-block-start: var(--keytag-offset-top);
  width: var(--keytag-w);
  height: auto;
  transform-origin: top center;
  animation: keytag-swing var(--duration-keytag-loop) var(--ease-in-out) infinite;
}

.ticket__pencil {
  inset-inline-start: var(--space-lg);
  inset-block-start: 100%;
  margin-block-start: var(--pencil-offset-bottom);
  width: var(--pencil-w);
  height: auto;
  transform-origin: left center;
  animation: pencil-swing var(--duration-pencil-loop) var(--ease-in-out) infinite;
  filter: drop-shadow(var(--shadow-sm)); /* cedar-on-parchment is low-contrast otherwise */
}

@keyframes keytag-swing {
  0%, 100% { transform: rotate(calc(var(--keytag-swing) * -1)); }
  50%      { transform: rotate(var(--keytag-swing)); }
}

@keyframes pencil-swing {
  0%, 100% { transform: rotate(calc(var(--pencil-swing) * -1)); }
  50%      { transform: rotate(var(--pencil-swing)); }
}

/* Reduced motion (house-tech-spec §8, brief §5): no exceptions. The still
   frame is the composed picture — ticket settled, stamp at rest, key-tag and
   pencil at their resting angles (a mid-swing rest, not a snap to vertical).
   base.css collapses the duration tokens; the named keyframes' end states
   still need pinning so the frame reads as settled rather than a flash. */
@media (prefers-reduced-motion: reduce) {
  .ticket {
    animation: none;
    opacity: 1;
    transform: rotate(var(--ticket-rotation));
  }

  .ticket__stamp {
    animation: none;
    opacity: 1;
    transform: scale(1) rotate(var(--stamp-rest-angle));
  }

  .ticket__keytag {
    animation: none;
    transform: rotate(var(--keytag-rest-angle));
  }

  .ticket__pencil {
    animation: none;
    transform: rotate(var(--pencil-rest-angle));
  }
}

/* --- The job log (`#services`) ---------------------------------------------
   A rule-separated ledger list, one row per job, second column naming a
   served city (brief §3.1 row 2). No ordinal marks — the process section
   below is the page's only true numbered sequence (design-rules §5). */

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

.ledger {
  margin-block-start: var(--space-lg);
  padding: 0;
  list-style: none;
}

.ledger__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-sm);
  border-block-end: var(--border-hairline) var(--border-dashed) var(--color-border);
}

.ledger__row:first-child {
  border-block-start: var(--border-hairline) var(--border-dashed) var(--color-border);
}

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

.ledger__city {
  flex: 0 0 auto;
  font-size: var(--text-small);
  color: var(--color-ink-muted);
  white-space: nowrap;
}

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

.ledger__closing a {
  font-weight: var(--font-weight-body-strong);
  color: var(--color-ink);
}

/* --- Trust strip (`#trust`): a rule-separated single proof row. --- */

.trust__row {
  padding-block: var(--space-md);
  border-block: var(--border-hairline) var(--border-dashed) var(--color-border);
  color: var(--color-ink);
}

/* --- Form -------------------------------------------------------------------
   brief §2.3: --color-border-strong is a CONTROL's own boundary (WCAG
   1.4.11), and a form field is exactly that — overriding the skeleton
   default, which reused the decorative-only --color-border. */

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

.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 {
  display: block;
  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-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);
}

/* --- How a job gets logged (`#process`) -------------------------------------
   The page's only true numbered sequence (design-rules §5's nested-counters
   check). Small ticket-stub marks, not a repeat of the hero's living stamp. */

.process {
  counter-reset: process-step;
  margin-block-start: var(--space-lg);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-lg);
}

.process__step {
  counter-increment: process-step;
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
}

/* The mark IS the counter's badge (brief §4 slot 5's tally texture sits
   under the number) — square, not round, distinct from the hero's stamp. */
.process__mark {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  width: var(--layout-tap-min);
  height: var(--layout-tap-min);
  border-radius: var(--radius-sm);
  border: var(--border-hairline) var(--border-style) var(--color-border-strong);
  background-color: var(--color-surface);
}

.process__mark::after {
  content: counter(process-step);
  position: relative;
  z-index: 1;
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  color: var(--color-ink);
}

.process__mark img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.35;
}

.process__text {
  flex: 1 1 auto;
  align-self: center;
  margin: 0;
  color: var(--color-ink);
}

/* --- Reviews (`#reviews`): verbatim Google reviews, displayed-name only. --- */

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

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

.reviews__quote {
  margin: 0;
  color: var(--color-ink);
}

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

/* --- FAQ (`#faq`) --- */

.faq {
  margin-block-start: var(--space-lg);
  display: grid;
  gap: var(--space-lg);
}

.faq__item {
  padding-block-end: var(--space-md);
  border-block-end: var(--border-hairline) var(--border-dashed) var(--color-border);
}

.faq__item:last-child {
  border-block-end: none;
  padding-block-end: 0;
}

.faq__question {
  font-family: var(--font-display);
  font-size: var(--text-lead);
  font-weight: var(--font-weight-display);
  color: var(--color-ink);
  margin: 0 0 var(--space-2xs);
}

.faq__answer {
  margin: 0;
  color: var(--color-ink);
}

/* --- Footer --- */

.site-footer {
  padding-block: var(--space-xl);
  border-block-start: var(--border-hairline) var(--border-style) var(--color-border);
  font-size: var(--text-small);
  color: var(--color-ink);
}

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

.site-footer__nap {
  font-style: normal;
}

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