/* ==========================================================================
   site.css — Wet Kat Pool Service Southwest. retro-local family, split-hero
   archetype, the postmark-press living-hero signature move (brief §5/§6).
   Tokens only — no raw hex/px/font literals (house-tech-spec §3); the two
   escape hatches are the house breakpoints and an explicit @allow-literal
   comment. Brief's own 900px breakpoint = 56.25em at the house 16px root.
   ========================================================================== */

/* --- 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.              */

/* Fixed content height (brief §5.1: "56px content + 1px border-bottom",
   expiry bar 0-44 + header 44-101). min-height so the row resolves to
   --header-height regardless of the CTA button's own height. */
.site-header {
  position: relative;
  z-index: var(--z-header);
  padding-block: 0;
  min-height: var(--header-height);
  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);
  min-height: var(--header-height);
}

/* "Wet Kat" (brief §5.1's own 360px one-line arithmetic) — overflow-safe so
   a narrower viewport than the check never breaks the header row. */
.site-header__brand {
  overflow: hidden;
  min-width: 0;
  white-space: nowrap;
  text-overflow: ellipsis;
  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);
}

/* --color-accent-deep, NOT --color-accent (skeleton default) — brief §3:
   --color-accent is decorative/rule-work ONLY (2.97:1 vs bg, fails even the
   3:1 large-text floor there); --color-accent-deep is the text-bearing role,
   5.21:1 vs accent-ink on surface / 4.61:1 vs accent-ink on bg. */
.btn--primary {
  background-color: var(--color-accent-deep);
  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);
}

.skip-link {
  background-color: var(--color-accent-deep);
}

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

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

/* base.css zeroes every block margin (its reset); the footer's own blocks
   (the NAP address, the copyright line — the hours slot is removed
   entirely per brief §9/copy §1.9) need the rhythm back. */
.site-footer .container > * + * {
  margin-block-start: var(--space-sm);
}

/* ==========================================================================
   HERO — split-hero archetype (brief §2/§5/§6). House order at every width
   (design-rules §7.2 rule 1): eyebrow -> h1 -> primary CTA -> field -> lead
   -> facts. Below 900px this is DOM order in a single column; at >= 900px a
   grid re-places the field into its own 42% column beside a 58% text
   column carrying everything else, without reordering any of it.
   ========================================================================== */

.hero {
  padding-block-start: var(--space-sm);
  padding-block-end: var(--section-gap);
  background-color: var(--color-bg);
  overflow: hidden;
}

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

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

.hero h1 {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-hero);
  line-height: var(--leading-tight);
}

/* The H1's accent word — Yeseva One's own colour shift (brief §1: "set in
   the display face's own colour shift, not an italic swap" — the face has
   no italic cut). */
.hero__accent {
  color: var(--color-accent-deep);
}

.hero__cta {
  margin: var(--space-sm) 0 0;
}

/* --- The living-hero field (brief §6) --------------------------------------
   No text sits directly on the illustration (brief §3): the postmark/stamp
   devices carry their own --color-surface backing, and the caption sits
   AFTER the stack, in normal flow, never overlaid. The sky/pool/deck layer
   is NEVER cropped (object-fit: contain, brief §5.1) — the coping ring and
   waterline stripe, the §6.1 anchor, stay legible at any size. The
   atmosphere gradient is the field's own CSS background (brief §6, verbatim
   token values), showing through wherever the image doesn't reach. */
/* The outer <figure>: a flex column, so the caption below the visual stack
   resolves like any other block — it is never an absolutely-positioned
   sibling fighting the stack for the field's height. Stays within the
   container's own padding (the caption reads at the same inset as the rest
   of the hero text); the visual stack below is what full-bleeds. */
.hero__field {
  display: flex;
  flex-direction: column;
  margin-block-start: var(--space-sm);
}

/* The visual stack: fixed height, full-bleed edge-to-edge (brief §6), the
   atmosphere gradient as its own CSS background (verbatim token values),
   showing through wherever the contained image doesn't reach. */
.hero__field-visual {
  position: relative;
  overflow: hidden;
  height: var(--hero-field-height);
  margin-inline: calc(var(--layout-gutter) * -1);
  background:
    radial-gradient(110% 80% at 50% 0%, var(--color-atmosphere-pink), transparent 55%),
    radial-gradient(90% 60% at 15% 100%, var(--color-atmosphere-teal), transparent 55%),
    linear-gradient(180deg, var(--color-atmosphere-sky-1) 0%, var(--color-atmosphere-sky-2) 45%, var(--color-atmosphere-sky-3) 75%, var(--color-atmosphere-sky-4) 100%);
}

.hero__pool {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}

/* Foreground layer, independent sway loop (brief §6/§8). object-fit:
   contain so it scales rather than crops at the field's own minimum
   height (brief §5.1's "what yields" note — the palm is permitted to
   shrink before the pool cross-section is touched). */
.hero__palm {
  position: absolute;
  inset-inline-start: calc(50% - var(--hero-pool-w) * 0.46);
  inset-block-end: 0;
  width: calc(var(--hero-pool-w) * 0.2);
  max-height: 86%;
  object-fit: contain;
  object-position: bottom;
  transform-origin: 50% 100%;
  animation: palmSway var(--duration-palm) var(--ease-signature) infinite;
}

/* The sun-glint — a soft travelling highlight sweeping the field, ambient
   only, aria-hidden (empty, no accessible name). */
.hero__glint {
  position: absolute;
  inset-block-start: 10%;
  inset-inline-start: 10%;
  width: 16%;
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background-image: radial-gradient(circle, var(--color-atmosphere-sky-2), transparent 70%);
  opacity: 0;
  animation: sunGlint var(--duration-glint) var(--ease-linear) infinite var(--delay-glint);
}

/* The stamp corner device (top-left, brief §6/§7.1's "negative space at
   top-left and top-right for live text") — its own solid surface backing,
   never text directly on the illustration. */
.hero__stamp {
  position: absolute;
  inset-block-start: var(--space-sm);
  inset-inline-start: var(--space-sm);
  margin: 0;
  padding: var(--space-3xs) var(--space-2xs);
  background-color: var(--color-surface);
  border: var(--border-hairline) var(--border-style) var(--color-border-strong);
  transform: rotate(-4deg);
}

.hero__stamp-label {
  display: block;
  color: var(--color-ink-muted);
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* The postmark medallion — the one-shot signature press (brief §6/§8),
   top-right, its own solid surface backing and ink ring. */
.hero__postmark {
  position: absolute;
  inset-block-start: var(--space-sm);
  inset-inline-end: calc(50% - var(--hero-pool-w) / 2 + var(--space-sm));
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--postmark-hero-size);
  height: var(--postmark-hero-size);
  margin: 0;
  padding: var(--space-2xs);
  background-color: var(--color-surface);
  border: var(--border-thick) var(--border-style) var(--color-ink);
  border-radius: var(--radius-pill);
  text-align: center;
  animation: postmarkPress var(--duration-postmark) var(--ease-out) 1 forwards;
}

.hero__postmark-label {
  color: var(--color-ink);
  font-size: var(--text-small);
  font-weight: var(--font-weight-body-strong);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* The honesty caption (brief §6 item 5) — normal document flow, NOT
   overlaid on the illustration (brief §3). */
.hero__caption {
  margin: var(--space-2xs) 0 0;
  color: var(--color-ink-muted);
  font-size: var(--text-small);
}

@keyframes palmSway {
  0%, 100% { transform: rotate(calc(var(--motion-palm-tilt) * -1)); }
  50%      { transform: rotate(var(--motion-palm-tilt)); }
}

@keyframes sunGlint {
  0%   { transform: translateX(var(--motion-glint-start)); opacity: 0; }
  15%  { opacity: .5; }
  85%  { opacity: .5; }
  100% { transform: translateX(var(--motion-glint-end)); opacity: 0; }
}

@keyframes postmarkPress {
  0%   { transform: scale(var(--motion-postmark-start)); opacity: 0; }
  60%  { transform: scale(var(--motion-postmark-settle)); opacity: 1; }
  100% { transform: scale(var(--motion-postmark-end)); opacity: 1; }
}

/* Reduced motion (house-tech-spec §8, absolute, no exception — brief §6/§8).
   tokens.css's kill switch already collapses --duration-palm/-glint/
   -postmark to ~0.01ms at :root; these explicit end-states are the belt to
   that brace, matching the brief's own reduced-motion block exactly. */
@media (prefers-reduced-motion: reduce) {
  .hero__palm,
  .hero__glint,
  .hero__postmark {
    animation: none;
  }
  .hero__palm {
    transform: rotate(0deg);
  }
  .hero__glint {
    opacity: 0;
  }
  .hero__postmark {
    opacity: 1;
    transform: scale(1);
  }
}

.hero__lead {
  margin: 0;
  font-family: var(--font-body);
  font-style: italic;
  font-size: var(--text-lead);
  line-height: var(--leading-body);
  max-width: var(--layout-measure);
}

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

@media (min-width: 56.25em) {
  .hero__grid {
    grid-template-columns: var(--hero-text-basis) var(--hero-visual-basis);
    grid-template-areas: "head field" "meta field";
    align-items: start;
    gap: var(--layout-gutter);
  }

  .hero__head {
    grid-area: head;
  }

  .hero__field {
    grid-area: field;
    margin-block-start: 0;
    align-self: stretch;
  }

  .hero__field-visual {
    flex: 1;
    height: auto;
    min-height: var(--hero-field-min-band);
    margin-inline: 0;
  }

  .hero__palm { inset-inline-start: 4%; width: 20%; }
  .hero__postmark { inset-inline-end: var(--space-sm); }

  .hero__meta {
    grid-area: meta;
  }
}

/* ==========================================================================
   THE STATIC POSTMARK REUSE MARK (brief §6: "a flattened, non-animated copy
   of the postmark ring... recurs as a small corner mark opening" the three
   content sections below the hero. Fixed 28px diameter, never scaling with
   viewport, no animation of any kind — this is NOT a second spectacular
   moment (design-rules §0's "exactly one" rule).
   ========================================================================== */

.section-mark {
  display: block;
  width: var(--postmark-mark-size);
  height: var(--postmark-mark-size);
  margin-block-end: var(--space-sm);
  border: var(--border-thick) var(--border-style) var(--color-ink);
  border-radius: var(--radius-pill);
}

/* ==========================================================================
   WHAT A WEEKLY VISIT INCLUDES — brief §5 row 3, §6.3.
   ========================================================================== */

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

.visit-list {
  display: grid;
  gap: var(--space-2xs);
}

.visit-list li {
  padding-inline-start: var(--space-md);
  position: relative;
}

.visit-list li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: var(--visit-dot-offset);
  width: var(--space-2xs);
  height: var(--space-2xs);
  background-color: var(--color-accent-deep);
  border-radius: var(--radius-pill);
}

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

.phone-repeat {
  margin-block-start: var(--space-md);
  font-weight: var(--font-weight-body-strong);
}

@media (min-width: 40em) {
  .visit-grid {
    grid-template-columns: 3fr 2fr;
    align-items: start;
  }
}

/* ==========================================================================
   SERVICES — brief §5 row 4, §6.5. Three labels, no icon set (design-rules
   §4's three-card-grid default is not licensed here by a generic benefit
   set — each card's copy is the dossier's own confirmed phrase, split).
   ========================================================================== */

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

.service {
  padding: var(--space-md);
  background-color: var(--color-surface);
  border-radius: var(--radius-sm);
}

.service__title {
  margin: 0 0 var(--space-2xs);
  font-size: var(--text-h3);
}

.service p {
  margin: 0;
}

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

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

/* ==========================================================================
   DIVIDER STRIP — imagery slot 4 (brief §7): decorative, no text overlay,
   full-bleed, separates Services from the season strip. 96px = --space-2xl,
   so no new token is needed.
   ========================================================================== */

.divider-strip {
  height: var(--space-2xl);
  overflow: hidden;
}

.divider-strip picture {
  display: block;
  height: 100%;
}

.divider-strip img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ==========================================================================
   POSTCARDS FROM EVERY SEASON — brief §5 row 5, §6.6.
   ========================================================================== */

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

.season {
  padding: var(--space-md);
  background-color: var(--color-surface);
  border-radius: var(--radius-sm);
}

.season__title {
  margin: 0 0 var(--space-2xs);
  font-size: var(--text-h3);
}

.season p {
  margin: 0;
}

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

/* ==========================================================================
   FAQ — brief §5 row 7, §6.7.
   ========================================================================== */

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

.faq-list h3 {
  margin: 0 0 var(--space-2xs);
  font-family: var(--font-body);
  font-size: var(--text-h3);
  font-weight: var(--font-weight-body-strong);
}

.faq-list p {
  margin: 0;
  max-width: var(--layout-measure);
}
