:root {
  --blue: #1F8FE0;
  --teal: #0C9DB6;
  --red: #E43227;
  --foam: #AFDFE3;
  --navy: #1A1A2E;
  --black: #000000;
  --charcoal: #101020;
  --surface: #181830;
  --elevated: #22223f;
  --border: rgba(12, 157, 182, 0.24);
  --glow: rgba(12, 157, 182, 0.35);
  --glow-soft: rgba(12, 157, 182, 0.12);
  --glow-card-rest: 0.16;
  --glow-card-hover: 0.55;
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

* { -webkit-tap-highlight-color: transparent; }
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  background: var(--charcoal);
  color: #fff;
  font-family: 'Montserrat', sans-serif;
  line-height: 1.7;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  position: relative;
}

body::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E");
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
}

.font-display { font-family: 'Anton', 'Oswald', sans-serif; letter-spacing: -0.01em; font-weight: 700; }

a, button, input { transition: transform 0.3s var(--spring), opacity 0.3s var(--spring), box-shadow 0.3s var(--spring), background-color 0.3s var(--spring), border-color 0.3s var(--spring), color 0.3s var(--spring); }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: 4px;
}

/* eyebrow rule */
.eyebrow { display: inline-flex; align-items: center; gap: 10px; }
.eyebrow::before { content: ''; width: 24px; height: 2px; background: var(--teal); display: inline-block; }

/* card glow */
.glow-card {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 0 0 1px rgba(12,157,182,var(--glow-card-rest)) inset, 0 20px 40px -24px rgba(0,0,0,0.8), 0 0 32px -8px rgba(12,157,182,var(--glow-card-rest));
}
.glow-card:hover {
  box-shadow: 0 0 0 1px rgba(12,157,182,var(--glow-card-hover)) inset, 0 24px 48px -20px rgba(0,0,0,0.85), 0 0 40px -4px rgba(12,157,182,var(--glow-card-hover));
  transform: translateY(-4px);
}

.btn-primary { box-shadow: 0 1px 1px rgba(0,0,0,0.3), 0 10px 24px -8px rgba(31,143,224,0.55); }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 1px 1px rgba(0,0,0,0.35), 0 16px 32px -8px rgba(31,143,224,0.65); }
.btn-primary:active { transform: translateY(0); }

.btn-outline:hover { background: rgba(12,157,182,0.08); border-color: var(--teal); transform: translateY(-2px); }
.btn-outline:active { transform: translateY(0); }

.nav-link { position: relative; }
.nav-link::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -6px;
  height: 2px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--spring);
}
.nav-link:hover::after { transform: scaleX(1); }
.nav-link.is-active::after { transform: scaleX(1); }

.dropdown-panel {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity 0.25s var(--spring), transform 0.25s var(--spring);
}
.dropdown-wrap:hover .dropdown-panel,
.dropdown-wrap:focus-within .dropdown-panel {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.pill-select {
  transition: background-color 0.25s var(--spring), color 0.25s var(--spring), border-color 0.25s var(--spring);
}

.marquee-track { animation: marquee 28s linear infinite; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } }

.ig-gradient {
  background: linear-gradient(90deg, #833ab4, #fd1d1d 55%, #fcb045);
}
.ig-gradient:hover { transform: translateY(-2px) scale(1.015); box-shadow: 0 12px 28px -8px rgba(253,29,29,0.45); }
.ig-gradient:focus-visible { outline: 2px solid #fcb045; outline-offset: 3px; }
.ig-gradient:active { transform: translateY(0) scale(0.99); }

.service-card { position: relative; overflow: hidden; }
.service-card img { transition: transform 0.6s var(--spring); }
.service-card:hover img { transform: scale(1.06); }
/* Tuned for the 280px card height. These were written for cards more than
   twice as tall, so the same percentage stops buried almost the whole photo
   once the cards shrank. The dark band now covers only the text area. */
.service-card .overlay {
  background: linear-gradient(180deg, rgba(10,15,30,0) 0%, rgba(10,15,30,0.08) 45%, rgba(16,16,32,0.55) 100%);
  mix-blend-mode: multiply;
}
.service-card .overlay2 {
  background: linear-gradient(0deg, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.72) 28%, rgba(0,0,0,0) 62%);
}

.popular-card {
  box-shadow: 0 0 0 1px rgba(31,143,224,0.55) inset, 0 0 60px -10px rgba(31,143,224,0.45);
}

/* Seasonal nav pill. Holiday lighting is the one service with its own brand
   and domain, and burying it as the twelfth item in the Services dropdown
   hides it during the only three months it sells. Green-to-gold reads as
   Christmas without putting a literal red button in a teal nav.
   SEASONAL: remove this link from HEADER in site-chrome.mjs and from the
   homepage nav in January — the styles can stay, they cost nothing unused. */
.nav-holiday {
  position: relative;
  display: inline-flex;
  align-items: center;
  border: 1px solid rgba(255, 217, 138, 0.42);
  border-radius: 9999px;
  padding: 8px 18px;
  font-weight: 800;
  letter-spacing: 0.04em;
  white-space: nowrap;
  background: rgba(35, 200, 128, 0.07);
  /* Gradient runs through the text itself, so the label carries the colour
     rather than sitting on a filled block that would shout louder than the
     phone number beside it. */
  background-image: linear-gradient(90deg, #23C880 0%, #7FD98E 45%, #FFD98A 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #FFD98A;
  box-shadow: 0 0 22px -6px rgba(255, 200, 90, 0.45);
  transition: transform 0.3s var(--spring), box-shadow 0.3s var(--spring), border-color 0.3s var(--spring);
}
.nav-holiday:hover {
  transform: translateY(-1px);
  border-color: rgba(255, 217, 138, 0.85);
  box-shadow: 0 0 30px -4px rgba(255, 200, 90, 0.7);
}
.nav-holiday:active { transform: translateY(0); }
/* The background-clip trick makes the pill's own fill transparent too, so the
   tint has to be painted behind the text rather than on the same element. */
.nav-holiday::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  background: rgba(35, 200, 128, 0.09);
  z-index: -1;
}

/* Signature service banner — the full-width card that closes the services
   grid. It deliberately breaks the grid's rhythm: the eleven cards above it
   are interchangeable 280px tiles, and this one is meant to stop the eye.
   Carries the Emerald Christmas palette rather than site teal, because it
   points at the holiday brand and should not read as another cleaning card. */
.signature-card {
  position: relative;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(35,200,128,0.45) inset, 0 24px 60px -30px rgba(0,0,0,0.9), 0 0 60px -18px rgba(35,200,128,0.35);
  transition: transform 0.4s var(--spring), box-shadow 0.4s var(--spring);
}
.signature-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(35,200,128,0.75) inset, 0 30px 70px -28px rgba(0,0,0,0.95), 0 0 80px -14px rgba(35,200,128,0.5);
}
.signature-card img { transition: transform 0.7s var(--spring); }
.signature-card:hover img { transform: scale(1.04); }
/* Reads left-to-right so the copy sits on near-solid ground while the lit
   roofline on the right stays visible. */
.signature-wash {
  background:
    linear-gradient(90deg, rgba(4,21,14,0.96) 0%, rgba(4,21,14,0.88) 34%, rgba(4,21,14,0.45) 62%, rgba(4,21,14,0.15) 100%),
    linear-gradient(0deg, rgba(4,21,14,0.55) 0%, rgba(4,21,14,0) 55%);
}
/* A left-to-right wash does nothing on a narrow card — the copy ends up over
   the lit house and stops being readable. Below the two-column breakpoint the
   whole photo is dimmed instead, so it reads as texture behind the text. */
@media (max-width: 900px) {
  .signature-wash {
    background: linear-gradient(180deg, rgba(4,21,14,0.92) 0%, rgba(4,21,14,0.85) 55%, rgba(4,21,14,0.78) 100%);
  }
}
.signature-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  border: 1px solid rgba(35,200,128,0.55);
  border-radius: 9999px;
  padding: 7px 16px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #23C880;
  background: rgba(35,200,128,0.08);
}
.signature-badge::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #23C880;
  box-shadow: 0 0 10px rgba(35,200,128,0.9);
}
.signature-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border-radius: 9999px;
  padding: 14px 26px;
  /* At 390px the label is a few pixels too wide and breaks onto a second line,
     stranding the arrow. Hold it on one line and trim the label to fit. */
  white-space: nowrap;
  background: var(--red);
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  box-shadow: 0 1px 0 rgba(255,255,255,0.2) inset, 0 12px 26px -12px rgba(228,50,39,0.85);
  transition: transform 0.3s var(--spring), box-shadow 0.3s var(--spring);
}
.signature-card:hover .signature-cta {
  transform: translateY(-2px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.2) inset, 0 18px 34px -14px rgba(228,50,39,0.95);
}
@media (max-width: 480px) {
  .signature-cta { padding: 13px 20px; font-size: 12px; gap: 8px; letter-spacing: 0.05em; }
}

/* before/after slider */
.ba-wrap { position: relative; overflow: hidden; user-select: none; touch-action: none; }
.ba-after { position: absolute; inset: 0; overflow: hidden; }
.ba-handle {
  position: absolute; top: 0; bottom: 0; width: 2px;
  background: var(--foam);
  box-shadow: 0 0 12px 2px rgba(175,223,227,0.6);
  transform: translateX(-50%);
  cursor: ew-resize;
}
.ba-grip {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 40px; height: 40px; border-radius: 9999px;
  background: var(--foam);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 16px rgba(0,0,0,0.5);
}

.checklist li { display: flex; align-items: flex-start; gap: 10px; }

input::placeholder { color: rgba(255,255,255,0.35); }

.mobile-menu { transition: max-height 0.35s var(--spring), opacity 0.3s var(--spring); overflow: hidden; }

/* Collapsible groups in the mobile menu. Sixteen services listed flat made the
   menu a wall of text you had to scroll past to reach Contact; grouping them
   behind Services and Commercial takes it to seven rows. Built on <details> so
   it works with no JavaScript — the same pattern the FAQ accordions use. */
.mobile-group summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
}
.mobile-group summary::-webkit-details-marker { display: none; }
.mobile-group summary::after {
  content: '';
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.3s var(--spring);
  opacity: 0.6;
  flex: none;
}
.mobile-group[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.mobile-sub {
  display: flex;
  flex-direction: column;
  padding: 2px 0 10px 14px;
  margin-left: 1px;
  border-left: 1px solid var(--border);
}
.mobile-sub a {
  padding: 9px 0;
  font-size: 13px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.68);
}
.mobile-sub a:hover { color: #fff; }
.mobile-link { padding: 11px 0; }

/* Hamburger folds into an X while the menu is open. The bars are 2px tall with
   a 6px gap, so 8px of travel puts the outer two exactly on the centre line. */
#menuToggle span { transition: transform 0.3s var(--spring), opacity 0.2s ease; }
#menuToggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
#menuToggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
#menuToggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ===== service landing page components ===== */

/* step card numbering (real sequence: quote -> schedule -> clean -> report) */
.step-num {
  font-family: 'Anton', 'Oswald', sans-serif;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* FAQ accordion */
.faq-item { border: 1px solid var(--border); background: var(--surface); }
.faq-question {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.faq-question::-webkit-details-marker { display: none; }
.faq-icon { transition: transform 0.3s var(--spring); flex-shrink: 0; }
details[open] .faq-icon { transform: rotate(45deg); }
.faq-answer {
  color: rgba(255,255,255,0.65);
}

/* feature/value tiles used on service pages */
.feature-tile {
  background: var(--surface);
  border: 1px solid var(--border);
}

/* related service cards reuse .glow-card + .service-card */

/* <picture> is only a wrapper for source selection — it must not participate in
   layout, or it breaks the absolutely-positioned image slots it sits inside. */
picture { display: contents; }

/* Pill state lives in CSS, not inline styles. The old JS painted inline styles
   per-pill on click, so a de-selected pill kept its highlight until it was
   clicked again — two pills could look selected at once. */
.pill-select {
  background: var(--elevated);
  color: rgba(255, 255, 255, 0.75);
  border-color: rgba(255, 255, 255, 0.12);
}
.pill-select:hover { border-color: rgba(12, 157, 182, 0.5); }
.pill-select.is-active {
  background: var(--blue);
  color: #fff;
  border-color: var(--blue);
}

/* Service cards follow goaclean's model: the service NAME is always visible,
   and the description + "View Service" expand on hover. Values below mirror
   theirs (.svc-desc max-height 0 -> 120px, .svc-cta opacity 0 -> 1, and the
   scrim deepening underneath so the longer text stays legible).
   This also settles the phone case correctly: with no hover on touch, a card
   shows its name and nothing else, rather than a full paragraph over a photo. */
.service-card .svc-desc {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transform: translateY(6px);
  transition: max-height 0.4s ease, opacity 0.35s ease 0.05s, transform 0.35s ease 0.05s;
}
.service-card .svc-cta {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.3s ease 0.1s, transform 0.3s ease 0.1s;
}
/* Rest scrim is strong enough to carry the always-visible name, and rises when
   the description appears. The gradient clears by 62% height, so the top of the
   photo still reads at rest. */
.service-card .overlay2 { opacity: 0.85; transition: opacity 0.4s ease; }

/* Keyboard focus expands the card on ANY device — tabbing the grid should reach
   the same content a mouse does. Taps don't match :focus-visible, so ordinary
   touch use is unaffected. */
.service-card:focus-visible .svc-desc { max-height: 120px; opacity: 1; transform: none; }
.service-card:focus-visible .svc-cta { opacity: 1; transform: none; }
.service-card:focus-visible .overlay2 { opacity: 1; }

@media (hover: hover) and (pointer: fine) {
  .service-card:hover .svc-desc { max-height: 120px; opacity: 1; transform: none; }
  .service-card:hover .svc-cta { opacity: 1; transform: none; }
  .service-card:hover .overlay2 { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .service-card .svc-desc,
  .service-card .svc-cta { transition: none; transform: none; }
}

/* Quote form — name, phone, email and address are all required.
   :user-invalid matches only AFTER the visitor has edited the field or tried to
   submit, so nothing is tinted red on page load the way :invalid would be.
   Browsers without :user-invalid still enforce `required`; they just skip the
   tint. The #id carries enough specificity to beat Tailwind's focus:border-teal. */
#quoteForm input:user-invalid,
#quoteForm input:user-invalid:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(228, 50, 39, 0.14);
}
