/* SMR Website v2 — Components */

/* ================= Grain texture (atmosphere on dark, not flat black) ================= */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ================= Header / Nav ================= */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-header);
  padding-block: var(--space-3);
  transition: background-color var(--dur-fast) var(--ease-out), padding-block var(--dur-fast) var(--ease-out);
}
/* Permanent subtle scrim behind the nav, independent of the hero overlay --
   guarantees nav-text contrast regardless of which photo sits underneath on
   a given page, not just after the .is-scrolled solid background kicks in. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(4, 7, 8, 0.55) 0%, rgba(4, 7, 8, 0) 100%);
  opacity: 1;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.site-header.is-scrolled::before { opacity: 0; }
.site-header.is-scrolled {
  background: rgba(4, 7, 8, 0.86);
  backdrop-filter: blur(10px);
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--ink-3);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
/* 768-1099px: the shared --pad clamp (~38px/side at 768px) plus normal gaps
   still overflowed by 104px after tightening the nav itself and shortening
   the CTA -- reclaim room from padding specifically on the header (other
   sections keep full --pad; only the header needs this). The matching
   .main-nav gap override lives below, after the base .main-nav rule, so it
   actually wins the cascade (same-specificity rules earlier in the file
   lose to later ones regardless of media-query nesting). */
@media (min-width: 768px) and (max-width: 1099px) {
  .header-inner { padding-inline: 1.25rem; gap: var(--space-2); }
}
.wordmark { display: inline-flex; align-items: center; flex-shrink: 0; }
.logo-img { display: block; height: 40px; width: auto; }

.main-nav { display: none; gap: var(--space-2); white-space: nowrap; }
.main-nav a {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.82;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.main-nav a:hover, .main-nav a[aria-current="page"] { opacity: 1; color: var(--accent); }

/* Nav items (BACK TO 7 as of 2026-07-20: roofing-siding + decks restored,
   basements page removed -- same 7-item count and same labels as the original
   set) + wordmark + CTA is tight right at the 768px breakpoint -- start
   compact there (above) and only relax toward the roomier desktop spacing once
   there's real width to spend (2026-07-19 fix pass: 768px was clipping the
   last nav items off the right edge at exactly this 7-item count; the compact
   spacing below is what fixed it, so it must stay). Re-verified at 768/1099/
   1101/1440 after the 2026-07-20 restore -- no clipping. */
@media (min-width: 768px) {
  .main-nav { display: flex; }
}
@media (min-width: 768px) and (max-width: 1099px) {
  .main-nav { gap: 0.5rem; }
  .main-nav a { font-size: 0.68rem; letter-spacing: 0.06em; }
}
/* .container maxes out at --maxw (1280px) minus 2x the padding cap (4rem/64px
   each side) = 1152px available -- that ceiling is reached at any viewport
   >=1280px and never grows further, however wide the screen gets. The old
   --space-4/--text-small/--ls-wide "roomy desktop" nav plus the full CTA
   phone-number text needed more room than that 1152px ceiling ever provides,
   so it clipped at every desktop width from 1100px up through 1600px+
   (2026-07-19 nav-clipping fix pass, measured 33px over at 1280px). Sized
   these values against the WORST case in range (1100px viewport, ~990px
   container -- the low end, not the 1152px plateau) so nothing above 1100px
   can ever reclip. */
@media (min-width: 1100px) {
  .main-nav { gap: 0.75rem; }
  .main-nav a { font-size: 0.78rem; letter-spacing: 0.07em; }
}

/* Header CTA is a genuine two-action group -- call AND text are each a real
   reachable link (tel: / sms:), not one button whose label merely implies
   both (2026-07-19 Phase 2 fix -- the old single "Call/Text" button only
   ever linked tel:, so "text" was never actually reachable by tapping it).
   Always visible, no hamburger menu, both actions stay one tap away at
   every breakpoint down to 320px. */
.header-cta-group {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}
.header-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.6rem 0.85rem;
  font-size: var(--text-xs);
}
.cta-icon { width: 15px; height: 15px; flex-shrink: 0; }
.header-cta-text { background: transparent; color: var(--paper-100); border-color: var(--paper-45); }
.header-cta-text:hover, .header-cta-text:focus-visible { border-color: var(--accent); color: var(--accent); }
/* Icon-only through the tight 768-1099px nav range (7 nav items + wordmark +
   2 full-text CTA buttons don't fit there -- measured overflow was 271px at
   768px for a single full-text button pre-fix, 2026-07-19; two buttons need
   the same discipline). aria-label on each <a> carries the accessible name
   when the visible label is hidden. Below 768px the nav is hidden entirely,
   so full labels fit fine; above 1100px the nav gets its roomier spacing
   and labels return. */
@media (min-width: 768px) and (max-width: 1099px) {
  .header-cta { padding: 0.55rem; }
  .header-cta .btn-label-full { display: none; }
}

/* Same "call AND text both genuinely reachable" fix as the header CTA
   (2026-07-19 Phase 2), applied to the hero and close-section CTAs on every
   page -- these previously used the identical single tel:-only "Call or
   text" button. Plenty of room here (unlike the header), so it's just two
   plain buttons that wrap to their own row instead of overflowing if the
   viewport is too narrow to fit both on one line. */
.cta-dual { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* ================= Buttons ================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.95rem 1.6rem;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--text-body);
  letter-spacing: 0.01em;
  border-radius: var(--radius-sm);
  border: 2px solid var(--accent);
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.btn-primary { background: var(--accent); color: var(--ink); }
.btn-primary:hover, .btn-primary:focus-visible { background: var(--accent-dim); border-color: var(--accent-dim); transform: translateY(-2px); }
.btn-outline { background: transparent; color: var(--paper-100); border-color: var(--paper-45); }
.btn-outline:hover, .btn-outline:focus-visible { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }

/* ================= Hero ================= */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-block: var(--space-7) var(--space-6);
  overflow: hidden;
}
.hero-media {
  position: absolute;
  inset: 0;
  z-index: -1;
}
.hero-media picture, .hero-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 30%;
}
.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--overlay-strong);
}
/* Homepage hero only (2026-07-19): the B1 photo (rain shower + niches) is
   brighter/more evenly lit than --overlay-strong was tuned for -- measured
   headline contrast against the rendered pixels came out to 2.97:1, below
   the 4.5:1 AA floor, at the lightest tile patch behind the text. This
   overrides the overlay for THIS hero specifically (not the shared token,
   so the 6 service-page heroes -- untouched, different photos -- aren't
   affected) with a stronger, earlier-reaching gradient so the text zone is
   reliably dark while the showerhead/upper tile stays bright. */
.hero--home .hero-media::after {
  background: linear-gradient(180deg, rgba(4,7,8,0.08) 0%, rgba(4,7,8,0.22) 38%, rgba(4,7,8,0.9) 62%, rgba(4,7,8,0.94) 100%);
}
.hero-content { max-width: 44rem; position: relative; z-index: 2; }
.hero-title {
  font-size: var(--text-hero);
  max-width: 16ch;
  /* guarantees AA contrast even over a locally-light patch of the photo,
     not just against the gradient's averaged darkness */
  text-shadow: 0 2px 28px rgba(4, 7, 8, 0.7), 0 1px 3px rgba(4, 7, 8, 0.9);
}
.hero-sub {
  margin-top: var(--space-3);
  max-width: 42ch;
  font-size: var(--text-body-lg);
  font-weight: var(--fw-body);
  color: var(--paper-100);
  text-shadow: 0 2px 20px rgba(4, 7, 8, 0.75), 0 1px 3px rgba(4, 7, 8, 0.9);
}
.hero-cta { margin-top: var(--space-4); }

/* Sub-page hero (shorter, service pages) */
.page-hero { min-height: 62svh; }
.page-hero .hero-title { font-size: var(--text-h1); max-width: 20ch; }

/* ================= Stats bar ================= */
.stats-bar {
  border-top: 1px solid var(--ink-3);
  border-bottom: 1px solid var(--ink-3);
}
.stats-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  padding-block: var(--space-5);
  text-align: center;
}
@media (min-width: 560px) {
  .stats-grid { grid-template-columns: repeat(3, 1fr); }
}
.stat-num {
  font-family: var(--font-display);
  font-weight: var(--fw-display);
  font-size: var(--text-h1);
  color: var(--accent);
  letter-spacing: var(--ls-tight);
  line-height: 1;
}
.stat-label {
  margin-top: var(--space-1);
  font-size: var(--text-small);
  color: var(--paper-72);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
}

/* ================= Section shell ================= */
.section { padding-block: var(--space-7); position: relative; }
.section-head { max-width: 40rem; margin-bottom: var(--space-5); }
.section-title { font-size: var(--text-h2); }
.section-lede { margin-top: var(--space-2); font-size: var(--text-body-lg); color: var(--paper-72); font-weight: var(--fw-body); }

/* Simple bullet list for a service's real sub-offerings -- used where a page
   has no verified-photo gallery to fill the space, so the section carries
   real substance about the service itself instead of a visual gap. */
.plain-list { margin-top: var(--space-3); display: grid; gap: var(--space-2); }
.plain-list li { position: relative; padding-left: 1.4em; font-size: var(--text-body-lg); color: var(--paper-72); }
.plain-list li::before { content: ""; position: absolute; left: 0; top: 0.65em; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* ================= The Real Work — real 3-stage sequence, one project =================
   2026-07-19 evidence-request pass, Hugo's ruling: waterproofing -> tile
   installation -> finished shower. Closer = IMG_4785 (candidate_IMG4785.jpg,
   the same file already used as service-bathrooms.jpg before this pass --
   see process_images.py SLOT_CONFIG for the full verification chain:
   same-project confirmed via 4 shared visual markers, every reflective
   surface swept with no human figure found). IMG_8222 and IMG_4783 stay
   excluded (painter's tape / ambiguous reflection / clear person-silhouette
   reflection respectively). Demolition stays excluded (every demolition-
   stage photo available for this project showed staining consistent with
   mold/water damage, rejected per Hugo's rule that no such photo ships
   regardless of caption). Mobile: single column, DOM order = chronological.
   Desktop: asymmetric three-column, finished tile wider as the payoff. */
.gallery-grid {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto;
  gap: var(--space-3);
}
@media (min-width: 768px) {
  .gallery-grid {
    grid-template-columns: 1fr 1fr 1.3fr;
    grid-template-rows: minmax(320px, 1fr);
  }
  .gallery-tile--stage-2 { grid-column: 1; grid-row: 1; }
  .gallery-tile--stage-3 { grid-column: 2; grid-row: 1; }
  .gallery-tile--after { grid-column: 3; grid-row: 1; }
}
.gallery-tile {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  min-height: 260px;
  box-shadow: var(--shadow-card);
}
.gallery-tile--after { min-height: 320px; }
.gallery-tile--stage-3 { min-height: 320px; }
.gallery-tile img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.gallery-tile-label {
  position: absolute;
  left: var(--space-3); bottom: var(--space-3);
  z-index: 2;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--text-small);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--paper-100);
  background: rgba(4, 7, 8, 0.55);
  padding: 0.5em 0.9em;
  border-radius: var(--radius-sm);
  backdrop-filter: blur(6px);
}
.gallery-tile::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(4,7,8,0) 50%, rgba(4,7,8,0.65) 100%);
  z-index: 1;
}

/* ================= Services ================= */
.services-primary {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
@media (min-width: 768px) {
  .services-primary { grid-template-columns: 1fr 1fr; }
}
.service-card {
  position: relative;
  background: var(--ink-2);
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform var(--dur-fast) var(--ease-out);
}
.service-card:hover { transform: translateY(-4px); }
.service-card-media { aspect-ratio: 4 / 3; overflow: hidden; }
.service-card-media img { width: 100%; height: 100%; object-fit: cover; }
.service-card-body { padding: var(--space-3); }
.service-card-title { font-size: var(--text-h3); }
.service-card-desc { margin-top: var(--space-1); font-size: var(--text-body); color: var(--paper-72); font-weight: var(--fw-body); }
.service-card-link {
  display: inline-block; margin-top: var(--space-2);
  font-family: var(--font-display); font-weight: var(--fw-medium);
  font-size: var(--text-small); text-transform: uppercase; letter-spacing: var(--ls-wide);
  color: var(--accent);
}

.services-secondary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.service-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.75em 1.2em;
  border: 1px solid var(--ink-3);
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--text-small);
  color: var(--paper-72);
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.service-chip:hover { border-color: var(--accent); color: var(--paper-100); }

/* ================= Trust block ================= */
.trust-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 768px) {
  .trust-grid { grid-template-columns: repeat(3, 1fr); }
}
.trust-item h3 { font-size: var(--text-h3); }
.trust-item p { margin-top: var(--space-1); color: var(--paper-72); font-size: var(--text-body); font-weight: var(--fw-body); }
.trust-slots {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--ink-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.trust-slot {
  font-size: var(--text-small);
  color: var(--paper-45);
  font-style: italic;
}

/* ================= FAQ ================= */
.faq-list { display: grid; gap: var(--space-2); }
.faq-item {
  background: var(--ink-2);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.faq-item summary {
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--text-body-lg);
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; float: right; color: var(--accent); }
.faq-item[open] summary::after { content: "\2212"; }
.faq-item p { margin-top: var(--space-2); color: var(--paper-72); font-weight: var(--fw-body); }

/* ================= Close ================= */
.close-section { text-align: center; }
.close-section .section-head { margin-inline: auto; text-align: center; }

/* ================= Footer ================= */
.site-footer {
  padding-block: var(--space-4);
  border-top: 1px solid var(--ink-3);
  font-size: var(--text-small);
  color: var(--paper-45);
}
.footer-inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); }
.site-footer a { color: var(--paper-72); text-decoration: underline; text-decoration-color: var(--ink-3); text-underline-offset: 3px; }
.site-footer a:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* ================= Sticky mobile click-to-call ================= */
.sticky-call {
  position: fixed;
  left: var(--space-2); right: var(--space-2); bottom: var(--space-2);
  z-index: var(--z-sticky);
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2);
  background: rgba(4, 7, 8, 0.72);
  backdrop-filter: blur(10px);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
  transform: translateY(140%);
  transition: transform var(--dur-base) var(--ease-out);
}
.sticky-call.is-visible { transform: translateY(0); }
/* Two real actions (call + text, both genuinely reachable via tel:/sms:,
   2026-07-19 Phase 2 fix -- the old single button's "Call or text" label
   only ever linked tel:, so text wasn't actually reachable) split the bar
   evenly down to 320px. Short labels ("Call now" / "Text us") instead of
   repeating the phone number keep both buttons comfortably inside a ~130px
   half-width budget at the smallest supported viewport -- the number is
   already visible in the header, hero, and footer. The dark blurred backing
   panel (added this pass) means the outline button stays legible regardless
   of what's scrolled underneath, instead of relying on page-background
   contrast the old single-filled-button version never had to deal with. */
.sticky-call-btn { flex: 1 1 0; padding-inline: 0.75rem; font-size: var(--text-small); }
@media (min-width: 768px) {
  .sticky-call { display: none; }
}
