/* Torres-Boren Roofing — main stylesheet
 *
 * EXACT_MIGRATION. Every value derives from assets/css/tokens.css, which was
 * measured from the live source. Nothing here is a design decision; where a
 * number appears inline it is a measured source value with a comment saying so.
 */

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-primary);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  color: var(--color-text);
  background: var(--color-surface-page);
}
img, picture, video { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4, p, ul, ol, figure, address { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; }
address { font-style: normal; }

/* ---------- accessibility floor ---------- */
:focus-visible {
  outline: 3px solid var(--color-green);
  outline-offset: 2px;
  /* white halo so the ring stays visible against the dark green header, where a
     green outline alone would disappear */
  box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.95);
}
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--color-green);
  color: var(--color-text-inverse);
  padding: .75rem 1.25rem;
  font-weight: var(--fw-bold);
}
.skip-link:focus { left: 0; }

/* ---------- container discipline (4.1b) ---------- */
.section { padding-inline: var(--gutter); }
.section > .inner,
.inner {
  max-width: var(--container);
  margin-inline: auto;
  width: 100%;
}
.section--wide > .inner { max-width: var(--container-wide); }
.section--bleed { padding-inline: 0; }
.section--bleed > .inner { padding-inline: var(--gutter); }
.prose { max-width: var(--measure); }

/* ---------- typography ---------- */
h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: var(--fw-bold); }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: var(--fw-bold); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: var(--fw-bold); }
.accent {
  font-family: var(--font-accent);
  font-weight: var(--fw-bold);
  font-size: var(--fs-accent-xl);
  line-height: 1;
  display: inline-block;
}
.accent--sm { font-size: var(--fs-accent-lg); }
.eyebrow {
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: var(--lh-h3);
  text-transform: uppercase;
}

/* ---------- buttons (square, no radius — source has zero everywhere) ---------- */
.btn {
  display: inline-block;
  font-family: var(--font-primary);
  font-size: var(--fs-button);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  text-decoration: none;
  padding: var(--btn-padding);
  border-radius: var(--radius);
  border: 0;
  cursor: pointer;
  transition: var(--transition-button);
}
.btn--primary { background: var(--color-green); color: var(--color-text-inverse); }
.btn--primary:hover { background: var(--color-surface); color: var(--color-green-alt); }
.btn--ghost {
  background: var(--color-surface);
  color: var(--color-green);
  border: 2px solid var(--color-green);
}
.btn--ghost:hover { background: var(--color-green); color: var(--color-text-inverse); }

/* ---------- utility bar ---------- */
.utility-bar {
  background: var(--color-surface-dark);
  color: var(--color-text-inverse);
  padding-inline: var(--gutter);
  font-size: 13px; /* measured */
}
.utility-list {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem 2rem;
  align-items: center;
  justify-content: space-between;
  padding-block: .6rem;
}
.utility-list li { display: flex; align-items: center; gap: .6rem; }
.utility-list img { width: 34px; height: 34px; flex: none; }
.utility-text { line-height: 1.3; }
.utility-bar a { color: var(--color-text-inverse); text-decoration: none; }
.utility-bar a:hover { text-decoration: underline; }

/* ---------- header / nav (floats over the hero, as the source does) ---------- */
.site-header {
  position: absolute;
  inset-inline: 0;
  z-index: 20;
  padding-inline: var(--gutter);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 1.25rem;
}
.site-logo img { width: 180px; height: auto; }
.site-nav ul { display: flex; gap: 1.5rem; align-items: center; }
.site-nav a {
  color: var(--color-text-inverse);
  text-decoration: none;
  font-size: var(--fs-nav);
  line-height: var(--lh-nav);
  font-weight: var(--fw-regular);
}
.site-nav a:hover { color: var(--color-text-inverse); text-decoration: underline; text-underline-offset: 6px; }
/* The source marks no active page at all. An indicator is an accessibility
   improvement, so it stays — but drawn in the header's own white rather than
   an accent colour the brand does not use in the nav. */
.site-nav a[aria-current="page"] {
  color: var(--color-text-inverse);
  border-bottom: 2px solid var(--color-text-inverse);
  padding-bottom: 2px;
}

.nav-toggle {
  display: none;
  background: transparent;
  border: 2px solid var(--color-text-inverse);
  color: var(--color-text-inverse);
  font-family: var(--font-primary);
  font-size: 14px;
  padding: .5rem .8rem;
  gap: .5rem;
  align-items: center;
  cursor: pointer;
}
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block; width: 20px; height: 2px; background: currentColor; position: relative;
}
.nav-toggle__bars::before, .nav-toggle__bars::after { content: ""; position: absolute; }
.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after { top: 6px; }

/* ---------- hero ---------- */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  padding-block: var(--space-hero-block);
  padding-inline: var(--gutter);
  background-size: cover;
  background-position: center;
  color: var(--color-text-inverse);
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--overlay-hero);
}
.hero > .inner { position: relative; }
.hero h1 {
  /* min() keeps the measured desktop size and only shrinks on very narrow
     viewports, so the source scale is preserved but cannot overflow. */
  font-size: min(var(--fs-h1-hero), 14vw);
  line-height: var(--lh-h1-hero);
  color: var(--color-text-inverse);
  max-width: 16ch;
}
.hero__sub {
  font-size: min(var(--fs-h3), 5vw);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  margin-top: 1rem;
}

/* ---------- generic content sections ---------- */
.section-block { padding-block: 4rem; background: var(--color-surface); }
.section-block--alt { background: var(--color-surface-alt); }
.section-block h2 { margin-bottom: 1rem; }
.section-block p + p { margin-top: 1rem; }

.split {
  display: grid;
  gap: 2.5rem;
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--60-40 { grid-template-columns: 3fr 2fr; }
}

/* ---------- estimate CTA strip ---------- */
.estimate-cta {
  background: var(--color-surface);
  padding-block: 2rem;
  padding-inline: var(--gutter);
  text-align: center;
}
.estimate-cta p {
  font-size: 24px; /* measured */
  font-weight: var(--fw-regular);
  color: var(--color-text-strong);
  margin-bottom: 1.25rem;
}

/* ---------- process steps ---------- */
.steps { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 768px) { .steps { grid-template-columns: repeat(4, 1fr); } }
.step h2 { font-size: var(--fs-h2); }
.step p { margin-top: .5rem; }

/* ---------- gallery ---------- */
.gallery-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 640px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1025px) { .gallery-grid { grid-template-columns: repeat(4, 1fr); } }
.gallery-grid img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* ---------- form (underline-only inputs, as the source has) ---------- */
.form-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 768px) { .form-grid--2 { grid-template-columns: 1fr 1fr; } }
.field label {
  display: block;
  font-size: var(--fs-input);
  margin-bottom: .35rem;
  color: var(--color-text-strong);
}
.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: var(--font-primary);
  font-size: var(--fs-input);
  color: var(--color-text-strong);
  background: transparent;
  /* border-WIDTH, not the border shorthand: `border: 0 0 1px 0` is not valid
     shorthand syntax, so it was dropped and the fields rendered as full boxes
     instead of the source's underline-only inputs. */
  border-style: solid;
  border-width: var(--input-border);
  border-color: var(--input-border-color);
  border-radius: var(--radius);
  padding: var(--input-padding);
  min-height: 44px; /* touch target — source used 30px */
}
.field textarea { min-height: 120px; padding-block: .5rem; }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline-offset: 0; }
.form-status { margin-top: 1rem; font-weight: var(--fw-bold); }
.form-status[data-state="error"] { color: #B3261E; }
.form-status[data-state="ok"] { color: var(--color-green); }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- CTA band ---------- */
/* Pre-footer CTA band — same copy, made eye-catching WITHOUT yellow, which the
 * client says is not part of the branding. Emphasis comes from scale and heavy
 * green rules against the source's own alternating band grey, not a new hue.
 * #023E1D on #DFDFDF measures 9.22:1 (AAA). */
.cta-band {
  background: var(--color-surface-alt);
  padding-block: 2.25rem;
  padding-inline: var(--gutter);
  text-align: center;
  border-top: 4px solid var(--color-green);
  border-bottom: 4px solid var(--color-green);
}
.cta-band__lead {
  font-size: min(30px, 6vw);
  line-height: 1.25;
  font-weight: var(--fw-bold);
  color: var(--color-green);
  max-width: 24ch;
  margin-inline: auto;
}
.cta-band__sub {
  margin-top: .5rem;
  font-size: 18px;
  font-weight: var(--fw-semibold);
  color: var(--color-green);
}

/* ---------- values bar ---------- */
.values-bar {
  background: var(--color-green);
  color: var(--color-text-inverse);
  padding-block: 2.5rem;
  padding-inline: var(--gutter);
}
.values-bar__inner { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 768px) { .values-bar__inner { grid-template-columns: 1fr 1fr 1fr; align-items: start; } }
.values-bar img { width: 235px; height: auto; }
.values-bar a { color: var(--color-text-inverse); }
.values-bar__list { margin-top: .75rem; }
.values-bar__list li { margin-top: .25rem; }
.values-bar__contact p + p { margin-top: .5rem; }

/* ---------- footer ---------- */
.site-footer {
  background: var(--color-surface-dark);
  color: var(--color-text-muted-on-dark);
  padding-block: 1rem;
  padding-inline: var(--gutter);
  font-size: 14px; /* measured */
}
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
}
.site-footer a { color: var(--color-text-muted-on-dark); }

/* ---------- embed (Atlas product catalogue) ---------- */
.embed-frame {
  width: 100%;
  border: 0;
  aspect-ratio: 1 / 1;
  min-height: 900px;
}

/* ---------- mobile-first shell ----------
 * Written as min-width rather than max-width so no breakpoint value can be
 * misread as a container width, and so the small screen is the default. */
.nav-toggle { display: inline-flex; }
.site-nav {
  display: none;
  position: absolute;
  inset-inline: var(--gutter);
  top: 100%;
  background: var(--color-surface-dark);
  padding: 1rem;
}
.site-nav[data-open="true"] { display: block; }
.site-nav ul { flex-direction: column; align-items: flex-start; gap: .75rem; }
.hero { min-height: 520px; }
.utility-list { justify-content: flex-start; }

@media (min-width: 1025px) {
  .nav-toggle { display: none; }
  .nav-scrim { display: none; }
  .site-nav {
    display: block;
    position: static;
    inset: auto;
    width: auto;
    background: transparent;
    padding: 0;
    box-shadow: none;
    transform: none;
    overflow: visible;
  }
  .site-nav li + li { border-top: 0; }
  .site-nav a { padding: 0; font-size: var(--fs-nav); font-weight: var(--fw-regular); }
  .site-nav a::after { display: block; }
  .site-nav a[aria-current="page"]::before { content: none; }
  .site-nav ul { flex-direction: row; align-items: center; gap: 1.5rem; }
  .hero { min-height: 737px; }
  .utility-list { justify-content: space-between; }
}

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

/* ---------- hero backgrounds ----------
 * In CSS rather than inline style: it keeps presentation out of the markup and
 * puts the source's background photography where verify-design's decorative
 * scan can see it. Each declaration pairs a plain url() fallback with an
 * image-set() so modern browsers still get the WebP derivative — Apache has no
 * negotiating origin, unlike the Duda CDN these came from.
 */
.hero--home {
  background-image: url('/assets/img/adobestock-233401519-4956h.opt.jpeg?v=d21d3ecf');
  background-image: image-set(url('/assets/img/adobestock-233401519-4956h.webp?v=da54333f') type('image/webp'), url('/assets/img/adobestock-233401519-4956h.opt.jpeg?v=d21d3ecf') type('image/jpeg'));
}
.hero--about {
  background-image: url('/assets/img/photo-1549517045-bc93de075e53-a6d8be19-3891h.opt.jpg?v=b9ad0d9f');
  background-image: image-set(url('/assets/img/photo-1549517045-bc93de075e53-a6d8be19-3891h.webp?v=50199d3f') type('image/webp'), url('/assets/img/photo-1549517045-bc93de075e53-a6d8be19-3891h.opt.jpg?v=b9ad0d9f') type('image/jpeg'));
}
.hero--services {
  background-image: url('/assets/img/photo-1592595896616-c37162298647-16726b92.opt.jpg?v=a56e35a8');
  background-image: image-set(url('/assets/img/photo-1592595896616-c37162298647-16726b92.webp?v=b1f23496') type('image/webp'), url('/assets/img/photo-1592595896616-c37162298647-16726b92.opt.jpg?v=a56e35a8') type('image/jpeg'));
}
.hero--products {
  background-image: url('/assets/img/adobestock-4814843.opt.jpeg?v=636002a4');
  background-image: image-set(url('/assets/img/adobestock-4814843.webp?v=2eebb602') type('image/webp'), url('/assets/img/adobestock-4814843.opt.jpeg?v=636002a4') type('image/jpeg'));
}
.hero--gallery {
  background-image: url('/assets/img/img-1910-1024x768-1-5245dcb7-3982h.opt.jpeg?v=134999e9');
  background-image: image-set(url('/assets/img/img-1910-1024x768-1-5245dcb7-3982h.webp?v=5ec9afb2') type('image/webp'), url('/assets/img/img-1910-1024x768-1-5245dcb7-3982h.opt.jpeg?v=134999e9') type('image/jpeg'));
}
.hero--reviews {
  background-image: url('/assets/img/photo-1583608205776-bfd35f0d9f83.opt.jpg?v=f903b418');
  background-image: image-set(url('/assets/img/photo-1583608205776-bfd35f0d9f83.webp?v=76214315') type('image/webp'), url('/assets/img/photo-1583608205776-bfd35f0d9f83.opt.jpg?v=f903b418') type('image/jpeg'));
}
.hero--cleburne {
  background-image: url('/assets/img/adobestock-233401519-4956h.opt.jpeg?v=d21d3ecf');
  background-image: image-set(url('/assets/img/adobestock-233401519-4956h.webp?v=da54333f') type('image/webp'), url('/assets/img/adobestock-233401519-4956h.opt.jpeg?v=d21d3ecf') type('image/jpeg'));
}

/* ---------- certification badges ----------
 * Five outbound trust badges the source carries at the top of the contact
 * section, over a photographic band. Real content, not decoration.
 */
.cert-band {
  position: relative;
  background-image: url('/assets/img/adobestock-316876592.opt.jpeg?v=59979d9e');
  background-image: image-set(url('/assets/img/adobestock-316876592.webp?v=7bc3498f') type('image/webp'), url('/assets/img/adobestock-316876592.opt.jpeg?v=59979d9e') type('image/jpeg'));
  background-size: cover;
  background-position: center;
  padding-inline: var(--gutter);
  margin-bottom: 3rem;
}
/* User-requested: the source runs the badges straight over the photograph and
   they are hard to read. A white wash sits between photo and badges. */
.cert-band::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.62);
}
.cert-badges {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  align-items: center;
  justify-content: space-between;
  padding-block: 2.75rem;
}
.cert-badges > li { position: relative; z-index: 1; }
.cert-badges img { width: clamp(120px, 12vw, 170px); height: auto; }

/* 6. icons drawn on one 24px grid so they read as a pair */
.social-links svg { width: 22px; height: 22px; display: block; }

.field--spaced { margin-top: 1.25rem; }
.form-actions { margin-top: 1.5rem; }

/* 404 uses a solid header rather than one floating over a hero */
.site-header[data-static-header] { position: static; background: var(--color-surface-dark); }

/* text blocks inside a full-width section still get a readable measure */
.section-block p:not(.eyebrow), .section-block li { max-width: var(--measure); }

/* ============================================================
 * Section layouts reproduced from the source. The first pass used one
 * generic block for everything, which flattened these into stacked
 * paragraphs. Copy is unchanged; only the structure is authored.
 * ============================================================ */

/* --- display pair: bold word + Dancing Script word, as the source sets them --- */
.display-pair { display: flex; flex-wrap: wrap; align-items: baseline; gap: .25em; margin: 0; }
.display-pair--center { justify-content: center; text-align: center; }
.display-pair__bold {
  font-weight: var(--fw-bold);
  font-size: var(--fs-h2);
  line-height: 1.05;
  color: var(--color-text-strong);
}
.display-pair .accent { color: var(--color-green); }
/* Declared AFTER the green rule on purpose: both selectors have the same
   specificity, so source order decides. With it above, "Love Us" rendered
   green on the green testimonial band and was invisible. */
.display-pair--inverse .display-pair__bold,
.display-pair--inverse .accent { color: var(--color-text-inverse); }

/* the contact heading sits on one line on the source; h2 scale is too large here */
.contact-block__form .display-pair__bold { font-size: clamp(1.5rem, 3vw, 2.2rem); }
.contact-block__form .accent { font-size: clamp(2.2rem, 5vw, 3.4rem); }

.eyebrow--center { text-align: center; max-width: none; }
.eyebrow { color: var(--color-text); letter-spacing: .04em; }

/* --- hero: centred, script word inline --- */
.hero { justify-content: center; text-align: center; }
.hero > .inner { display: flex; flex-direction: column; align-items: center; }
.hero__title { max-width: 22ch; }
.hero__title .accent { color: var(--color-text-inverse); font-size: min(1.15em, 16vw); }

/* --- experience: right-aligned copy, framed image --- */
.experience__grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; align-items: center; }
.experience__copy > * + * { margin-top: .75rem; }
.experience__lead { font-weight: var(--fw-bold); color: var(--color-text-strong); }
.experience .display-pair { gap: .3em; }
@media (min-width: 900px) {
  .experience__grid { grid-template-columns: 1fr 1fr; }
  .experience__copy { text-align: right; }
  .experience .display-pair { justify-content: flex-end; }
  .experience__copy p { margin-left: auto; }
}
/* the green offset frame the source puts behind the photo */
.framed { position: relative; }
.framed::before {
  content: "";
  position: absolute;
  /* Measured from the source: a SOLID green block offset 15px left and 18px
     above the photo, ending 19px short of its right edge and 28px short of the
     bottom. It was an outline before, which read as a frame rather than a
     filled shape. */
  inset: -18px 19px 28px -15px;
  background: var(--color-green);
  z-index: 0;
}
.framed picture, .framed img { position: relative; z-index: 1; }

/* --- let's work: centred heading, four numbered steps --- */
.lets-work { text-align: center; }
.lets-work__intro { margin: 1rem auto 2.5rem; }
.lets-work__intro p + p { margin-top: 2px; }
.lets-work__intro p { margin-inline: auto; }
.steps { counter-reset: step; text-align: left; }
.step__head { display: flex; align-items: baseline; gap: .3em; margin: 0; }
.step__num {
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  color: var(--color-green);
  line-height: 1;
}
.step__copy { margin-top: .5rem; }

/* --- commitment --- */
.commitment { text-align: center; }
.commitment__lead { font-style: italic; margin-inline: auto; }
.commitment__call {
  margin-top: 1rem;
  font-size: min(var(--fs-h3), 6vw);
  font-weight: var(--fw-regular);
  color: var(--color-text-strong);
  max-width: none;
}
.commitment__call strong, .commitment__call a { color: var(--color-green); font-weight: var(--fw-bold); }

/* --- testimonial band: full-width green, white type --- */
.testimonial-band {
  background: var(--color-green);
  color: var(--color-text-inverse);
  padding-block: 3.5rem;
  padding-inline: var(--gutter);
  text-align: center;
}
.testimonial { margin: 2rem auto 0; max-width: 70ch; }
.testimonial p { color: var(--color-text-inverse); max-width: none; }
.testimonial__by { margin-top: 1rem; font-family: var(--font-accent); }
.testimonial__cta { margin-top: 2rem; }
.btn--light { background: var(--color-surface); color: var(--color-green); }
.btn--light:hover { background: var(--color-surface-alt); color: var(--color-green); }

/* --- contact: image left, form right --- */
.contact-block { background: var(--color-surface); padding-block: 4rem; padding-inline: var(--gutter); }
.contact-block__grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; align-items: end; }
@media (min-width: 900px) { .contact-block__grid { grid-template-columns: 2fr 3fr; } }
.contact-block__intro { margin: .75rem 0 1.5rem; }
.field--full { grid-column: 1 / -1; }

/* ============================================================
 * Values measured directly from the source, not eyeballed.
 * ============================================================ */

/* estimate CTA: one row, copy left, button right */
.estimate-cta { text-align: left; }
.estimate-cta__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem 2rem;
}
.estimate-cta__copy { margin: 0; max-width: none; }
.ec-plain  { font-size: min(24px, 5vw); font-weight: var(--fw-regular); color: var(--color-text-strong); }
.ec-strong { font-size: min(24px, 5vw); font-weight: var(--fw-semibold); color: var(--color-green); }

/* process steps: number 60px green at 50%, word 55px BLACK Dancing Script */
.step__num {
  font-family: var(--font-primary);
  font-size: min(60px, 11vw);
  font-weight: var(--fw-bold);
  color: rgba(2, 62, 29, 0.5);
  line-height: 1;
}
.step__word {
  font-family: var(--font-accent);
  font-size: min(55px, 10vw);
  font-weight: var(--fw-bold);
  color: var(--color-text-strong);
  line-height: 1;
}

/* CALL NOW is a 36px composed line, not body copy */
.commitment p.commitment__call { margin-top: 1.25rem; max-width: none; text-align: center; }
.cn-strong { font-size: min(36px, 7vw); font-weight: var(--fw-semibold); color: var(--color-green); }
.cn-plain  { font-size: min(36px, 7vw); font-weight: var(--fw-regular); color: var(--color-text); }
.cn-phone  { font-size: min(36px, 7vw); font-weight: var(--fw-bold); color: var(--color-green); text-decoration: none; }

/* testimonial quote runs the full container on the source, not a narrow measure */
.testimonial { max-width: none; }
.testimonial p { font-size: 18px; }

/* footer social icons */
.social-links { display: flex; gap: .75rem; margin-top: 1rem; }
.social-links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 29px;
  height: 29px;
  color: var(--color-text-inverse);
}
.social-links a:hover { color: var(--color-text-inverse); text-decoration: underline; }

/* ============================================================
 * MEASURED OVERRIDES — every value below was read off the live source with
 * getComputedStyle, not estimated. The earlier pass used one global size for
 * .display-pair and body copy; the source varies both per section.
 * ============================================================ */

/* eyebrows are 16px/300 grey everywhere, not 20px/700.
   Selector is p.eyebrow so it outranks `.section-block p`, which would
   otherwise win on specificity and force 18px. */
p.eyebrow, .eyebrow {
  font-size: 16px;
  font-weight: var(--fw-light);
  color: var(--color-text);
  text-transform: uppercase;
  line-height: 1.4;
}

/* section body copy is 18px on the source, not the 14px page default */
.section-block p:not(.eyebrow),
.experience__copy p:not(.eyebrow),
.lets-work__intro p,
.commitment__lead,
.contact-block__intro,
.step__copy { font-size: 18px; line-height: 1.5; }

/* --- experience --- */
.experience .display-pair__bold { font-size: min(50px, 9vw); color: var(--color-text-strong); }
.experience .accent { font-size: min(72px, 13vw); font-weight: var(--fw-semibold); color: var(--color-green); }
.experience__lead { font-weight: var(--fw-bold); color: var(--color-text-strong); }
.experience__copy > p { color: var(--color-text); }

/* --- let's work --- */
.lets-work .display-pair__bold { font-size: min(50px, 9vw); }
.lets-work .accent { font-size: min(72px, 13vw); font-weight: var(--fw-bold); }
.lets-work__intro p {
  font-weight: var(--fw-semibold);
  color: var(--color-text-strong);
  max-width: 62ch;
}
/* step copy is black at 50%, and sits indented under the script word */
.steps p.step__copy { color: rgba(0, 0, 0, 0.5); max-width: 181px; margin-left: 2rem; }
.lets-work .lets-work__intro p { max-width: 820px; }

/* --- commitment --- */
/* measured: italic, and the section carries no padding at all (152px tall) */
.commitment__lead { color: var(--color-text); font-style: italic; max-width: 870px; margin-inline: auto; }
.commitment { padding-block: 1.25rem; }
.lets-work { padding-bottom: 2rem; }

/* --- testimonial band --- */
.testimonial-band .display-pair__bold { font-size: min(35px, 7vw); }
.testimonial-band .accent { font-size: min(55px, 10vw); font-weight: var(--fw-light); }
.testimonial__by {
  font-size: min(35px, 7vw);
  font-weight: var(--fw-light);
  font-family: var(--font-accent);
  color: var(--color-text-inverse);
}

/* --- contact --- */
.contact-block__form { text-align: center; }
.contact-block__form .display-pair { justify-content: center; }
.contact-block__form .accent {
  font-size: min(55px, 10vw);
  font-weight: var(--fw-bold);
  color: var(--color-green-alt);
}
.contact-block__form .display-pair__bold { font-size: min(35px, 7vw); font-weight: var(--fw-semibold); }
.contact-block__intro { font-weight: var(--fw-regular); margin-inline: auto; max-width: 60ch; }
.contact-block__form form { text-align: left; }

/* ---------- sticky header ----------
 * The source pins the nav to the top on scroll and fades in the dark green
 * background; at the top of the page it is transparent over the hero. The hero
 * is pulled up by the header's height so the header overlays it rather than
 * pushing it down — sticky and absolute cannot both apply to one element.
 */
:root { --header-h: 92px; }
@media (min-width: 1025px) { :root { --header-h: 119px; } }

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  /* A guaranteed dark backing rather than pure transparency. White nav text over
     a photograph has no contrast floor — it depends entirely on which part of
     the image sits behind it, and pa11y flagged all 8 links on all 6 pages at
     1.16:1. This is subtle over the hero and gives the text a real backing. */
  background-color: rgba(0, 0, 0, 0.45);
  transition: background-color .3s var(--easing);
}
.site-header.is-stuck { background: var(--color-green); }

.site-header { margin-bottom: calc(-1 * var(--header-h)); }
.hero { padding-top: calc(var(--space-hero-block) + var(--header-h)); }
@media (prefers-reduced-motion: reduce) { .site-header { transition: none; } }


/* ============================================================
 * MOTION — user-requested. The source has none; Phase 2 recorded "do not add
 * any, that would be redesign". This is a deliberate, user-approved departure.
 *
 * Restraint on purpose: one reveal gesture reused everywhere, plus hover on the
 * things that are already interactive. No parallax, no counters, no carousels.
 * Nothing is hidden until JS confirms it can reveal it again, so a script
 * failure leaves the page fully readable rather than blank.
 * ============================================================ */

.has-js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .55s var(--easing), transform .55s var(--easing);
  transition-delay: calc(var(--i, 0) * 70ms);
  will-change: opacity, transform;
}
.has-js .reveal.is-visible { opacity: 1; transform: none; }

/* trust badges: lift and come up to full strength on hover */
.cert-badges li a {
  display: block;
  transition: transform .3s var(--easing), filter .3s var(--easing);
  filter: saturate(.85);
}
.cert-badges li a:hover,
.cert-badges li a:focus-visible { transform: translateY(-6px) scale(1.04); filter: saturate(1); }

/* gallery: a slow push-in, clipped by the tile */
.gallery-grid li { overflow: hidden; }
.gallery-grid img { transition: transform .5s var(--easing); }
.gallery-grid li:hover img { transform: scale(1.07); }

/* process steps: the numeral firms up as the row arrives */
.step__num { transition: color .4s var(--easing); }
.step:hover .step__num { color: var(--color-green); }

/* the framed photo and its green block separate slightly on hover */
.experience__media { transition: transform .4s var(--easing); }
.experience__media:hover { transform: translate(4px, -4px); }
.framed::before { transition: transform .4s var(--easing); }
.experience__media:hover.framed::before { transform: translate(-6px, 6px); }

/* buttons already transition colour; add a small physical response */
.btn { transition: var(--transition-button), transform .2s var(--easing); }
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

/* nav underline grows from the centre rather than appearing */
.site-nav a { position: relative; }
.site-nav a::after {
  content: "";
  position: absolute;
  left: 50%; right: 50%; bottom: -4px;
  height: 2px;
  background: currentColor;
  transition: left .25s var(--easing), right .25s var(--easing);
}
.site-nav a:hover::after { left: 0; right: 0; }
.site-nav a:hover { text-decoration: none; }

/* Reduced motion: everything above collapses to a plain, instant page. */
@media (prefers-reduced-motion: reduce) {
  .has-js .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .cert-badges li a,
  .gallery-grid img,
  .experience__media,
  .framed::before,
  .btn,
  .site-nav a::after { transition: none !important; }
  .cert-badges li a:hover,
  .experience__media:hover,
  .gallery-grid li:hover img,
  .btn:hover { transform: none !important; }
}

/* ============================================================
 * SUBPAGE SECTIONS — same system as the homepage, values measured from the
 * source per page. eyebrow -> bold heading (+ green script word) -> body.
 * ============================================================ */

/* Hero: subpages run a 60px headline with a 78px WHITE script word; the two
   landing pages (home, cleburne) run 72px with a larger script. */
.hero--sub .hero__title { font-size: min(60px, 12vw); line-height: 1.05; }
.hero--sub .hero__title .accent { font-size: min(78px, 15vw); }
/* Measured: home and cleburne run a 120px script word against a 72px headline;
   /products runs 72px; every other subpage 78px. */
.hero--landing .hero__title { font-size: min(72px, 14vw); }
.hero--landing .hero__title .accent { font-size: min(120px, 22vw); }
.hero--products .hero__title .accent { font-size: min(72px, 14vw); }

/* the heading pair takes its sizes per section, as the source varies them */
.sub__head { --bold-size: 32px; --script-size: 48px; }
.sub__head .display-pair__bold { font-size: min(var(--bold-size), 7vw); }
.sub__head .accent { font-size: min(var(--script-size), 10vw); color: var(--color-green); }

.sub .inner { display: grid; gap: 2.5rem; grid-template-columns: 1fr; align-items: center; }
.sub__copy > * + * { margin-top: 1rem; }
.sub__copy--wide { max-width: none; }

.sub--right { text-align: right; }
.sub--right .display-pair { justify-content: flex-end; }
.sub--right .sub__copy p { margin-left: auto; }
.sub--center { text-align: center; }
.sub--center .display-pair { justify-content: center; }
.sub--center .sub__copy { margin-inline: auto; }
.sub--center .sub__copy p { margin-inline: auto; }
.sub--left { text-align: left; }

@media (min-width: 900px) {
  .sub:has(.sub__media) .inner { grid-template-columns: 1fr 1fr; }
  .sub--right:has(.sub__media) .sub__copy { order: 1; }
  .sub--right:has(.sub__media) .sub__media { order: 2; }
}

/* body sizes are per-paragraph on the source, so they are explicit here */
.sub .body-18, .cleburne-intro__body .body-18 { font-size: 18px; line-height: 1.5; }
.sub .body-16, .cleburne-intro__body .body-16 { font-size: 16px; line-height: 1.6; }
.sub .w-300 { font-weight: var(--fw-light); }
.sub .w-400 { font-weight: var(--fw-regular); }
.sub .w-600 { font-weight: var(--fw-semibold); color: var(--color-text); }
.sub .w-700 { font-weight: var(--fw-bold); color: var(--color-text); }
.sub .tone-green { color: var(--color-green); }
.sub .tone-strong { color: var(--color-text-strong); }
.sub .sub__copy p { max-width: 62ch; }
/* Wide body copy fills the shared container rather than declaring its own
   width — the source runs these at 1097-1187px inside the 1200 container, and
   a third max-width in container range is a design decision, not a detail. */
.sub--center .sub__copy--wide p { max-width: none; }

/* owners: two blocks, each copy + a small photo grid */
/* Uniform tiles: the six owner photos have mixed aspect ratios, which left the
   grid ragged with a lone tile hanging below the others. */
.owners__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; align-content: start; }
.owners__grid > div { overflow: hidden; }
.owners__grid img { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.owners__grid picture { display: block; height: 100%; }
.owners__second { margin-top: 4rem; }

/* the Cleburne intro runs left-aligned body under a centred heading */
.cleburne-intro__body { text-align: left; margin-top: 1.5rem; }
.cleburne-intro__body p + p { margin-top: 1rem; }
.cleburne-intro__body p { max-width: none; }

/* FAQ: 20px/700 question, 16px/300 answer, both left */
.faq__list { max-width: 760px; }
.faq__list dt {
  font-size: min(20px, 5vw);
  font-weight: var(--fw-bold);
  color: var(--color-text-strong);
  margin-top: 2rem;
}
.faq__list dt:first-child { margin-top: 0; }
.faq__list dd {
  margin: .5rem 0 0;
  font-size: 16px;
  font-weight: var(--fw-light);
  color: var(--color-text-strong);
  max-width: 62ch;
}

/* Adjacent bands of the same colour butt together on the source (repairs and
   the FAQ are one continuous grey block), so the doubled padding between them
   is removed rather than left as a dead gap. */
.section-block--alt + .section-block--alt { padding-top: 0; }

/* ============================================================
 * REVIEW ROUND — user-reported items
 * ============================================================ */

/* 2. FAQ: six cards in two columns, native <details> accordion.
 *    No JS needed, keyboard accessible, and correct semantics. */
.faq__grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 860px) { .faq__grid { grid-template-columns: 1fr 1fr; } }
.faq__item {
  background: var(--color-surface);
  border: 1px solid #cfcfcf;
  border-left: 4px solid var(--color-green);
}
.faq__item summary {
  display: flex;
  gap: .9rem;
  align-items: center;
  padding: 1.15rem 1.25rem;
  cursor: pointer;
  list-style: none;
  font-size: min(18px, 4.4vw);
  font-weight: var(--fw-bold);
  color: var(--color-text-strong);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__icon { flex: none; color: var(--color-green); }
.faq__q { flex: 1; }
.faq__item summary::after {
  content: "";
  flex: none;
  width: 10px; height: 10px;
  border-right: 2px solid var(--color-green);
  border-bottom: 2px solid var(--color-green);
  transform: rotate(45deg);
  transition: transform .25s var(--easing);
}
.faq__item[open] summary::after { transform: rotate(-135deg); }
.faq__a { padding: 0 1.25rem 1.25rem calc(1.25rem + 26px + .9rem); }
.faq__a p { font-size: 16px; font-weight: var(--fw-light); color: var(--color-text-strong); max-width: 60ch; }
.faq__item summary:focus-visible { outline: 3px solid var(--color-green); outline-offset: -3px; }

/* 1. About photos: a staggered mosaic rather than a rigid grid that crops faces. */
.owners__grid {
  display: grid;
  /* two across: three square tiles fill two rows, so the media column is close
     in height to the long bio copy beside it instead of leaving a dead gap. */
  grid-template-columns: repeat(2, 1fr);
  gap: .75rem;
}
.owners__grid > div { aspect-ratio: 1; }
.owners__grid > div { overflow: hidden; }
.owners__grid picture { display: block; height: 100%; }
.owners__grid img {
  width: 100%; height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  object-position: center 30%;   /* bias up so faces are not cropped */
  transition: transform .5s var(--easing);
}
.owners__grid > div:hover img { transform: scale(1.05); }

/* 3. Footer bar taller — the design credit reveals its stars ABOVE the link on
 *    hover, which was clipping against the bar's original height. */
.site-footer { padding-block: 2.25rem; }
.site-footer__inner { align-items: center; row-gap: 1rem; }

/* 4. Reviews hero: the headline was breaking after "Are". Let it run wider. */
.hero--reviews .hero__title { max-width: 30ch; }
.hero--reviews .hero__title .accent { font-size: min(64px, 12vw); }

/* 9. BUG: .section-block set padding-block but no padding-inline, so body copy
 *    ran flush to the viewport edge on narrow screens. */
.section-block,
.contact-block,
.testimonial-band,
.faq { padding-inline: var(--gutter); }

/* ---------- 6. sticky call button (mobile only) ---------- */
.call-fab {
  position: fixed;
  right: max(1rem, env(safe-area-inset-right));
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 60;
  display: none;
  align-items: center;
  gap: .55rem;
  padding: .85rem 1.1rem;
  background: var(--color-green);
  color: var(--color-text-inverse);
  font-size: 15px;
  font-weight: var(--fw-bold);
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .35);
}
.call-fab:hover, .call-fab:focus-visible { background: var(--color-green-alt); }

@media (max-width: 767px) {
  /* 6. the black utility bar is hidden on phones; its content lives in the
     sticky call button and the footer, so nothing is lost. */
  .utility-bar { display: none; }
  .call-fab { display: inline-flex; }
  /* keep the fixed button clear of the footer's last line */
  .site-footer { padding-bottom: 5.5rem; }

  /* 8. headlines were sized for a 390px viewport but still ran long */
  .hero--sub .hero__title { font-size: min(34px, 9vw); }
  .hero--sub .hero__title .accent { font-size: min(42px, 11vw); }
  .hero--landing .hero__title { font-size: min(38px, 10vw); }
  .hero--landing .hero__title .accent { font-size: min(52px, 14vw); }
  .hero--reviews .hero__title .accent { font-size: min(34px, 9vw); }
  .hero { min-height: 420px; }

  /* 10. the four process steps read badly stacked at full width */
  .steps { grid-template-columns: 1fr 1fr; gap: 1.75rem 1rem; }
  .step__num { font-size: 34px; }
  .step__word { font-size: 30px; }
  .steps p.step__copy { margin-left: 0; max-width: none; font-size: 15px; }
  .lets-work__intro p { font-size: 16px; }

  /* 11. the pointing photo eats a full screen before the form on a phone */
  .contact-block__media { display: none; }

  /* general narrow-screen tightening */
  .display-pair { justify-content: center !important; text-align: center; }
  .sub--right { text-align: center; }
  .sub--right .sub__copy p { margin-inline: auto; }
  .section-block { padding-block: 2.5rem; }
  .owners__grid { grid-template-columns: repeat(2, 1fr); gap: .5rem; }
  .cert-badges { gap: 1.25rem; justify-content: center; }
  .cert-badges img { width: clamp(96px, 26vw, 130px); }
  .estimate-cta__row { justify-content: center; text-align: center; }
}

/* ---------- 7. mobile nav tray ----------
 * Written mobile-first: the tray IS the default and the desktop rule below
 * undoes it. Declaring it in a max-width query meant the breakpoint value was
 * read as a third container width. */
.site-nav {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(320px, 84vw);
    background: var(--color-green);
    padding: 5rem 1.5rem 2rem;
    box-shadow: -12px 0 32px rgba(0, 0, 0, .35);
    transform: translateX(100%);
    transition: transform .32s var(--easing);
    display: block;
    overflow-y: auto;
  }
.site-nav[data-open="true"] { transform: translateX(0); }
.site-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
.site-nav li + li { border-top: 1px solid rgba(255, 255, 255, .18); }
.site-nav a {
    display: block;
    padding: .95rem .25rem;
    font-size: 18px;
    font-weight: var(--fw-semibold);
  }
.site-nav a::after { display: none; }
.site-nav a[aria-current="page"] { border-bottom: 0; color: var(--color-text-inverse); }
.site-nav a[aria-current="page"]::before { content: "▸ "; }

.nav-toggle { z-index: 70; position: relative; }
.nav-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .5);
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s var(--easing);
    z-index: 25;
  }
.nav-scrim[data-open="true"] { opacity: 1; pointer-events: auto; }
body.nav-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .site-nav, .nav-scrim, .faq__item summary::after, .owners__grid img { transition: none !important; }
}

/* the sticky call button competes with the open nav tray */
body.nav-open .call-fab { display: none; }

/* ---------- desktop nav restore ----------
 * MUST come after the tray rules above. The tray is the mobile-first default,
 * so it is declared unconditionally; the earlier min-width:1025px reset was
 * losing to it on source order and left the nav translated off-screen at
 * desktop. Declared last so it wins.
 */
@media (min-width: 1025px) {
  .site-nav {
    position: static;
    inset: auto;
    width: auto;
    background: transparent;
    padding: 0;
    box-shadow: none;
    transform: none;
    overflow: visible;
    display: block;
  }
  .site-nav ul { flex-direction: row; align-items: center; gap: 1.5rem; }
  .site-nav li + li { border-top: 0; }
  .site-nav a {
    display: inline-block;
    padding: 0;
    font-size: var(--fs-nav);
    font-weight: var(--fw-regular);
  }
  .site-nav a::after { display: block; }
  .site-nav a[aria-current="page"] {
    border-bottom: 2px solid var(--color-text-inverse);
    padding-bottom: 2px;
  }
  .site-nav a[aria-current="page"]::before { content: none; }
  .nav-toggle { display: none; }
  .nav-scrim { display: none; }
}

/* ============================================================
 * 1. Owner photos must not be cropped.
 *    Natural ratios run 0.56 (portrait) to 1.75 (wide), so ANY fixed-ratio
 *    tile using object-fit: cover will cut something off. contain shows each
 *    photo whole and keeps the tiles uniform, so grid rows still align.
 * ============================================================ */
/* The tiles now hold attention-cropped square variants, so cover fills them
   edge to edge with no letterboxing and the subject already centred. */
.owners__grid > div { overflow: hidden; background: var(--color-surface-alt); }
.owners__grid picture { display: block; width: 100%; height: 100%; }
.owners__grid img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ============================================================
 * 2. Subpage heroes at 60% of the homepage hero.
 *    Homepage measures 737px desktop / 467px mobile.
 * ============================================================ */
:root { --hero-h: 737px; --hero-h-sub: 442px; }

.hero--landing { min-height: var(--hero-h); }
.hero--sub {
  min-height: var(--hero-h-sub);
  padding-block: calc(48px + var(--header-h)) 48px;
}

@media (max-width: 767px) {
  :root { --hero-h: 467px; --hero-h-sub: 280px; }
  .hero--landing { min-height: var(--hero-h); }
  .hero--sub {
    min-height: var(--hero-h-sub);
    padding-block: calc(20px + var(--header-h)) 20px;
  }
}

/* label and number are separate elements with no literal space between them:
   html-validate reads the whole link text as a phone number and flags any
   plain space inside it. */
.call-fab__label { margin-right: .35em; }

/* Breadcrumbs — visible trail matching the BreadcrumbList markup. Google
   expects structured data to reflect something on the page, not stand alone. */
.breadcrumbs { background: var(--color-surface); padding: .9rem var(--gutter); border-bottom: 1px solid #e6e6e6; }
.breadcrumbs ol { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; font-size: 14px; }
.breadcrumbs li + li::before { content: "›"; margin-right: .4rem; color: var(--color-text); }
.breadcrumbs a { color: var(--color-green); text-decoration: none; }
.breadcrumbs a:hover { text-decoration: underline; }
.breadcrumbs [aria-current="page"] { color: var(--color-text); }

/* invalid fields need to be visible, not only announced */
.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
  border-color: #B3261E;
  border-bottom-width: 2px;
}
