/* ==========================================================================
   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 two escape hatches are
   the house breakpoints (48em, 64em) and an explicit `@allow-literal: reason`
   comment on the preceding line.

   Archetype: split-hero (brief §2). Family: bold-industrial. Signature move:
   "the meter reads the rate" (brief §8) — a settling gauge needle, three
   independently-timed coping-edge rivets, a travelling waterline glint.
   ========================================================================== */

/* Hidden sprite sheet for the settled gauge-needle-and-numeral corner mark
   (brief §5, §8) — a class, not an inline `style`, because the site's own
   CSP `style-src` allow-lists one exact inline `<style>` hash and nothing
   else; an inline `style="display:none"` attribute is silently dropped
   under that policy, leaving the sprite's default replaced-element box
   (300x150, from the `svg{display:block}` rule below) in flow. */
.sprite {
  display: none;
}

/* --- 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 the site (design-rules §7.1). */
.section {
  padding-block: var(--section-gap);
}

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

h1 {
  font-size: var(--text-display);
  margin-block-end: var(--space-md);
}

h2 {
  font-size: var(--text-h2);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-block-end: var(--space-md);
}

/* The settled gauge-needle-and-numeral corner mark, reused at the head of
   every section below the hero (brief §5, §8) — fixed 24x24px, per the
   brief's own repeated-ornament minimum. Colour follows context: ink on the
   page's light ground, accent on the dark plate (scoped below). */
.section-mark {
  flex: none;
  width: var(--gauge-mark);
  height: var(--gauge-mark);
  color: var(--color-ink);
}

/* --- Header & navigation --------------------------------------------------
   Progressive enhancement: with JS off the nav list is a plain, always-visible
   list of links and the toggle stays [hidden]. With JS on, narrow viewports
   collapse the list behind the toggle.                                     */

.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);
  border: var(--border-hairline) var(--border-style) var(--color-border-strong);
  border-radius: var(--radius-none);
  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);
}

.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 ----------------------------------------------------------------
   Primary CTA fill is the validated navy/white pair (brief §4's own
   contrast-table row: --color-accent-ink on --color-ink, 8.80:1) — NOT
   --color-accent, which is reserved for the one place it clears AA as text
   (the gauge numeral on the dark panel) and is never a button fill (1.37:1
   on --color-bg). A third role, --btn--on-dark, is the dark-panel CTA. */

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

.btn--quiet {
  border-color: var(--color-border-strong);
  color: var(--color-ink);
}

/* The dark-plate secondary CTA (brief §6.2 row 2). CRITIC ROUND 1 FIX
   (lensA/palette): the previous rule filled this control with
   --color-accent, which brief §4 reserves for exactly one text use (the
   gauge numeral) and tokens.css's own comment says NEVER a button fill —
   an uncited colour choice, not a design decision the Builder may make.
   Outline treatment instead, matching .btn--quiet's role one step up the
   same scale: --color-on-dark for both border and label, 12.54:1 against
   the plate (well past the 3:1 control-boundary floor and full AA as
   text) — no accent anywhere in this rule. */
.btn--on-dark {
  border-color: var(--color-on-dark);
  color: var(--color-on-dark);
}

/* --- Hero -------------------------------------------------------------------
   Single column below 64rem, DOM order eyebrow/h1/CTA -> field -> lead/facts
   (design-rules §7.2 rule 1). At >=64rem a two-column grid: text ~45%, field
   ~55%, field placed visually right via grid-template-areas while staying
   AFTER the text block in DOM order.                                       */

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

.hero__rule {
  margin: 0;
  margin-block-end: var(--space-sm);
  border-top: var(--border-thick) var(--border-style) var(--color-ink);
  /* @allow-literal: a short fixed-width rule, not a reading measure */
  width: 3rem;
}

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

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

.cta-row {
  margin: 0;
}

.hero__lead {
  margin-block-start: var(--space-md);
  color: var(--color-ink-muted);
}

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

/* The living-hero field. NO text sits over it outside the dark gauge panel
   described below (brief §4). Ornaments are aria-hidden. */
.field {
  position: relative;
  margin-block-start: var(--space-md);
  margin-inline: calc(var(--layout-gutter) * -1);
  height: var(--field-height);
  overflow: hidden;
  background-color: var(--color-bg);
}

.field__media,
.field__img {
  display: block;
  width: 100%;
  height: 100%;
}

.field__img {
  object-fit: cover;
  object-position: 50% 20%;
}

/* Three coping-edge rivets, positioned to sit over the same three points the
   generated image already draws along the coping edge (brief §7's per-slot
   clause: "leave clear negative space ... for the CSS/SVG satellite overlay,
   added in code"). Independent, non-synchronised glint loops. */
.field__rivet {
  position: absolute;
  inset-block-start: 24%;
  /* @allow-literal: circular ornament diameter, scaled to the field's own
     coordinate space as a percentage — not a reading measure or type size */
  width: 3.4%;
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background-color: var(--color-accent);
  box-shadow: 0 0 var(--space-sm) var(--space-3xs) var(--color-accent);
  animation-name: rivet-glint;
  animation-timing-function: var(--ease-in-out);
  animation-iteration-count: infinite;
}

.field__rivet--1 {
  inset-inline-start: 14%;
  animation-duration: var(--duration-ambient-a);
  animation-delay: 0s;
}

.field__rivet--2 {
  inset-inline-start: 49%;
  transform: translateX(-50%);
  animation-duration: var(--duration-ambient-b);
  animation-delay: 1.6s;
}

.field__rivet--3 {
  inset-inline-end: 14%;
  animation-duration: var(--duration-ambient-a);
  animation-delay: 3.1s;
}

@keyframes rivet-glint {
  0%, 100% { opacity: 0.15; }
  50%      { opacity: 0.9; }
}

/* A soft sun-glint travelling the waterline band, on its own separately
   timed loop (brief §8, --duration-ambient-c). */
.field__glint {
  position: absolute;
  inset-block-start: 34%;
  inset-inline-start: -25%;
  width: 32%;
  height: 7%;
  background-color: var(--color-on-dark);
  opacity: 0.4;
  filter: blur(var(--blur-glint));
  animation-name: glint-travel;
  animation-duration: var(--duration-ambient-c);
  animation-timing-function: var(--ease-in-out);
  animation-iteration-count: infinite;
}

@keyframes glint-travel {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(420%); }
}

/* The gauge: a flat dark dial face drawn in code over the image's blank
   brass rim, with a needle that sweeps once on load and settles reading the
   weekly price (brief §8). Positioned over the image's own gauge motif. */
.field__gauge {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-end: 4%;
  width: 20%;
  aspect-ratio: 1;
  transform: translateX(-50%);
}

.field__gauge-dial {
  display: block;
  width: 100%;
  height: 100%;
}

.field__gauge-face {
  fill: var(--color-dark-panel);
}

.field__gauge-pin {
  fill: var(--color-accent);
}

.field__needle {
  stroke: var(--color-accent);
  stroke-width: 4;
  stroke-linecap: round;
  transform-box: view-box;
  transform-origin: 50% 50%;
  transform: rotate(-95deg);
  animation: needle-settle var(--duration-settle) var(--ease-out) var(--delay-settle) 1 forwards;
}

@keyframes needle-settle {
  from { transform: rotate(-95deg); }
  to   { transform: rotate(40deg); }
}

/* A small solid dark-panel chip behind the numeral, not the full inset box —
   the needle and pin must stay visible above it. Giving the numeral its OWN
   background (rather than relying on the SVG circle painted beneath it) is
   what makes its real 9.15:1 accent-on-dark-panel contrast (brief §4)
   readable by a contrast checker walking the CSS cascade, not only by eye. */
.field__gauge-figure {
  position: absolute;
  inset-inline: 15%;
  inset-block-end: 14%;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-dark-panel);
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-eyebrow);
  color: var(--color-accent);
}

/* Brief §7 slot 4 (quaternary "section divider") — a thin full-bleed strip
   bridging the dark plate back to the light body, decorative but carrying
   real alt text (copy.md §4). CRITIC ROUND 1 FIX (lensA/imagery). A fixed
   height, not the field's aspect-ratio: this is a texture strip, not a
   second living-hero, so it stays visibly minor. */
.divider {
  display: block;
  width: 100%;
  height: var(--divider-height);
  overflow: hidden;
}

/* CRITIC ROUND 2 FIX (lensA/imagery): 50% 35% never reached the source's
   water band at any box height this strip actually renders at (48-96px
   against a 1000x558 source scaled to cover a full-bleed width-driven box —
   the maths always landed in the coping/deck rows, 27-50% down the source).
   Bottom-anchored instead: for object-fit: cover, 100% vertical always
   shows the source's OWN bottom edge as the box's bottom edge, whatever the
   box height — which is exactly where the brief's water hint sits (the
   source's bottom ~10-12%), confirmed by re-screenshotting at 375/768/1440. */
.divider img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
}

/* --- The dark "shop-dark" plate: The Weekly Plan + What Your Weekly Visit
   Includes, one continuous instrument panel (brief §4, §8). Two sub-blocks
   inside a single .section, separated by --space-sm — strictly under half
   of --section-gap at every width, so the page's largest gap stays the
   section boundary either side of the plate (design-rules §7.1). --------- */

.plate {
  background-color: var(--color-dark-panel);
  color: var(--color-on-dark);
}

.plate h2 {
  color: var(--color-on-dark);
}

.plate .section-mark {
  color: var(--color-accent);
}

.plate p {
  color: var(--color-on-dark);
}

/* Dark-band-safe focus ring (house-tech-spec/base.css requirement whenever a
   dark band exists) — scoped to the plate only. */
.plate :focus-visible {
  outline-color: var(--color-focus-on-band);
}

.rate-figure {
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  font-size: var(--text-h2);
  color: var(--color-accent);
  line-height: var(--leading-tight);
}

#weekly-plan p.measure {
  margin-block-end: var(--space-md);
}

.visit-includes {
  margin-block-start: var(--space-sm);
}

/* Brief §7 slots 2 + 3, object studies on their own worklight-ivory ground,
   read like two stamped spec-sheet clippings inset into the dark plate —
   the same register the hero's gauge motif already uses (brass-on-navy).
   CRITIC ROUND 1 FIX (lensA/imagery). */
.visit-media {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-block-start: var(--space-md);
}

.visit-media__item {
  flex: 1 1 var(--visit-media-basis);
  max-width: var(--visit-media-basis);
  margin: 0;
  background-color: var(--color-bg);
  border: var(--border-hairline) var(--border-style) var(--color-on-dark-muted);
}

.visit-media__item img {
  width: 100%;
  height: auto;
}

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

.visit-list li {
  padding-block: var(--space-sm);
  border-top: var(--border-hairline) var(--border-style) var(--color-on-dark-muted);
}

.visit-list li:last-child {
  border-bottom: var(--border-hairline) var(--border-style) var(--color-on-dark-muted);
}

/* --- What Neighbors Say ----------------------------------------------------- */

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

.review {
  padding-block-start: var(--space-md);
  border-top: var(--border-hairline) var(--border-style) var(--color-border);
}

.review__text {
  font-size: var(--text-body);
  max-width: var(--layout-measure);
}

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

/* --- Form -------------------------------------------------------------------
   Renamed from the skeleton's `.field` to `.field-row`: this brief's living-
   hero container is already `.field`, and a form-row wrapper of the same
   name would collide with it. */

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

.field-row {
  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-none);
}

textarea.field__control {
  min-height: var(--space-3xl);
  resize: vertical;
}

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

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

/* --- Accessibility: the skip link and text selection are house primitives
   painted from --color-accent/--color-accent-ink in base.css, but THIS
   brief's --color-accent (light-blue) fails AA as a fill under white text
   (1.37:1) — the brief reserves it for text-on-dark-panel use only. Reuse
   the same validated navy/white pair the primary CTA uses instead. */

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

/* --- Breakpoints — two states, house-fixed (design-rules §5) --------------- */

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

  .review:first-child {
    grid-column: 1 / -1;
  }
}

@media (min-width: 64em) {
  .hero__grid {
    display: grid;
    grid-template-areas: 'head field' 'meta field';
    grid-template-columns: var(--hero-columns-lg);
    grid-template-rows: auto 1fr;
    align-items: start;
    gap: 0 var(--space-2xl);
  }

  .hero__head {
    grid-area: head;
  }

  .hero__meta {
    grid-area: meta;
  }

  .field {
    grid-area: field;
    margin: 0;
    height: auto;
  }

  #weekly-plan,
  .visit-includes {
    max-width: var(--layout-measure);
  }
}

/* --- Reduced motion (brief §8, house-tech-spec §8) — no exceptions. The
   duration tokens collapse to ~0 at the source (base.css), which already
   neutralises the ambient loops and the needle sweep's easing; these rules
   additionally freeze every animated property at its settled end-state, per
   the brief's own reduced-motion description: rivets rest at opacity .7, the
   glint holds a fixed offset, the needle is pre-set already reading $150. */

@media (prefers-reduced-motion: reduce) {
  .field__rivet {
    animation: none;
    opacity: 0.7;
  }

  .field__glint {
    animation: none;
    transform: translateX(180%);
  }

  .field__needle {
    animation: none;
    transform: rotate(40deg);
  }
}
