/* ==========================================================================
   HW Tree Service LLC - Stylesheet
   Mobile-first. Every value comes from css/variables.css.
   Written for phones first: most tree-service searches happen on a phone,
   outdoors, with the homeowner standing in the yard looking at the tree.
   ========================================================================== */

/* ==========================================================================
   1. RESET + BASE
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* BUG 3 fix: anchor targets (/services/#faq, #view-clearing) were landing
   underneath the sticky header. Every id target clears it by its height
   plus breathing room. */
[id] {
  scroll-margin-top: 96px;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd {
  margin: 0;
}

ul, ol {
  padding: 0;
  list-style: none;
}

img, svg, video {
  display: block;
  max-width: 100%;
}

/* Base size for every sprite icon. Without this an <svg><use> with no width
   falls back to its intrinsic size, which renders enormous. Contexts that
   want a different size override it; this is the floor so a new icon can
   never be dropped in at 300px by accident. */
.icon {
  width: 1em;
  height: 1em;
  flex: none;
}

img {
  height: auto;
}

a {
  color: inherit;
}

button {
  font: inherit;
  color: inherit;
}

/* Headings ---------------------------------------------------------------- */

h1, h2, h3, .display {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: var(--lh-head);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
  text-wrap: balance;
}

h1 {
  font-size: var(--t-h1);
}

h2 {
  font-size: var(--t-h2);
}

h3 {
  font-size: var(--t-h3);
  letter-spacing: 0.01em;
}

h4 {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: var(--t-h4);
  line-height: 1.25;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

p {
  text-wrap: pretty;
}

.lede {
  font-size: var(--t-body-lg);
  color: var(--ink-soft);
  max-width: var(--measure);
}

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

strong, b {
  font-weight: 600;
}

/* ==========================================================================
   2. LAYOUT PRIMITIVES
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap--narrow {
  max-width: var(--wrap-narrow);
}

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

.section--sunk {
  background: var(--surface-sunk);
}

.stack > * + * {
  margin-top: var(--s-4);
}

.stack-lg > * + * {
  margin-top: var(--s-6);
}

.section-head {
  max-width: 46rem;
  margin-bottom: var(--s-7);
}

.section-head p {
  margin-top: var(--s-4);
  color: var(--ink-soft);
  font-size: var(--t-body-lg);
  max-width: var(--measure);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   3. FOCUS + SKIP LINK
   Visible focus on every interactive element. Never removed.
   ========================================================================== */

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--r);
}

.skip-link {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  z-index: var(--z-skip);
  padding: var(--s-3) var(--s-5);
  background: var(--accent);
  color: var(--surface);
  font-family: var(--font-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-decoration: none;
  border-radius: var(--r);
  transform: translateY(calc(-100% - var(--s-4)));
  transition: transform var(--dur) var(--ease);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ==========================================================================
   4. BUTTONS
   Two intents on this site and exactly two labels, used everywhere:
     .btn--call  -> "Call (706) 968-8951"
     .btn--quote -> "Request a quote"
   Labels never wrap: white-space is pinned and the phone label is short.
   Contrast verified in both themes (see variables.css header).
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 0.9rem 1.6rem;
  min-height: 48px; /* touch target */
  border: var(--border) solid transparent;
  border-radius: var(--r);
  font-family: var(--font-label);
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}

.btn:active {
  transform: translateY(1px);
}

.btn .icon {
  width: 1.1em;
  height: 1.1em;
  flex: none;
}

/* Primary: solid accent red, light text. Verified 5.1:1. */
.btn--call {
  background: var(--accent);
  color: #fdfdfd;
  border-color: var(--accent);
}

.btn--call:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

@media (prefers-color-scheme: dark) {
  /* On dark the accent is a lightened red, so the label must go dark to keep
     contrast. Verified 7.0:1. */
  .btn--call {
    color: #141013;
  }
}

/* Text/SMS: a peer of the call button, visually distinct from the outlined
   secondary so a visitor reads it as a second real way to reach a human. */
.btn--text {
  background: var(--accent-tint);
  color: var(--accent);
  border-color: var(--accent);
}

.btn--text:hover {
  background: var(--accent);
  color: #fdfdfd;
  border-color: var(--accent);
}

@media (prefers-color-scheme: dark) {
  .btn--text:hover {
    color: #141013;
  }
}

/* On the dark surfaces it inverts to a light outline. */
.band .btn--text,
.hero .btn--text,
.feature .btn--text,
.urgent .btn--text,
.callbar .btn--text {
  background: transparent;
  color: var(--band-ink);
  border-color: rgba(244, 242, 242, 0.55);
}

.band .btn--text:hover,
.hero .btn--text:hover,
.feature .btn--text:hover,
.urgent .btn--text:hover {
  background: rgba(244, 242, 242, 0.12);
  border-color: var(--band-ink);
  color: var(--band-ink);
}

/* The call bar sits on --paper, not a dark band, so it keeps the tinted look. */
.callbar .btn--text {
  background: var(--accent-tint);
  color: var(--accent);
  border-color: var(--accent);
}

/* Secondary: outlined, inherits page ink. */
.btn--quote {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}

.btn--quote:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* All three dark surfaces (CTA band, video hero, feature band) re-skin their
   buttons. These sit on a dark background in BOTH themes, so the colors are
   fixed rather than tokenised per theme.

   The feature band was originally left out, which put a dark accent button on
   a dark background at roughly 2:1 as a component boundary. WCAG 1.4.11 wants
   3:1 for that, and it read as muddy besides. */
.band .btn--call,
.hero .btn--call,
.feature .btn--call,
.urgent .btn--call {
  background: var(--band-ink);
  color: #191416;
  border-color: var(--band-ink);
}

.band .btn--call:hover,
.hero .btn--call:hover,
.feature .btn--call:hover,
.urgent .btn--call:hover {
  background: #ffffff;
  border-color: #ffffff;
}

.band .btn--quote,
.hero .btn--quote,
.feature .btn--quote,
.urgent .btn--quote {
  color: var(--band-ink);
  border-color: rgba(244, 242, 242, 0.42);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.band .btn--quote:hover,
.hero .btn--quote:hover,
.feature .btn--quote:hover,
.urgent .btn--quote:hover {
  border-color: var(--band-ink);
  color: var(--band-ink);
  background: rgba(244, 242, 242, 0.10);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

/* Text link with a rule under it. */
.link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--accent);
  border-bottom: 2px solid currentColor;
  padding-bottom: 2px;
  transition: color var(--dur) var(--ease);
}

.link .icon {
  transition: transform var(--dur) var(--ease);
}

/* Compositor-only nudge. The previous version animated `gap`, which is a
   layout property and reflows on every frame of the hover. */
.link:hover {
  color: var(--accent-deep);
}

.link:hover .icon {
  transform: translateX(4px);
}

/* ==========================================================================
   5. HEADER + NAVIGATION
   Single line at desktop, 72px tall. Phone number is always visible,
   even on the smallest phone, because this customer base calls.
   ========================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  -webkit-backdrop-filter: saturate(150%) blur(10px);
  backdrop-filter: saturate(150%) blur(10px);
  border-bottom: var(--border) solid var(--line);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .header {
    background: var(--paper);
  }
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  height: 64px;
}

@media (min-width: 1024px) {
  .header__inner {
    height: 72px;
  }
}

/* Wordmark ---------------------------------------------------------------- */

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  flex: none;
}

/* Real logo emblem: a raster PNG (HW's actual artwork, no vector source
   exists), not a monochrome icon, so it carries its own color and does not
   take `color`. Native ratio is 214x176; height is fixed and width follows
   so it never looks squeezed.

   TWO VARIANTS, swapped by a <picture> media query, because the artwork is
   near-black plus his brand red. On the dark theme's near-black header the
   original measured about 1.1:1 against the background, i.e. invisible. The
   -light variant recolors the black to --band-ink cream and the red to the
   dark-theme --accent, so the two-tone identity survives. If the emblem is
   ever re-exported, regenerate BOTH variants.

   The <picture> wrapper must not become a flex item with its own sizing, or
   the mark's height stops matching the wordmark.

   The source artwork carries a full-height vertical rule down its left edge
   plus a band of empty padding. Cropping by alpha bounding box keeps that rule
   (it is opaque, so it counts as content) and it rendered as a stray line
   floating left of the mark. Both variants are now generated by first erasing
   any column that is opaque top-to-bottom, THEN taking the bbox. If the emblem
   is re-exported, regenerate both the same way and re-check the ratio below. */
.brand picture {
  display: flex;
  flex: none;
}

.brand__mark {
  height: 42px;
  width: auto;
  aspect-ratio: 115 / 152;
  flex: none;
  object-fit: contain;
}

.brand__name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  line-height: 1;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  color: var(--ink);
}

.brand__name span {
  color: var(--accent);
}

/* Desktop nav ------------------------------------------------------------- */

.nav {
  display: none;
}

@media (min-width: 900px) {
  .nav {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2.2vw, 2rem);
  }
}

.nav a {
  position: relative;
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-soft);
  white-space: nowrap;
  padding-block: var(--s-2);
  transition: color var(--dur) var(--ease);
}

/* Underline grows from the left on hover, stays full on the current page.
   Transform-only, so it never reflows. */
.nav a::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
}

@media (prefers-reduced-motion: no-preference) {
  .nav a::after {
    transition: transform var(--dur) var(--ease);
  }
}

.nav a:hover {
  color: var(--ink);
}

.nav a:hover::after,
.nav a[aria-current='page']::after {
  transform: scaleX(1);
}

.nav a[aria-current='page'] {
  color: var(--ink);
}

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

/* The header call button collapses to an icon-only tap target on phones so
   the wordmark never gets crushed. Accessible name stays on the element. */
.header .btn--call {
  padding-inline: 1rem;
}

.header__phone-text {
  display: none;
}

@media (min-width: 480px) {
  .header__phone-text {
    display: inline;
  }
}

@media (min-width: 900px) {
  .header .btn--call {
    padding-inline: 1.4rem;
  }
}

/* Mobile menu toggle ------------------------------------------------------ */

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  background: transparent;
  border: var(--border) solid var(--line-strong);
  border-radius: var(--r);
  cursor: pointer;
  color: var(--ink);
}

@media (min-width: 900px) {
  .nav-toggle {
    display: none;
  }
}

.nav-toggle .icon {
  width: 22px;
  height: 22px;
}

.nav-toggle__close {
  display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__open {
  display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__close {
  display: block;
}

/* Mobile drawer ----------------------------------------------------------- */

.mobile-nav {
  display: none;
  border-top: var(--border) solid var(--line);
  background: var(--surface);
}

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

@media (min-width: 900px) {
  .mobile-nav[data-open='true'] {
    display: none;
  }
}

.mobile-nav ul {
  padding-block: var(--s-3);
}

.mobile-nav a {
  display: block;
  padding: var(--s-4) 0;
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  border-bottom: var(--border) solid var(--line);
}

.mobile-nav li:last-child a {
  border-bottom: 0;
}

.mobile-nav a[aria-current='page'] {
  color: var(--accent);
}

/* Secondary group. Two things a visitor needs that are not top-level pages:
   storm damage is the highest-intent service on the site, and the FAQ answers
   the price and insurance questions before a homeowner has to ask them. Both
   were previously reachable only by scrolling the services page. Quieter than
   the four page links so the primary nav still reads as the nav.
   Deliberately NOT repeating call/text here: the fixed call bar already holds
   both in the thumb zone at every mobile width, including while this drawer
   is open. */
.mobile-nav__more {
  border-top: var(--border) solid var(--line);
}

.mobile-nav__more a {
  font-size: 1rem;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.mobile-nav__more a:hover {
  color: var(--ink);
}

/* ==========================================================================
   6. IMAGE SLOTS
   Every photo on this site is a named slot. Until HW's own job photos are
   dropped into images/optimized/, the slot renders as a designed brand panel
   rather than a broken-image icon. See notes.md for the drop-in manifest.
   ========================================================================== */

.shot {
  position: relative;
  overflow: hidden;
  border-radius: var(--r);
  /* Plain neutral behind a loading photo. The decorative fill below is for
     EMPTY slots only: it used to apply always, which was harmless behind an
     opaque photo but showed through anywhere an image is semi-transparent. */
  background: var(--surface-sunk);
  isolation: isolate;
}

.shot[data-empty='true'] {
  background-color: var(--accent-tint);
  background-image:
    repeating-linear-gradient(
      135deg,
      transparent 0 14px,
      color-mix(in srgb, var(--accent) 7%, transparent) 14px 15px
    );
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .shot[data-empty='true'] {
    background-image: none;
  }
}

.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: relative;
  z-index: var(--z-base);
}

/* Watermark shown behind the image, only ever seen if the file is absent.
   The stroke color is baked into the data URI, so light and dark each need
   their own copy. A single light-green mark would vanish on the dark tint.

   Scoped to [data-empty] only. It used to render unconditionally, which was
   invisible behind an opaque photo but bled through the feature band, where
   the image is deliberately semi-transparent under a scrim. */
.shot[data-empty='true']::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none' stroke='%232f5d3a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' opacity='0.30'%3E%3Cpath d='M32 6 19 26h7L14 44h12l-3 14h22l-3-14h12L38 26h7z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: clamp(56px, 16%, 104px);
  pointer-events: none;
  z-index: 0;
}

@media (prefers-color-scheme: dark) {
  .shot[data-empty='true']::after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none' stroke='%237fb98a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' opacity='0.34'%3E%3Cpath d='M32 6 19 26h7L14 44h12l-3 14h22l-3-14h12L38 26h7z'/%3E%3C/svg%3E");
  }
}

/* When JS confirms the file is missing it hides the empty img box so the
   panel reads as intentional. */
.shot[data-empty='true'] img {
  opacity: 0;
}

/* Preview-only hint: shows the exact filename the slot expects. Never
   rendered on a production build, only when the preview note is present. */
.shot__hint {
  display: none;
}

html[data-preview='true'] .shot[data-empty='true'] .shot__hint {
  display: block;
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 2;
  padding: 0.4rem 0.6rem;
  background: rgba(20, 22, 26, 0.82);
  color: #fdfdfd;
  font-family: var(--font-body);
  font-size: 0.6875rem;
  line-height: 1.35;
  letter-spacing: 0.02em;
  text-align: center;
}

/* Aspect variants --------------------------------------------------------- */

/* Capped so the hero still fits the first screen on a short laptop. Without
   the cap, a 4:5 portrait in a wide column grows tall enough to push the
   CTAs below the fold at 1440x800. */
.shot--hero {
  aspect-ratio: 4 / 5;
  max-height: 62dvh;
}

@media (min-width: 900px) {
  .shot--hero {
    max-height: 68dvh;
  }
}

.shot--wide {
  aspect-ratio: 16 / 10;
}

.shot--square {
  aspect-ratio: 1 / 1;
}

.shot--tall {
  aspect-ratio: 3 / 4;
}

/* ==========================================================================
   7. HERO  (layout family: full-bleed video with left-aligned content)

   The video is decoration behind a scrim. Text contrast is guaranteed by the
   scrim, never by the footage, so a bright frame can never wash out the
   headline. Deliberately left-aligned rather than centered.

   The hero is always dark regardless of the page theme. That is not a
   mid-page theme flip: it is one full-bleed media section, the same treatment
   the CTA band uses, and it is consistent across every viewport.
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  min-height: min(92dvh, 880px);
  padding-top: clamp(2rem, 1rem + 4vw, 4rem);
  padding-bottom: clamp(2.5rem, 1.5rem + 5vw, 5rem);
  overflow: hidden;
  background: var(--band);
  color: var(--band-ink);
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.hero__media img,
.hero__media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The poster sits under the video so there is never a flash of empty box
   while the video buffers, and it is all a reduced-motion visitor ever sees. */
.hero__poster {
  position: absolute;
  inset: 0;
}

.hero__video {
  position: relative;
  opacity: 0;
  transition: opacity 900ms var(--ease);
}

/* Only revealed once the browser reports it can actually play through, so a
   half-buffered video never stutters into view over the poster. */
.hero__video[data-ready='true'] {
  opacity: 1;
}

/* Two-layer scrim: a left-weighted wedge that carries headline contrast, and
   a bottom lift so the CTAs stay readable over bright water. */
.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      100deg,
      rgba(9, 17, 11, 0.96) 0%,
      rgba(9, 17, 11, 0.93) 34%,
      rgba(9, 17, 11, 0.74) 62%,
      rgba(9, 17, 11, 0.50) 100%
    ),
    linear-gradient(to top, rgba(9, 17, 11, 0.62) 0%, rgba(9, 17, 11, 0) 42%);
}

@media (max-width: 899px) {
  /* On a phone the copy sits over the middle of the frame, so the scrim goes
     flat and heavier rather than left-weighted. */
  .hero__media::after {
    background:
      linear-gradient(
        to bottom,
        rgba(9, 17, 11, 0.86) 0%,
        rgba(9, 17, 11, 0.78) 45%,
        rgba(9, 17, 11, 0.88) 100%
      );
  }
}

.hero__inner {
  position: relative;
  z-index: 1;
  width: 100%;
}

.hero__copy {
  max-width: 62rem;
}

.hero__copy h1 {
  font-size: var(--t-display);
  line-height: var(--lh-tight);
  color: var(--band-ink);
}

.hero__copy h1 .accent {
  display: block;
  color: var(--accent-hero);
}

.hero__copy .lede {
  margin-top: var(--s-5);
  max-width: 36ch;
  color: var(--band-ink-soft);
}

.hero__copy .btn-row {
  margin-top: var(--s-6);
}

/* ==========================================================================
   8. TRUST STRIP  (layout family: inline rule-separated row)
   No cards, no icons-in-circles. Just three true facts on hairlines.
   ========================================================================== */

.trust {
  border-bottom: var(--border) solid var(--line);
  background: var(--surface);
}

.trust__row {
  display: grid;
  gap: 0;
}

@media (min-width: 860px) {
  .trust__row {
    grid-template-columns: repeat(4, 1fr);
  }
}

.trust__item {
  padding-block: var(--s-5);
  border-bottom: var(--border) solid var(--line);
}

.trust__item:last-child {
  border-bottom: 0;
}

@media (min-width: 860px) {
  .trust__item {
    padding-block: var(--s-6);
    padding-inline: var(--s-5);
    border-bottom: 0;
    border-left: var(--border) solid var(--line);
  }

  .trust__item:first-child {
    padding-left: 0;
    border-left: 0;
  }

  .trust__item:last-child {
    padding-right: 0;
  }
}

.trust__value {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);
  line-height: 1.05;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--ink);
}

.trust__value .icon {
  width: 1em;
  height: 1em;
  color: var(--accent);
  flex: none;
}

.trust__label {
  margin-top: var(--s-2);
  color: var(--ink-soft);
  font-size: var(--t-small);
  line-height: 1.5;
}

/* ==========================================================================
   8b. URGENT STRIP  (layout family: full-bleed accent band, split)
   Sits between the hero and the trust bar. A storm visitor is the highest
   intent traffic on the site and previously had to reach fold 7, or open the
   contact page, before anything spoke to their situation.
   ========================================================================== */

.urgent {
  background: var(--band);
  color: var(--band-ink);
  border-bottom: var(--border) solid var(--band-line);
}

.urgent__inner {
  display: grid;
  gap: var(--s-5);
  padding-block: clamp(1.75rem, 1rem + 2.5vw, 2.75rem);
}

@media (min-width: 900px) {
  .urgent__inner {
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--s-7);
  }
}

.urgent h2 {
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem);
  color: var(--band-ink);
}

.urgent p {
  margin-top: var(--s-3);
  color: var(--band-ink-soft);
  font-size: var(--t-body);
  max-width: 52ch;
}

/* ==========================================================================
   9. SERVICES  (layout family: asymmetric grid, exact cell count)
   Four real services, four cells. One lead cell, three supporting.
   ========================================================================== */

.services__grid {
  display: grid;
  gap: var(--s-5);
}

@media (min-width: 760px) {
  .services__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1080px) {
  .services__grid {
    grid-template-columns: 1.3fr 1fr 1fr;
    grid-template-rows: auto auto;
  }

  /* Lead service spans two rows in the first column. */
  .service:first-child {
    grid-row: span 2;
  }

  /* Fourth service spans the two right columns on the second row. */
  .service:nth-child(4) {
    grid-column: span 2;
  }
}

.service {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: var(--border) solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  /* Hover lift uses `translate`, not `transform`: the scroll drift animation
     owns `transform` with a fill, and an animation outranks a transition on
     the same property. The two individual channels compose instead. */
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              translate var(--dur) var(--ease);
}

.service:hover,
.service:focus-within {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-lift);
  translate: 0 -4px;
}

.service .shot {
  border-radius: 0;
  flex: none;
}

.service:first-child .shot {
  aspect-ratio: 4 / 3;
}

@media (min-width: 1080px) {
  .service:first-child .shot {
    aspect-ratio: 3 / 4;
  }

  .service:nth-child(4) .shot {
    aspect-ratio: 21 / 9;
  }
}

.service__body {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-5);
  flex: 1 1 auto;
}

@media (min-width: 1080px) {
  .service__body {
    padding: var(--s-6) var(--s-5);
  }
}

.service h3 {
  color: var(--ink);
}

.service p {
  color: var(--ink-soft);
  font-size: var(--t-small);
  line-height: 1.6;
}

.service .link {
  margin-top: auto;
  align-self: flex-start;
  font-size: 0.9375rem;
}

/* ==========================================================================
   10. FEATURE BAND  (layout family: full-bleed image with overlay panel)
   The view-clearing moment. Only place on the page an image carries text.
   ========================================================================== */

.feature {
  position: relative;
  background: var(--band);
  color: var(--band-ink);
}

.feature__media {
  position: absolute;
  inset: 0;
}

.feature__media .shot {
  width: 100%;
  height: 100%;
  border-radius: 0;
}

/* Fully opaque. The scrim above does all the darkening, which keeps the photo
   readable as a photograph. Fading the image itself instead just greys it out
   and lets whatever is behind it bleed through. */
.feature__media .shot img {
  opacity: 1;
}

/* Scrim guarantees text contrast no matter how bright the photo is. */
.feature__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to right,
    rgba(16, 30, 20, 0.94) 0%,
    rgba(16, 30, 20, 0.86) 46%,
    rgba(16, 30, 20, 0.62) 100%
  );
}

@media (max-width: 899px) {
  /* The left-weighted wedge only works while the copy is constrained. On
     desktop .feature__panel caps at 40rem inside a 1200px wrap, so the text
     stays in the 0.94 to 0.86 region and measured 9.95:1. On a phone the panel
     is full width, so line ends ran out into the 0.62 region over the bright
     cut logs and measured 2.81:1 against the 4.5:1 requirement. Flat and
     heavier here, exactly as the hero already does. */
  .feature__media::after {
    background: linear-gradient(
      to bottom,
      rgba(16, 30, 20, 0.90) 0%,
      rgba(16, 30, 20, 0.86) 50%,
      rgba(16, 30, 20, 0.92) 100%
    );
  }
}

.feature__inner {
  position: relative;
  z-index: var(--z-base);
  padding-block: clamp(3.5rem, 2rem + 7vw, 8rem);
}

.feature__panel {
  max-width: 40rem;
}

.feature h2 {
  color: var(--band-ink);
}

.feature p {
  margin-top: var(--s-5);
  color: var(--band-ink-soft);
  font-size: var(--t-body-lg);
  max-width: 44ch;
}

.feature .btn-row {
  margin-top: var(--s-6);
}

/* ==========================================================================
   11. TESTIMONIAL  (layout family: centered quote)
   One real Google review. Centered is allowed here: the words are the design.
   ========================================================================== */

.quote {
  position: relative;
  text-align: center;
}

/* Oversized typographic opening quote behind the testimonial. Real
   typography in the display face, not clip art, and it sits behind the
   stars in the section's own tint so it reads as texture, not content. */
.quote::before {
  content: '\201C';
  position: absolute;
  top: -0.28em;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(8rem, 6rem + 9vw, 15rem);
  line-height: 1;
  color: var(--accent);
  opacity: 0.10;
  pointer-events: none;
  z-index: 0;
}

.quote > * {
  position: relative;
  z-index: 1;
}

.quote__stars {
  display: flex;
  justify-content: center;
  gap: 0.2rem;
  color: var(--accent);
  margin-bottom: var(--s-5);
}

.quote__stars .icon {
  width: 22px;
  height: 22px;
}

.quote blockquote {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: clamp(1.3rem, 1.05rem + 1.15vw, 2rem);
  line-height: 1.42;
  color: var(--ink);
  max-width: 26ch;
  margin-inline: auto;
  text-wrap: balance;
}

@media (min-width: 720px) {
  .quote blockquote {
    max-width: 34ch;
  }
}

.quote figcaption {
  margin-top: var(--s-6);
  font-family: var(--font-label);
  font-weight: 700;
  font-size: var(--t-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-soft);
}

.quote figcaption .name {
  display: block;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  color: var(--ink);
  margin-bottom: var(--s-1);
}

/* Outbound link to the real Google listing. Only appears under a quoted
   review; it is the honest version of the "see all reviews" pattern while
   the business has two reviews rather than two hundred. */
.quote__more {
  margin-top: var(--s-6);
}

/* ==========================================================================
   12. SERVICE AREA  (layout family: two-column definition list)
   ========================================================================== */

.area__grid {
  display: grid;
  gap: var(--s-7);
}

@media (min-width: 900px) {
  .area__grid {
    grid-template-columns: 0.9fr 1.1fr;
    gap: clamp(2.5rem, 5vw, 5rem);
    align-items: start;
  }
}

/* Exactly two columns at every size: there are four confirmed service areas,
   so a 2x2 grid fills with no orphan cell. Adding a fifth area means
   revisiting this, not pasting a blank. */
.area__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 var(--s-5);
  border-top: var(--border) solid var(--line);
}

.area__list li {
  padding-block: var(--s-4);
  padding-right: var(--s-3);
  border-bottom: var(--border) solid var(--line);
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink);
}

.area__note {
  margin-top: var(--s-5);
  color: var(--ink-soft);
  font-size: var(--t-small);
}

/* Regions replaced the old four-item .area__list when the owner confirmed a
   two hour radius (2026-07-30). That radius reaches Asheville, Greenville, and
   metro Atlanta, so a flat list of towns would be unreadable and a page per
   town would be doorway spam. Three grouped regions instead: scannable, and
   the town names still carry real local keyword surface.
   Single column stacked; the parent .area__grid already provides the split. */
.area__regions {
  display: grid;
  gap: var(--s-6);
  border-top: var(--border) solid var(--line);
  padding-top: var(--s-6);
}

.area__regions h3 {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink);
}

.area__regions p {
  margin-top: var(--s-3);
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* ==========================================================================
   13. PROCESS  (layout family: vertical stack, verb-led)
   No "Step 1 / Step 2" labels. The verb is the label.
   ========================================================================== */

.process {
  border-top: var(--border) solid var(--line);
}

.process__item {
  display: grid;
  gap: var(--s-2);
  padding-block: var(--s-6);
  border-bottom: var(--border) solid var(--line);
}

@media (min-width: 760px) {
  .process__item {
    grid-template-columns: minmax(12rem, 20rem) 1fr;
    gap: var(--s-6);
    align-items: baseline;
  }
}

.process__item h3 {
  color: var(--ink);
  font-size: clamp(1.375rem, 1.15rem + 1vw, 1.875rem);
}

.process__item p {
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* ==========================================================================
   14. CTA BAND  (layout family: full-bleed solid band)
   ========================================================================== */

.band {
  background: var(--band);
  color: var(--band-ink);
}

.band__inner {
  display: grid;
  gap: var(--s-6);
  padding-block: clamp(3rem, 2rem + 5vw, 5.5rem);
}

@media (min-width: 900px) {
  .band__inner {
    grid-template-columns: 1.2fr auto;
    align-items: center;
    gap: var(--s-8);
  }
}

.band h2 {
  color: var(--band-ink);
}

.band p {
  margin-top: var(--s-4);
  color: var(--band-ink-soft);
  font-size: var(--t-body-lg);
  max-width: 46ch;
}


/* ==========================================================================
   14b. CREDENTIAL PANEL  (layout family: centred credential panel)
   Used once, on the About page, for the insurance proof. Centred is the right
   call here specifically because the coverage figure IS the message: there is
   nothing to put beside it that would not weaken it.
   ========================================================================== */

/* Tinted rather than a bordered white card. On --paper the panel sat on
   --surface behind a --line-strong border that measured only 1.88:1 against
   the page, so the card barely separated from it and the whole "this is a
   credential" read was lost. The green tint does the separating, and the
   green rule on top reads as a seal instead of a generic box. */
.credential {
  text-align: center;
  padding: clamp(2.25rem, 1.5rem + 3.5vw, 3.75rem) var(--gutter);
  border-top: 3px solid var(--accent);
  border-radius: var(--r);
  background: var(--accent-tint);
}

/* margin-inline:auto, not text-align. The base reset sets svg to display:block,
   so the parent's text-align:center does not move it. */
.credential__mark {
  width: 2.5rem;
  height: 2.5rem;
  margin-inline: auto;
  color: var(--accent);
}

.credential h2 {
  margin-top: var(--s-4);
}

/* The figure is the whole point of the panel, so it carries display weight
   rather than body weight. Tabular figures keep the comma grouping from
   looking loose. */
.credential__figure {
  margin-top: var(--s-6);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 2rem + 7vw, 5.5rem);
  line-height: 0.95;
  letter-spacing: var(--tracking-display);
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

.credential__kind {
  margin-top: var(--s-3);
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}

.credential__body {
  margin-inline: auto;
  margin-top: var(--s-5);
  max-width: 52ch;
  color: var(--ink-soft);
}

.credential .btn-row {
  margin-top: var(--s-6);
  justify-content: center;
}

/* ==========================================================================
   15. FOOTER
   ========================================================================== */

.footer {
  background: var(--surface);
  border-top: var(--border) solid var(--line);
  padding-block: var(--s-8) var(--s-6);
}

.footer__grid {
  display: grid;
  gap: var(--s-7);
}

@media (min-width: 720px) {
  .footer__grid {
    grid-template-columns: 1.4fr 1fr 1fr;
    gap: var(--s-6);
  }
}

/* Footer column headings are h3, not h4: they follow an h2 in the document,
   and skipping a level breaks the heading outline for screen reader users.
   The label styling is re-applied here so they still read as small captions. */
.footer h3 {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: var(--t-label);
  line-height: 1.25;
  letter-spacing: var(--tracking-label);
  color: var(--ink);
  margin-bottom: var(--s-4);
}

.footer__links li + li {
  margin-top: var(--s-3);
}

.footer__links a {
  color: var(--ink-soft);
  font-size: var(--t-small);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}

.footer__links a:hover {
  color: var(--accent);
}

.footer__about p {
  margin-top: var(--s-4);
  color: var(--ink-soft);
  font-size: var(--t-small);
  max-width: 34ch;
}

.footer__phone {
  display: inline-block;
  margin-top: var(--s-4);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.75rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--accent);
  text-decoration: none;
}

.footer__phone:hover {
  color: var(--accent-deep);
}

.footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-5);
  align-items: center;
  justify-content: space-between;
  margin-top: var(--s-8);
  padding-top: var(--s-5);
  border-top: var(--border) solid var(--line);
  color: var(--ink-faint);
  font-size: 0.875rem;
}

.footer__base a {
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer__base a:hover {
  color: var(--accent);
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
}

/* ==========================================================================
   15b. MOBILE CALL BAR
   Fixed tap-to-call in the thumb zone, phones only. This is the standard
   converter for local service sites whose customers call rather than email:
   the header call button is at the top of a scrolled page, this one is under
   the thumb at all times. Desktop never sees it.

   Uses the same label as every other call CTA on the site (one label per
   intent), and the whole bar is one link, so the tap target is the full bar.
   ========================================================================== */

.callbar {
  display: none;
}

@media (max-width: 899px) {
  .callbar {
    display: block;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--z-nav);
    padding: 0.625rem var(--gutter)
             calc(0.625rem + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--paper) 92%, transparent);
    -webkit-backdrop-filter: saturate(150%) blur(10px);
    backdrop-filter: saturate(150%) blur(10px);
    border-top: var(--border) solid var(--line);
  }

  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .callbar {
      background: var(--paper);
    }
  }

  /* Two-up: Call and Text side by side. Texting a photo is often the lower
     friction option for a homeowner standing under the tree, and unlike a
     call it never lands in voicemail. Both live in the thumb zone. */
  .callbar__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-2);
  }

  .callbar .btn {
    width: 100%;
    padding-inline: 0.75rem;
    font-size: 0.9375rem;
    letter-spacing: 0.03em;
  }

  /* Clear the bar so footer content is never hidden behind it. */
  body {
    padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
  }

  /* While the preview disclaimer is on the page it stacks ABOVE the call
     bar: the thumb zone belongs to the phone number, not the banner. Both
     clear the page bottom together. Removed automatically at go-live when
     the banner goes. */
  html[data-preview='true'] #dgb-preview-note {
    bottom: calc(70px + env(safe-area-inset-bottom, 0px));
  }

  html[data-preview='true'] body {
    padding-bottom: calc(150px + env(safe-area-inset-bottom, 0px));
  }
}

@media print {
  .callbar {
    display: none !important;
  }
}

/* ==========================================================================
   15c. PREMIUM LAYER
   Cross-page transitions, FAQ open animation, header scroll shadow,
   selection color. Every piece is progressive: unsupported browsers get the
   exact site that existed before this section.
   ========================================================================== */

/* Cross-document view transitions: pages cross-fade instead of hard-cutting,
   and the header is named so it holds still while content changes beneath
   it. No JS, no fallback needed: unsupported browsers navigate normally. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  .header {
    view-transition-name: site-header;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 240ms;
  }
}

/* FAQ height animates open where the primitives exist (interpolate-size,
   Chrome 129+). Everywhere else the accordion snaps exactly as before. */
@media (prefers-reduced-motion: no-preference) {
  .faq__item::details-content {
    block-size: 0;
    overflow: clip;
    transition: block-size var(--dur) var(--ease),
                content-visibility var(--dur) allow-discrete;
  }

  .faq__item[open]::details-content {
    block-size: auto;
  }

  @supports (interpolate-size: allow-keywords) {
    .faq {
      interpolate-size: allow-keywords;
    }
  }
}

/* The sticky header grows a shadow once the page scrolls, scrubbed by a
   scroll timeline. Decorative only: without support the border alone keeps
   the boundary, which is the pre-existing design. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    .header {
      animation: header-depth linear both;
      animation-timeline: scroll();
      animation-range: 0 120px;
    }

    @keyframes header-depth {
      from { box-shadow: 0 0 0 rgba(30, 20, 22, 0); }
      to   { box-shadow: 0 8px 24px rgba(30, 20, 22, 0.10); }
    }
  }
}

/* Selection in the brand green. Small, and everyone notices it. */
::selection {
  background: var(--accent);
  color: var(--surface);
}

/* ==========================================================================
   16. FORMS
   Label above input. Helper below label. Error below input.
   No placeholder-as-label anywhere.
   Contrast verified: label 9.3:1, helper 5.6:1, border 3.1:1 against surface.
   ========================================================================== */

.form {
  display: grid;
  gap: var(--s-5);
}

.field {
  display: grid;
  gap: var(--s-2);
}

.field label {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 0.9375rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink);
}

.field .req {
  color: var(--accent);
}

.field .help {
  font-size: var(--t-small);
  line-height: 1.45;
  color: var(--ink-soft);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 0.85rem 0.95rem;
  min-height: 48px;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.4;
  color: var(--ink);
  background: var(--surface);
  border: var(--border) solid var(--field-border);
  border-radius: var(--r);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

.field textarea {
  min-height: 140px;
  resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder {
  color: var(--ink-faint);
  opacity: 1;
}

.field input:hover,
.field select:hover,
.field textarea:hover {
  border-color: var(--ink-faint);
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--accent);
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}

.field .error {
  font-size: var(--t-small);
  font-weight: 600;
  color: #a01d1d;
}

@media (prefers-color-scheme: dark) {
  .field .error {
    color: #ff9a9a;
  }
}

.field[data-invalid='true'] input,
.field[data-invalid='true'] select,
.field[data-invalid='true'] textarea {
  border-color: #a01d1d;
}

@media (prefers-color-scheme: dark) {
  .field[data-invalid='true'] input,
  .field[data-invalid='true'] select,
  .field[data-invalid='true'] textarea {
    border-color: #ff9a9a;
  }
}

/* Form status messages: success, error, and the inert fallback. */
.form__status {
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r);
  border: var(--border) solid var(--line-strong);
  background: var(--accent-tint);
  color: var(--ink);
  font-size: var(--t-small);
  line-height: 1.55;
}

.form__status[hidden] {
  display: none;
}

.form__status a {
  color: var(--accent);
  font-weight: 600;
}

/* Honeypot: hidden from people, visible to bots. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ==========================================================================
   16b. FAQ  (native details/summary, zero JS)
   The open/closed mark reuses the sprite's X glyph: rotated 45deg it reads
   as a plus, and it rotates back to an X when the item is open. No new
   icon needed and the affordance is the standard accordion pattern.
   ========================================================================== */

.faq {
  border-top: var(--border) solid var(--line);
}

.faq__item {
  border-bottom: var(--border) solid var(--line);
}

.faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-5);
  min-height: 48px;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.125rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.faq__item summary::-webkit-details-marker {
  display: none;
}

.faq__mark {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  color: var(--accent);
  transform: rotate(45deg); /* X glyph shown as a plus while closed */
}

.faq__item[open] .faq__mark {
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  .faq__mark {
    transition: transform var(--dur) var(--ease);
  }
}

.faq__body {
  padding-bottom: var(--s-5);
}

.faq__body p {
  color: var(--ink-soft);
  max-width: var(--measure);
}

.faq__body a {
  color: var(--accent);
  font-weight: 600;
  text-underline-offset: 3px;
}

/* ==========================================================================
   17. PAGE HEADER + PROSE (interior + legal pages)
   ========================================================================== */

.page-head {
  padding-block: clamp(2.5rem, 1.5rem + 5vw, 5rem) clamp(2rem, 1.5rem + 3vw, 3.5rem);
  border-bottom: var(--border) solid var(--line);
  background: var(--surface);
}

.page-head h1 {
  max-width: 20ch;
}

.page-head .lede {
  margin-top: var(--s-5);
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: center;
  margin-bottom: var(--s-5);
  font-family: var(--font-label);
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.breadcrumb a {
  color: var(--ink-soft);
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--accent);
}

/* Uses a text color, not a border color. --line-strong is tuned for hairlines
   and renders the separator at 1.8:1, which looks like a display artifact
   rather than a character. --ink-faint clears AA at 4.8:1. */
.breadcrumb .sep {
  color: var(--ink-faint);
}

.prose {
  max-width: 72ch;
}

.prose > * + * {
  margin-top: var(--s-5);
}

.prose h2 {
  margin-top: var(--s-8);
  font-size: var(--t-h3);
}

.prose h3 {
  margin-top: var(--s-7);
  font-size: var(--t-h4);
  font-family: var(--font-label);
  letter-spacing: 0.03em;
}

.prose p,
.prose li {
  color: var(--ink-soft);
}

.prose ul {
  padding-left: var(--s-5);
  list-style: disc;
}

.prose ul li {
  margin-top: var(--s-3);
  padding-left: var(--s-1);
}

.prose ul li::marker {
  color: var(--accent);
}

.prose a {
  color: var(--accent);
  font-weight: 600;
  text-underline-offset: 3px;
}

.prose .updated {
  font-size: var(--t-small);
  color: var(--ink-faint);
}

/* Reviewer flag shown on legal pages until the client signs off. */
.review-flag {
  padding: var(--s-4) var(--s-5);
  margin-bottom: var(--s-6);
  border: var(--border) solid var(--line-strong);
  border-left: 4px solid var(--accent);
  border-radius: var(--r);
  background: var(--accent-tint);
  color: var(--ink);
  font-size: var(--t-small);
  line-height: 1.55;
}

/* ==========================================================================
   18. SERVICE DETAIL (services.html)
   Alternating split, capped at two consecutive before the rhythm breaks.
   ========================================================================== */

.detail {
  display: grid;
  gap: var(--s-6);
  padding-block: var(--section-y);
  border-bottom: var(--border) solid var(--line);
}

@media (min-width: 900px) {
  .detail {
    grid-template-columns: 1fr 1fr;
    gap: clamp(2.5rem, 5vw, 4.5rem);
    align-items: center;
  }

  .detail--flip .detail__media {
    order: -1;
  }
}

.detail h2 {
  font-size: var(--t-h2);
}

.detail p {
  margin-top: var(--s-4);
  color: var(--ink-soft);
  font-size: var(--t-body-lg);
  max-width: var(--measure);
}

.detail__points {
  margin-top: var(--s-5);
  border-top: var(--border) solid var(--line);
}

/* No bullet marker and no tick icon. A green check on every line is the
   single most recognisable "generated by a machine" visual tell, and a trade
   contractor does not need to reassure you that each line is a real thing it
   does. The hairline rows carry the structure on their own. */
.detail__points li {
  padding-block: var(--s-3);
  border-bottom: var(--border) solid var(--line);
  color: var(--ink-soft);
  font-size: var(--t-small);
  line-height: 1.55;
}

.detail .btn-row {
  margin-top: var(--s-6);
}

/* ==========================================================================
   19. ABOUT VALUES  (layout family: numbered-free value stack)
   ========================================================================== */

.values {
  display: grid;
  gap: var(--s-5);
}

@media (min-width: 760px) {
  .values {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-6);
  }
}

.value {
  padding-top: var(--s-5);
  border-top: 3px solid var(--accent);
}

.value h3 {
  font-size: var(--t-h4);
  font-family: var(--font-label);
  letter-spacing: 0.03em;
  color: var(--ink);
}

.value p {
  margin-top: var(--s-3);
  color: var(--ink-soft);
  font-size: var(--t-small);
  line-height: 1.6;
}

/* ==========================================================================
   20. CONTACT LAYOUT
   ========================================================================== */

.contact__grid {
  display: grid;
  gap: var(--s-8);
}

@media (min-width: 900px) {
  .contact__grid {
    grid-template-columns: 1fr 1.1fr;
    gap: clamp(3rem, 5vw, 5rem);
    align-items: start;
  }
}

.contact__call {
  padding: var(--s-6);
  background: var(--accent-tint);
  border: var(--border) solid var(--line);
  border-radius: var(--r);
}

.contact__number {
  display: inline-block;
  margin-top: var(--s-3);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.25rem, 1.6rem + 3vw, 3.25rem);
  line-height: 1;
  letter-spacing: 0.005em;
  color: var(--accent);
  text-decoration: none;
}

.contact__number:hover {
  color: var(--accent-deep);
}

.contact__meta {
  margin-top: var(--s-6);
  border-top: var(--border) solid var(--line);
}

.contact__meta div {
  padding-block: var(--s-4);
  border-bottom: var(--border) solid var(--line);
}

.contact__meta dt {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: var(--t-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.contact__meta dd {
  margin-top: var(--s-2);
  color: var(--ink);
  font-size: var(--t-small);
  line-height: 1.55;
}

/* ==========================================================================
   21. MOTION
   MOTION_INTENSITY 4. One effect: sections fade up as they scroll in.
   Justification: it establishes reading order down a long marketing page.

   This is a CSS scroll-driven animation, deliberately NOT JavaScript.
   Content visibility must never depend on a script running. With an
   IntersectionObserver, any failure to run (blocked script, JS error, or a
   page that loads in a background tab where the observer gets no rendering
   ticks) leaves every section stuck at opacity 0 with nothing alive to
   reveal it. That failure mode does not exist here:

     - Browser supports animation-timeline  -> sections fade up on scroll.
     - Browser does not support it          -> @supports never applies,
                                               nothing is ever hidden.
     - prefers-reduced-motion: reduce       -> @media never applies,
                                               nothing is ever hidden.

   Nothing hides content by default. The animation only ever adds motion to
   content that is already there.

   The animation-range also handles deep links for free: an element scrolled
   past on load sits beyond its range, and "both" fill leaves it at the final
   visible keyframe. Landing on services.html#storm-cleanup shows the section.
   ========================================================================== */

/* MOTION_INTENSITY 7. Two mechanisms, split by one hard rule learned the
   hard way on this build: NOTHING BELOW THE FOLD MAY DEPEND ON AN ANIMATION
   TO BECOME READABLE.

   1. LOAD ANIMATIONS, hero only. Time-based, so they run on the document
      timeline and always complete, whether or not the visitor scrolls and
      whether or not the tab is focused. Worst case the hero copy is briefly
      transparent and then resolves by itself. If CSS animations are
      unavailable the rule simply never applies and the copy renders normally.

   2. SCROLL EFFECTS, everything else. TRANSFORM ONLY, never opacity. An
      unresolved scroll timeline therefore leaves content fully visible and
      merely un-drifted. This is precisely what the earlier scroll-reveal got
      wrong: it animated opacity from 0, so a timeline that never resolved
      left whole sections invisible.

   Both are inside prefers-reduced-motion: no-preference, so a visitor who
   asks for reduced motion gets a completely static page. */

@media (prefers-reduced-motion: no-preference) {

  /* --- 1. Hero entrance (time-based, self-completing) --- */

  .hero__copy > * {
    animation: hero-rise 0.9s var(--ease) both;
  }

  .hero__copy > *:nth-child(1) { animation-delay: 0.10s; }
  .hero__copy > *:nth-child(2) { animation-delay: 0.22s; }
  .hero__copy > *:nth-child(3) { animation-delay: 0.34s; }

  @keyframes hero-rise {
    from {
      opacity: 0;
      transform: translate3d(0, 26px, 0);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  /* Settle the frame in behind the copy. Transform only. */
  .hero__media {
    animation: hero-settle 1.6s var(--ease) both;
  }

  @keyframes hero-settle {
    from { transform: scale(1.07); }
    to   { transform: none; }
  }

  /* --- 2. Scroll drift on editorial imagery (transform only) --- */

  @supports (animation-timeline: view()) {
    .feature__media .shot img,
    .detail__media .shot img {
      animation: drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      will-change: transform;
    }

    /* Held at scale so the drift can never expose an edge of the frame. */
    @keyframes drift {
      from { transform: scale(1.12) translate3d(0, -2.2%, 0); }
      to   { transform: scale(1.12) translate3d(0, 2.2%, 0); }
    }
  }

  /* --- 2b. Scroll drift on cards and rows (transform only, visible at
     rest, so an unresolved timeline just means no drift) --- */

  @supports (animation-timeline: view()) {
    .service,
    .value,
    .process__item,
    .faq__item {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }

    @keyframes rise-in {
      from { transform: translate3d(0, 26px, 0); }
      to   { transform: none; }
    }
  }

  /* --- 2c. Call bar enters from below once, on load. Time-based, so it
     always completes. --- */

  .callbar {
    animation: callbar-up 0.55s var(--ease) 0.25s both;
  }

  @keyframes callbar-up {
    from { transform: translateY(110%); }
    to   { transform: none; }
  }

  /* --- 3. Hover feedback on the service cards --- */

  .service .shot img {
    transition: transform 750ms var(--ease);
  }

  .service:hover .shot img,
  .service:focus-within .shot img {
    transform: scale(1.055);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   22. PRINT
   ========================================================================== */

@media print {
  .header,
  .mobile-nav,
  .band,
  .skip-link,
  .nav-toggle,
  #dgb-preview-note {
    display: none !important;
  }

  body {
    background: #fff;
    color: #000;
    font-size: 12pt;
  }

  a[href^='http']::after {
    content: ' (' attr(href) ')';
    font-size: 0.85em;
  }
}
