/* ==========================================================================
   The Handy Butler Home Services LLC
   --------------------------------------------------------------------------
   Design read: local home-services landing page for Bay Area homeowners
   deciding who to let into their house. Trust-first premium-trades language,
   built to echo the business card: white stock, heavy black type, one
   illustrated butler carrying the colour.

   Dials: DESIGN_VARIANCE 6 / MOTION_INTENSITY 5 / VISUAL_DENSITY 4

   THEME: locked light, on the client's instruction. There is deliberately no
   prefers-color-scheme block. `color-scheme: light` below stops browsers
   auto-darkening form controls, which is what breaks most "light only" pages.

   SHAPE SYSTEM (locked): buttons/inputs/chips = --r-sm, cards/images = --r-lg
   COLOR SYSTEM (locked): one accent, Evergreen. Nothing else is ever accent.
   Z-INDEX SCALE: nav 100 / sticky call bar 90 / skip link 200. Nothing else.
   ========================================================================== */

/* ---------- 1. Tokens ---------- */

:root {
  color-scheme: light;

  /* Brand palette, taken off the card: white stock, near-black ink. */
  --ink:            #14181A;
  --white:          #FFFFFF;
  --bone:           #F7F6F3;
  --evergreen:      #1F6B4C;
  --evergreen-deep: #164E37;
  --slate:          #5A6570;
  --hairline:       #E6E4DE;
  --signal:         #B4472C;

  --bg:           var(--white);
  --bg-alt:       var(--bone);
  --surface:      var(--white);
  --text:         var(--ink);
  --text-muted:   var(--slate);
  --accent:       var(--evergreen);
  --accent-hover: var(--evergreen-deep);
  --accent-ink:   var(--white);
  --accent-wash:  #EAF2ED;
  --rule:         var(--hairline);

  --r-sm: 10px;
  --r-lg: 18px;

  /* Shadows tinted to the background hue, never pure black. */
  --shadow-sm: 0 1px 2px rgba(20, 24, 26, .05), 0 2px 6px rgba(20, 24, 26, .04);
  --shadow-md: 0 2px 4px rgba(20, 24, 26, .04), 0 14px 32px rgba(20, 24, 26, .07);

  --font-display: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body: "Geist", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --gutter: clamp(1.25rem, 5vw, 2.5rem);
  --measure: 62ch;
  --section-y: clamp(4rem, 8vw, 7rem);

  --ease: cubic-bezier(.16, 1, .3, 1);
}

/* ---------- 2. Reset and base ---------- */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1.0625rem;          /* 17px floor, this audience skews older */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* MUST be `clip`, not `hidden`. `overflow-x: hidden` makes <body> a scroll
     container, and `position: sticky` on a child of a scroll container sticks to
     that container rather than the viewport -- which silently stopped the navbar
     sticking at all. `clip` contains the same horizontal overflow WITHOUT
     creating a scroll container, so the header sticks. */
  overflow-x: clip;
}
@supports not (overflow-x: clip) { body { overflow-x: hidden; } }

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  /* Loosened from -.03em when the display face changed from Bricolage Grotesque
     (condensed) to Archivo (wider, more open). The old value crowded Archivo's
     letterforms, most visibly in the hero headline. */
  letter-spacing: -.02em;
  line-height: 1.06;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(2.35rem, 4.9vw, 3.55rem); }
h2 { font-size: clamp(1.9rem, 4.1vw, 2.9rem); }
h3 { font-size: clamp(1.15rem, 1.8vw, 1.35rem); letter-spacing: -.02em; line-height: 1.2; }

p { margin: 0; max-width: var(--measure); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }

ul { margin: 0; padding: 0; list-style: none; }

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

.skip-link {
  position: absolute; left: 50%; top: 0; transform: translate(-50%, -120%);
  z-index: 200; background: var(--ink); color: var(--white);
  padding: .75rem 1.25rem; border-radius: 0 0 var(--r-sm) var(--r-sm);
  font-weight: 600; text-decoration: none;
}
.skip-link:focus-visible { transform: translate(-50%, 0); }

/* ---------- 3. Layout primitives ---------- */

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

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

.section-head { margin-bottom: clamp(2rem, 3.5vw, 3rem); }
.section-head p { margin-top: .875rem; color: var(--text-muted); }

/* Eyebrow. 10 sections, so the budget is 3. Used on hero, services, reviews. */
.eyebrow {
  font-family: var(--font-body); font-size: .75rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--accent);
  margin: 0 0 .875rem;
}

/* ---------- 4. Buttons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  white-space: nowrap;                 /* CTA labels never wrap */
  font-family: var(--font-body); font-size: 1rem; font-weight: 600; line-height: 1;
  padding: 1.0625rem 1.75rem;
  border-radius: var(--r-sm); border: 1.5px solid transparent;
  text-decoration: none; cursor: pointer;
  transition: background-color .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .12s var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn i { font-size: 1.15em; }

.btn--primary { background: var(--accent); color: var(--accent-ink); }  /* 5.9:1 */
.btn--primary:hover { background: var(--accent-hover); }

.btn--secondary { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn--secondary:hover { background: var(--ink); color: var(--white); }

.btn--block { width: 100%; }
.cta-row { display: flex; flex-wrap: wrap; gap: .875rem; }

/* ---------- 5. Navigation ---------- */

.nav {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, .88);
  backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.nav[data-stuck="true"] { border-bottom-color: var(--rule); box-shadow: var(--shadow-sm); }
@supports not (backdrop-filter: blur(1px)) { .nav { background: var(--white); } }

.nav__inner { height: 76px; display: flex; align-items: center; gap: 1.5rem; }

.brand { display: flex; align-items: center; gap: .625rem; text-decoration: none; color: var(--ink); margin-right: auto; flex-shrink: 0; }
/* Jacob's logo is a portrait illustration (336x400), not a square mark. Pin the
   HEIGHT and let width follow, otherwise the square box squashes him. */
.brand img { width: auto; height: 46px; flex-shrink: 0; }
.brand__name { font-family: var(--font-display); font-weight: 700; font-size: 1.125rem; letter-spacing: -.025em; line-height: 1; display: block; }
.brand__sub { display: block; font-size: .5625rem; font-weight: 500; letter-spacing: .22em; color: var(--text-muted); margin-top: .3rem; }

.nav__links { display: flex; align-items: center; gap: 1.6rem; }
.nav__links a { color: var(--ink); text-decoration: none; font-size: .9375rem; font-weight: 500; transition: color .18s var(--ease); }
.nav__links a:hover { color: var(--accent); }

.nav__phone { display: inline-flex; align-items: center; gap: .5rem; color: var(--ink); text-decoration: none; font-weight: 600; font-size: .9375rem; font-variant-numeric: tabular-nums; }
.nav__phone:hover { color: var(--accent); }
.nav__cta { padding: .8125rem 1.25rem; font-size: .9375rem; }

.nav__toggle { display: none; background: none; border: 0; padding: .5rem; margin-right: -.5rem; color: var(--ink); font-size: 1.65rem; cursor: pointer; line-height: 1; }

@media (max-width: 1040px) { .nav__links { display: none; } .nav__toggle { display: block; } }
@media (max-width: 620px) { .nav__phone, .nav__cta { display: none; } }

.nav__drawer { display: none; padding: .5rem 0 1.5rem; border-top: 1px solid var(--rule); }
.nav__drawer[data-open="true"] { display: block; }
/* Scoped to :not(.btn) so the drawer's link styling cannot outrank the button
   styling. `.nav__drawer a` is specificity (0,1,1) and `.btn--primary` is
   (0,1,0), so without this the green call button in the phone menu
   inherited ink-coloured text and rendered black on green. */
.nav__drawer a:not(.btn) {
  display: block; padding: .9rem .25rem; color: var(--ink);
  text-decoration: none; font-size: 1.0625rem; font-weight: 500;
  border-bottom: 1px solid var(--rule);
}
.nav__drawer .btn { margin-top: 1.25rem; }
.nav__drawer .btn--primary { color: var(--accent-ink); }

/* ---------- 6. Hero ----------
   The hero now lives inside scroll-world as its first full-screen scene. */

/* ---------- 7. Trust strip ---------- */

.trust { border-block: 1px solid var(--rule); background: var(--bg-alt); padding-block: clamp(1.5rem, 3vw, 2.25rem); }
.trust__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(1.25rem, 3vw, 2.5rem); }
.trust__item { display: flex; align-items: flex-start; gap: .75rem; }
.trust__item i { color: var(--accent); font-size: 1.5rem; line-height: 1; flex-shrink: 0; }
.trust__item strong { display: block; font-family: var(--font-display); font-size: 1.0625rem; font-weight: 700; letter-spacing: -.02em; line-height: 1.25; }
.trust__item > div > span { display: block; font-size: .875rem; color: var(--text-muted); line-height: 1.4; }
.stars { color: var(--accent); font-size: .95rem; letter-spacing: .06em; line-height: 1; }

@media (max-width: 860px) { .trust__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .trust__grid { grid-template-columns: 1fr; } }

/* ---------- 8. Services bento ---------- */

.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1rem; }

.tile {
  border: 1px solid var(--rule); border-radius: var(--r-lg);
  padding: clamp(1.35rem, 2.2vw, 1.85rem); background: var(--surface);
  display: flex; flex-direction: column; gap: .5rem; grid-column: span 2;
}
.tile i { color: var(--accent); font-size: 1.7rem; line-height: 1; }
.tile p { color: var(--text-muted); font-size: .9375rem; }

.tile--wide { grid-column: span 4; }
.tile--photo { padding: 0; overflow: hidden; border: 0; min-height: 16rem; position: relative; justify-content: flex-end; }
.tile--photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.tile--photo .tile__caption {
  position: relative; color: var(--white);
  padding: 5rem clamp(1.35rem, 2.2vw, 1.85rem) clamp(1.35rem, 2.2vw, 1.85rem);
  background: linear-gradient(to top, rgba(8, 11, 12, .92) 0%, rgba(8, 11, 12, .82) 45%, rgba(8, 11, 12, 0) 100%);
}
.tile--photo h3 { color: var(--white); }
.tile--photo p { color: #DCE2E4; }

.tile--wash { background: var(--accent-wash); border-color: transparent; }

/* Solid accent panel. Replaces the old ink panel so the page stays light. */
.tile--accent { background: var(--accent); border-color: transparent; color: var(--white); }
.tile--accent h3 { color: var(--white); }
.tile--accent p { color: #D5E7DD; }
.tile--accent i { color: var(--white); }

@media (max-width: 900px) {
  .bento { grid-template-columns: repeat(4, 1fr); }
  .tile, .tile--wide { grid-column: span 2; }
  .tile--photo { grid-column: span 4; }
}
@media (max-width: 600px) {
  .bento { grid-template-columns: 1fr; }
  .tile, .tile--wide, .tile--photo { grid-column: span 1; }
}

/* ---------- 9. Gallery ---------- */

.gallery { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
.shot { position: relative; border-radius: var(--r-lg); overflow: hidden; background: var(--bone); grid-column: span 1; margin: 0; }
.shot img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 3 / 2; transition: transform .5s var(--ease); }
.shot--tall { grid-column: span 2; grid-row: span 2; }
.shot--tall img { aspect-ratio: 1 / 1; }
.shot--wide { grid-column: span 2; }
@media (hover: hover) { .shot:hover img { transform: scale(1.03); } }

.shot figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 3.5rem 1.125rem 1.125rem; color: var(--white);
  font-size: .875rem; font-weight: 500;
  background: linear-gradient(to top, rgba(8, 11, 12, .88), rgba(8, 11, 12, 0));
}

@media (max-width: 900px) {
  .gallery { grid-template-columns: repeat(2, 1fr); }
  .shot--tall, .shot--wide { grid-column: span 2; }
  .shot--tall { grid-row: auto; }
  .shot--tall img { aspect-ratio: 3 / 2; }
}
@media (max-width: 520px) {
  .gallery { grid-template-columns: 1fr; }
  .shot, .shot--tall, .shot--wide { grid-column: span 1; }
}

/* ---------- 10. About ---------- */

.about__grid { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(2rem, 4.5vw, 4rem); align-items: center; }
.about__portrait img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; border-radius: var(--r-lg); }
.about__body p + p { margin-top: 1.125rem; }
.about__body p { color: var(--text-muted); }
.about__sig { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); display: flex; align-items: center; gap: .875rem; }
.about__sig img { width: auto; height: 44px; flex-shrink: 0; }
.about__sig strong { display: block; font-family: var(--font-display); font-size: 1.0625rem; letter-spacing: -.02em; line-height: 1.3; }
.about__sig span { display: block; color: var(--text-muted); font-size: .9375rem; line-height: 1.35; }

@media (max-width: 900px) { .about__grid { grid-template-columns: 1fr; } .about__portrait img { aspect-ratio: 3 / 2; } }

/* ---------- 11. Reviews ---------- */

.reviews__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1.5rem; margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem); }
.reviews__score { display: flex; align-items: center; gap: .875rem; }
.reviews__score b { font-family: var(--font-display); font-size: 2.5rem; font-weight: 700; letter-spacing: -.025em; line-height: 1; }
.reviews__score > span { font-size: .9375rem; color: var(--text-muted); line-height: 1.35; }

.rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(18rem, 22rem);
  gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: 1rem; margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
  scrollbar-width: thin;
}
.rail > * { scroll-snap-align: start; }

.review { border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--surface); padding: 1.6rem; display: flex; flex-direction: column; gap: .875rem; }
.review blockquote { margin: 0; font-size: 1rem; line-height: 1.5; color: var(--text); }
.review__who { margin-top: auto; display: flex; align-items: center; gap: .75rem; }

/* The avatar and the meta text are separate concerns. Scoping the meta rules to
   .review__meta keeps them from outranking .review__avatar and flattening its
   grid centring, which is what pushed the initials off-centre before. */
.review__avatar {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  background: var(--accent-wash); color: var(--evergreen-deep);
  display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 700;
  font-size: .875rem; line-height: 1; letter-spacing: 0;
}
.review__meta strong { display: block; font-size: .9375rem; font-weight: 600; line-height: 1.3; }
.review__meta span { display: block; font-size: .8125rem; color: var(--text-muted); line-height: 1.3; }

.reviews__note { margin-top: 1.25rem; font-size: .8125rem; color: var(--text-muted); }
.reviews__note a { color: var(--evergreen-deep); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }

/* ---------- 11a. Home maintenance ---------- */

.maintenance-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.maintenance-card { border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--surface); padding: clamp(1.35rem, 2.5vw, 1.8rem); }
.maintenance-card > i { display: inline-grid; place-items: center; width: 42px; height: 42px; margin-bottom: 1rem; border-radius: 50%; background: var(--accent-wash); color: var(--evergreen-deep); font-size: 1.35rem; }
.maintenance-card h3 { margin-bottom: .8rem; }
.maintenance-card ul { display: grid; gap: .65rem; }
.maintenance-card li { position: relative; padding-left: 1.1rem; color: var(--text-muted); font-size: .925rem; line-height: 1.45; }
.maintenance-card li::before { content: ""; position: absolute; left: 0; top: .57em; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.maintenance-note { margin-top: 1.2rem; color: var(--text-muted); font-size: .8rem; }
@media (max-width: 860px) { .maintenance-grid { grid-template-columns: 1fr; } }

/* ---------- 12. Process ---------- */

.process { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 3rem); }
.process__step { padding-top: 1.25rem; border-top: 2px solid var(--accent); }
.process__step h3 { margin-bottom: .5rem; }
.process__step p { color: var(--text-muted); font-size: .9375rem; }
@media (max-width: 760px) { .process { grid-template-columns: 1fr; gap: 1.75rem; } }

/* ---------- 13. Service area ---------- */

.areas__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: clamp(2rem, 4vw, 4rem); align-items: start; }
.area-group + .area-group { margin-top: 1.5rem; }
.area-group h3 { font-size: .9375rem; margin-bottom: .75rem; }
.chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.chip { border: 1px solid var(--rule); border-radius: var(--r-sm); padding: .5rem .875rem; font-size: .875rem; color: var(--text-muted); background: var(--surface); }
@media (max-width: 860px) { .areas__grid { grid-template-columns: 1fr; } }

/* ---------- 14. Closing CTA + form ---------- */

.close__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(2rem, 4.5vw, 4rem); align-items: start; }
.close__aside .cta-row { margin-top: 1.75rem; }
.close__hours { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--rule); display: grid; gap: .5rem; font-size: .9375rem; color: var(--text-muted); }
.close__hours div { display: flex; justify-content: space-between; gap: 1rem; max-width: 20rem; }

.form { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--r-lg); padding: clamp(1.35rem, 2.6vw, 2rem); display: grid; gap: 1rem; box-shadow: var(--shadow-sm); }
.form h3 { margin-bottom: -.25rem; }
.form__hint { font-size: .875rem; color: var(--text-muted); }

/* Every in-page link (the nav, "Get a Free Estimate", the footer) jumps to an
   anchor, and the navbar is sticky at ~77px. Without this the target's heading
   lands UNDERNEATH the bar and the section looks like it starts mid-sentence.
   scroll-margin-top offsets the scroll position, not the layout. */
section[id],
#estimateForm { scroll-margin-top: 5.75rem; }

.field { display: grid; gap: .4375rem; }
.field label { font-size: .875rem; font-weight: 600; color: var(--ink); }
.field input, .field textarea {
  font-family: var(--font-body); font-size: 1rem; color: var(--ink); background: var(--white);
  border: 1.5px solid var(--rule); border-radius: var(--r-sm); padding: .8125rem .9375rem; width: 100%;
  transition: border-color .18s var(--ease);
}
.field textarea { resize: vertical; min-height: 6rem; }
.field input::placeholder, .field textarea::placeholder { color: var(--text-muted); opacity: 1; }
.field input:hover, .field textarea:hover { border-color: var(--text-muted); }
.field input:focus-visible, .field textarea:focus-visible { outline: 3px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.field__error { font-size: .8125rem; color: var(--signal); font-weight: 500; display: none; }
.field[data-invalid="true"] input, .field[data-invalid="true"] textarea { border-color: var(--signal); }
.field[data-invalid="true"] .field__error { display: block; }

.field--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 560px) { .field--pair { grid-template-columns: 1fr; } }

.form__status { font-size: .9375rem; font-weight: 500; padding: .875rem 1rem; border-radius: var(--r-sm); display: none; }
.form__status[data-state="success"] { display: block; background: var(--accent-wash); color: var(--evergreen-deep); }
.form__status[data-state="error"] { display: block; background: #FBEAE5; color: #8A3421; }
.form__status[data-state="loading"] { display: block; background: var(--bg-alt); color: var(--text-muted); }

/* ---- Estimate form fallback ----
   `mailto:` only opens a mail app; the visitor still has to press Send, and on
   desktop webmail nothing may open at all. This panel appears after a submit so
   a lead is never silently lost: the exact message to copy, the address, and the
   phone number. Hidden via the `hidden` attribute until JS reveals it. */
.form__fallback {
  margin-top: 1rem;
  padding: 1.125rem;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--bg-alt);
  display: grid;
  gap: .875rem;
}
.form__fallback[hidden] { display: none; }
.form__fallback-lead { font-size: .9375rem; line-height: 1.55; color: var(--text); margin: 0; }
.form__fallback-lead a { color: var(--accent); font-weight: 600; }
.form__fallback-text {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .8125rem;
  line-height: 1.5;
  padding: .75rem;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--white);
  color: var(--ink);
  resize: vertical;
}
.form__fallback-actions { display: flex; flex-wrap: wrap; gap: .625rem; }
.form__fallback-actions .btn { flex: 1; min-width: 12rem; }

/* Label kept for screen readers without showing it. */
.u-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 900px) { .close__grid { grid-template-columns: 1fr; } }

/* ---------- 15. Footer ---------- */

.footer { background: var(--bg-alt); border-top: 1px solid var(--rule); padding-block: 3rem 2.25rem; }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2.5rem; padding-bottom: 2.25rem; border-bottom: 1px solid var(--rule); }
.footer__grid h4 { font-family: var(--font-body); font-size: .75rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); margin-bottom: .875rem; }
.footer__grid li + li { margin-top: .5rem; }
.footer__grid a { color: var(--ink); text-decoration: none; font-size: .9375rem; }
.footer__grid a:hover { color: var(--accent); text-decoration: underline; }
.footer__blurb { color: var(--text-muted); font-size: .9375rem; margin-top: .875rem; }
.footer__bottom { padding-top: 1.5rem; display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between; font-size: .8125rem; color: var(--text-muted); }
@media (max-width: 760px) { .footer__grid { grid-template-columns: 1fr; gap: 2rem; } }

/* ---------- 16. Sticky mobile call bar ---------- */

.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  display: none; gap: .625rem; padding: .625rem;
  padding-bottom: calc(.625rem + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, .94); backdrop-filter: blur(14px);
  border-top: 1px solid var(--rule);
}
.callbar .btn { padding: .9375rem .625rem; font-size: .875rem; }
.callbar .btn--primary { flex: 1.15; }
.callbar .btn--secondary { flex: 1; }
@media (max-width: 359px) { .callbar .btn { font-size: .8125rem; } }
@media (max-width: 760px) { .callbar { display: flex; } body { padding-bottom: 4.75rem; } #film { --callbar-h: 4.75rem; } }

/* Live backdrop blur forces the browser to re-sample the scrubbed video behind
   these fixed bars on every frame. An opaque mobile surface looks the same here
   and keeps the video on its own compositor layer. */
@media (max-width: 860px) {
  .nav,
  .callbar { backdrop-filter: none; -webkit-backdrop-filter: none; background: #fff; }
}

/* ---------- 17. Scroll-world film ---------- */

/* Theme the vendored scroll-world engine to the brand. It reads these off
   the container it is mounted on. */
#film {
  /* Phone film layout, shared by the media band and the copy beneath it so the
     two can never drift apart. */
  --band-top: 13vh;
  --band-h: min(40vh, 70vw);
  --callbar-h: 0px;
  --sw-bg: var(--white);
  --sw-ink: var(--ink);
  --sw-ink-soft: var(--slate);
  --sw-accent: var(--evergreen);
  --sw-font-display: var(--font-display);
  --sw-font-body: var(--font-body);
}

/* Landing copy: the user asked for exactly the eyebrow, headline and two
   actions. Remove the scene counter and make this read as a proper hero. */
#film .sw-copy--hero .sw-copy__num { display: none; }
#film .sw-copy--hero { width: min(52vw, 650px); }
#film .sw-copy--hero .sw-copy__title {
  max-width: 13ch;
  font-size: clamp(3rem, 5vw, 4.75rem);
  line-height: .96;
  letter-spacing: -.03em;
}
#film .sw-copy--hero .sw-copy__title em {
  color: var(--accent);
  /* <em> renders italic by default in every browser, so this has to be an
     explicit `normal` — deleting the old `font-style: italic` would not have
     been enough. The accent colour carries the emphasis on its own. */
  font-style: normal;
}
/* The hero's body line is social proof, not prose - small, muted and quiet,
   matching .reviews__note rather than the large body copy the repair sections
   use. The stars carry the colour so the text itself stays understated. */
#film .sw-copy--hero .sw-copy__body {
  font-size: .875rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .01em;
  margin-top: 1rem;
  max-width: none;
}

#film .sw-copy--hero .sw-copy__cta { margin-top: 2rem; }

@media (max-width: 860px) {
  #film .sw-copy--hero { width: auto; }
  #film .sw-copy--hero .sw-copy__title {
    /* 13ch, not 11ch. `ch` is the width of the font's own "0", and Archivo is a
       wider face than the Bricolage Grotesque this was set for, so 11ch no
       longer fit "The handyman" and the headline broke onto four lines instead
       of three. Tuned to hold the intended three-line break. */
    max-width: 13ch;
    font-size: clamp(2.35rem, 9.6vw, 4rem);
    line-height: .98;
  }
  #film .sw-copy--hero .sw-copy__cta {
    display: grid;
    grid-template-columns: 1fr;
    gap: .65rem;
    margin-top: 1.35rem;
  }
}

/* The engine ships its own button styling: a pill, and a primary whose label
   inherits the page link colour, landing evergreen-on-ink at about 2.1:1.
   Override both so the film's CTAs match the rest of the site and pass AA. */
#film .sw-btn {
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 1rem 1.6rem;
  border: 1.5px solid transparent;
}
#film .sw-btn--primary { background: var(--accent); color: var(--white); }   /* 5.9:1 */
#film .sw-btn--primary:hover { background: var(--accent-hover); }
#film .sw-btn--ghost { background: var(--white); color: var(--ink); border-color: var(--ink); }
#film .sw-btn--ghost:hover { background: var(--ink); color: var(--white); }

/* The footage is bright, and the engine lays its copy straight onto it with no
   backdrop. This scrim guarantees the headline stays legible on the palest
   frames. It follows the copy: left column on desktop, bottom on phones, which
   is where the engine moves it at its own 860px breakpoint. */
#film .sw-copylayer::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to right,
    rgba(255, 255, 255, .94) 0%, rgba(255, 255, 255, .78) 30%, rgba(255, 255, 255, 0) 60%);
}
@media (max-width: 860px) {
  /* No scrim on phones any more. The footage is a band and everything around it
     is plain white (see below), so the copy sits on white and needs no help --
     and the old scrim was what produced the mid-screen white seam. */
  #film .sw-copylayer::before { content: none; }
}

/* Chrome removed at the client's request: the dot rail, the progress bar and
   the scroll hint. Just the footage and the copy. */
#film .sw-route,
#film .sw-scrollbar,
#film .sw-hint { display: none !important; }

/* The hard boundary set by the engine patch. Outside its own block the film
   stops painting and stops taking hit tests, so it can never sit over the
   services, gallery or reviews. */
#film.sw-idle .sw-stage,
#film.sw-idle .sw-copylayer,
#film.sw-idle .sw-sky { visibility: hidden; pointer-events: none; }

/* .sw-sky is a fixed backdrop at inset:0 filled with an opaque page-coloured
   background. Because the scenes fade in and out at the film's edges while the
   sky stays solid, it showed as a full screen of flat white on the way in and
   on the way out. Nothing needs it: each scene is opaque video that already
   covers the viewport. Making it transparent lets the hero cross-dissolve
   into the first scene, and the last scene into the services section. */
#film .sw-sky { background: transparent !important; }


/* The engine slowly scales the poster still from 1.03 up to 1.17 as a ken-burns
   effect. Against a video that is NOT scaled, that reads as the film starting
   zoomed in and then popping to normal once the clip loads. Pin the poster to
   the video's own framing instead. */
#film .sw-scene__still { transform: none !important; }

/* The global reset's `img { height: auto }` was beating the engine's
   `height: 100%`, so each poster still rendered at its natural aspect height
   (a 188px strip at the top of a full-height stage) instead of filling it.
   That left a hard horizontal edge with the white sky showing below it until
   the clip finished loading. Pin both media layers to the stage explicitly. */
#film .sw-scene__still,
#film .sw-scene__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- Phones: letterbox the footage on plain white ----
   `object-fit: cover` on a 375x812 portrait viewport scales a 16:9 frame by
   HEIGHT, so a 1444px-wide frame is shown through a 375px window and only ~26%
   of it survives -- every scene became an unreadable vertical strip. The media
   is therefore a full-width band at roughly its own aspect ratio, and the rest
   of the screen is the page's white.

   The band keeps its exact size and position; only the surround changed from a
   blurred poster to flat white, at the client's request. */
@media (max-width: 860px) {
  /* Each scene paints its own white ground. It has to live on .sw-scene rather
     than on .sw-sky, because the sky is NOT covered by the engine's clip-path --
     a white sky would show as a full-screen flash above and below the film. */
  #film .sw-scene { background: var(--white); }

  #film .sw-scene__still,
  #film .sw-scene__video {
    top: var(--band-top);
    bottom: auto;
    height: var(--band-h);
    z-index: 1;
  }

  /* Every section's heading lands in the same place: directly under the band,
     which is where the hero's headline already sits. The engine bottom-anchors
     this block, so each section's copy floated to a different height depending
     on how many lines it had. Anchoring the TOP instead pins the eyebrow, and
     therefore the heading, to one spot for all seven sections -- so headings
     swap in and out on the spot instead of jumping around. */
  /* Centre the copy in the space between the band and the sticky call bar,
     rather than hanging it off either edge. Every section gets the identical
     box, so headings occupy the same optical centre and swap in and out on the
     spot instead of jumping around. */
  #film .sw-copy {
    top: calc(var(--band-top) + var(--band-h));
    bottom: calc(var(--callbar-h) + env(safe-area-inset-bottom) + 1.5vh);
    left: clamp(22px, 7vw, 48px);
    right: clamp(22px, 7vw, 48px);
    transform: none;
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* The hero headline carries a `max-width` in ch so it breaks onto three tidy
     lines. But a block with a max-width sits flush LEFT in its container --
     `text-align: center` only centres the text INSIDE that narrow box, so the
     whole headline sat off to the left while the section headings (which have
     no max-width) centred correctly. Centre the constrained box itself. */
  #film .sw-copy__title,
  #film .sw-copy__body { margin-inline: auto; }

  /* The call bar already pins BOTH actions to the bottom of every phone screen,
     so the hero's own pair was a duplicate that collided with it. Drop the
     hero's buttons entirely on phones and let the bar do that job. */
  #film .sw-copy--hero .sw-copy__cta { display: none; }
  /* The counter sits above the eyebrow and would push each heading down by its
     own height, breaking the alignment the rule above exists to create. The
     hero already hides it. */
  #film .sw-copy__num { display: none; }
  #film .sw-copy__tags { justify-content: center; }
  #film .sw-copy__eyebrow { letter-spacing: .18em; margin-top: 0; }
}

/* Shown when the engine cannot run (no JS, or reduced motion). */
.film-fallback { border-radius: var(--r-lg); overflow: hidden; }
.film-fallback img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }

/* ---------- 18. Motion ----------
   Scroll-reveal animations were removed at the client's request: sections
   fading in on scroll left visible gaps and made the page feel like it was
   loading as you went. Everything now renders immediately. The only motion
   left on the page is the film, which is driven by scroll on purpose. */
