﻿/* ==========================================================================
   TAK Heating & Cooling â€” PPC Landing Page styles
   Palette derived from the real TAK flame-in-gear logo.
   Colour-by-action: --call (green) = phone only. --brand = book/quote only.
   ========================================================================== */

:root {
  --ink:        #0B0E13;
  --ink-2:      #141A24;
  --ink-3:      #1F2733;
  --paper:      #FFFFFF;
  --paper-2:    #F4F6FA;
  --paper-3:    #E8ECF3;

  --brand:      #E8442C;   /* flame red â€” decorative accents, gradients, icons */
  --brand-dark: #C4321E;
  /* TAK's actual brand red, confirmed by crawling takheating.com's own CSS.
     Reserved for anything carrying WHITE TEXT: 5.53:1 vs #fff â€” the brighter
     --brand only reaches 3.98:1 and fails WCAG AA. */
  --brand-cta:      #D2042D;
  --brand-cta-dark: #A80324;
  --brand-2:    #F7941D;   /* flame orange â€” accents */
  --blue:       #1E7BC4;   /* logo blue â€” informational accents */
  --blue-dark:  #14588F;

  --call:       #15803D;   /* green â€” CALL action only */
  --call-dark:  #116430;

  --text:       #1A2029;
  --text-soft:  #4A5563;
  --line:       #DDE3EC;

  /* Stars need DIFFERENT golds on light and dark, so there are two tokens.
     --star is the light-background default: #F5A623 falls to 2.03:1 on white
     and reads as washed-out decoration, while #B4690E holds 4.23:1 on white and
     3.91:1 on --paper-2, clear of the 3:1 floor for non-text and still a warm
     gold rather than a brown. --star-on-dark keeps the original bright gold for
     the hero, where the light value would disappear. Stars are the most-scanned
     trust element on the page; they have to actually read on both. */
  --star:         #B4690E;
  --star-on-dark: #F5A623;

  --wrap: 1140px;
  --radius: 14px;
  --radius-lg: 20px;
  --shadow: 0 4px 20px rgba(11,14,19,.08);
  --shadow-lg: 0 18px 50px rgba(11,14,19,.18);
  --header-h: 74px;

  --sans: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--sans);
  color: var(--text);
  background: var(--paper);
  font-size: 17px;
  line-height: 1.6;
}

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

h1, h2, h3 { line-height: 1.18; margin: 0 0 .5em; letter-spacing: -.02em; font-weight: 800; }
h1 { font-size: clamp(2rem, 4.4vw, 3.15rem); }
h2 { font-size: clamp(1.6rem, 3.1vw, 2.35rem); }
h3 { font-size: 1.18rem; }
p  { margin: 0 0 1em; }

a { color: var(--blue-dark); }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 20px; }

section { padding: 72px 0; }
.sec-head { max-width: 760px; margin: 0 auto 44px; text-align: center; }
.sec-head p { color: var(--text-soft); font-size: 1.06rem; margin: 0; }

.eyebrow {
  display: inline-block; font-size: .78rem; font-weight: 800; letter-spacing: .13em;
  text-transform: uppercase; color: var(--brand-dark); margin-bottom: .7rem;
}

/* Anchor offset for the sticky header */
[id] { scroll-margin-top: calc(var(--header-h) + 16px); }

/* ---------- Skip link ---------- */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: #fff; padding: 12px 18px; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-family: inherit; font-size: 1.02rem; font-weight: 800; line-height: 1.2;
  padding: 16px 26px; border-radius: 999px; border: 2px solid transparent;
  text-decoration: none; cursor: pointer; transition: transform .15s ease, background .15s ease;
  text-align: center;
}
/* Gated: an ungated hover latches on touch browsers, leaving the last-tapped
   button stuck in its hover state after the visitor has moved on. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-2px); }
}
.btn:active { transform: translateY(1px); transition-duration: 0s; }
.btn:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

.btn-book  { background: var(--brand-cta); color: #fff; }
.btn-book:hover { background: var(--brand-cta-dark); }
.btn-call  { background: var(--call); color: #fff; }
.btn-call:hover { background: var(--call-dark); }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.55); }
.btn-ghost:hover { background: rgba(255,255,255,.12); }
.btn-block { width: 100%; }
.btn-lg { padding: 19px 34px; font-size: 1.1rem; }

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(11,14,19,.96);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid rgba(255,255,255,.09);
}
.header-in {
  height: var(--header-h);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.brand img { height: 40px; width: auto; }

.nav { display: flex; gap: 26px; }
.nav a {
  color: #E4E9F0; text-decoration: none; font-size: .95rem; font-weight: 600;
  padding: 6px 0; border-bottom: 2px solid transparent;
}
.nav a:hover { color: #fff; border-bottom-color: var(--brand-2); }
/* The offer earns a colour in the nav — it's the only promotional item there. */
.nav a.nav-offer { color: var(--brand-2); font-weight: 800; }
.nav a.nav-offer:hover { color: #FFB84D; }

/* Header right-hand cluster: phone (green) + form CTA (brand red).
   Colour-by-action holds — one colour per action, everywhere on the page. */
.header-actions { display: flex; align-items: center; gap: 12px; }

/* Desktop-only. Below 1041px the nav collapses and the sticky bottom bar
   carries this action instead, so the header must not compete for width. */
.header-cta { display: none; padding: 11px 20px; font-size: 1rem; }
@media (min-width: 1041px) {
  .header-cta { display: inline-flex; }
  /* No-JS safe: visible by default, and only hidden once the inline head
     script has set html.js — so it never flashes and never traps focus. */
  html.js .header-cta {
    visibility: hidden; opacity: 0; transition: opacity .25s ease;
  }
  html.js .header-cta.show { visibility: visible; opacity: 1; }
}

/* Header phone â€” high contrast, obvious, never bland */
.header-call {
  display: inline-flex; align-items: center; gap: .5rem;
  background: var(--call); color: #fff; text-decoration: none;
  font-weight: 800; font-size: 1.02rem; padding: 11px 20px; border-radius: 999px;
  white-space: nowrap;
}
.header-call:hover { background: var(--call-dark); }
.header-call svg { flex: none; }

/* ---------- Hero ---------- */
.hero {
  position: relative; overflow: hidden;
  background: var(--ink);
  padding: 0;
}
/* Hero photo: a technician servicing an outdoor AC condenser with the branded
   van behind him. Chosen because it reads as "AC repair" at a glance, without
   the visitor having to read a word — equipment alone didn't communicate that. */
/* The hero photos are cropped to 1.50 — the same shape as the hero box — so the
   framing baked into the file is what desktop visitors actually see and a plain
   50% is correct. Each crop places its subject at 53.5% of the frame, which is
   the centre of the clear channel between the copy and the form card at every
   desktop width (1280 / 1440 / 1600 all land within a few px).
   The previous 1370x800 (ratio 1.713) files were much wider than the box, so
   cover() scaled them to fill HEIGHT and overflowed width by 192-352px — the
   composition slid sideways as the window resized and background-position-y was
   inert (the measured vertical overflow was 0 at every width). */
.hero-bg {
  position: absolute; inset: 0;
  background-size: cover;
  background-position: 50% 50%;
  filter: brightness(1.02) saturate(1.04);
}

/* Per-page hero photo. One stylesheet serves all five landing pages; the page
   picks its hero with a modifier class on <section class="hero h-…">.
   EVERY source below is real, client-owned photography — no stock, no AI.
   (The previous default, hero-ac-tech-*, was an AI-generated image from the
   client's Unbounce funnel: warped chest logo, malformed hand. Removed.) */
.h-gauges      .hero-bg { background-image: url("../assets/hero-gauges-desktop.webp"); }
/* This frame is a black polo shirt in shade — under the standard overlay the
   channel between the copy and the card went to an unreadable dark mass. It is
   also the one hero whose source has no face (see the crop script), so the
   torso, the TAK chest logo and the refrigerant hoses have to carry it; they
   need the extra exposure to read at all. */
.h-gauges      .hero-bg { filter: brightness(1.34) saturate(1.12) contrast(.97); }
.h-gauges      .hero-bg::after {
  background:
    linear-gradient(100deg, rgba(11,14,19,.93) 0%, rgba(11,14,19,.84) 30%, rgba(11,14,19,.26) 52%, rgba(11,14,19,.34) 70%, rgba(11,14,19,.66) 100%),
    linear-gradient(to top, rgba(11,14,19,.56) 0%, rgba(11,14,19,0) 44%);
}
.h-heatpump    .hero-bg { background-image: url("../assets/hero-heatpump-desktop.webp"); }
.h-furnace     .hero-bg { background-image: url("../assets/hero-furnace-desktop.webp"); }
.h-waterheater .hero-bg { background-image: url("../assets/hero-waterheater-desktop.webp"); }
.h-crew        .hero-bg { background-image: url("../assets/hero-crew-van-desktop.webp"); }
/* Weighted to the LEFT where the copy sits; stays lighter through the centre so
   the unit remains clearly visible between the copy and the form. */
.hero-bg::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(11,14,19,.94) 0%, rgba(11,14,19,.88) 32%, rgba(11,14,19,.40) 54%, rgba(11,14,19,.46) 70%, rgba(11,14,19,.72) 100%),
    linear-gradient(to top, rgba(11,14,19,.60) 0%, rgba(11,14,19,0) 44%);
}
.hero-in {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: minmax(0,1fr) 420px; gap: 52px; align-items: center;
  padding: 60px 20px 68px;
}
/* 520, not 580. The clear channel between the copy and the form card is
   (wrap 1100 − card 420 − this), and the grid gap does NOT widen it — the gap
   is taken out of the 1fr track, which this max-width already caps, so raising
   the gap changes nothing. 580 left a 100px channel, narrower than a face
   (~150px), which is why every hero technician was half-hidden behind the card.
   520 opens it to 160px so the subject actually fits between the two columns. */
.hero-copy { color: #fff; max-width: 520px; }
/* Capped at 2.875rem (46px) rather than the global 3.15rem. Narrowing the copy
   column to 520px to open the photo channel pushed every default H1 onto a
   third line with "Cities" orphaned on its own; 46px is the largest size at
   which all five fit two lines. Measured, not guessed — at 48px the furnace and
   heat-pump headlines still wrapped. A long ?lp= value can still take three
   lines, which is fine: the hero grows and nothing overlaps. */
.hero-copy h1 { color: #fff; font-size: clamp(2rem, 4vw, 2.875rem); }
.hero-copy h1 .accent {
  background: linear-gradient(90deg, var(--brand-2), var(--brand));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero-sub { font-size: 1.14rem; color: #DCE3ED; margin-bottom: 1.4rem; }

.hero-points { list-style: none; margin: 0 0 1.7rem; padding: 0; display: grid; gap: .6rem; }
.hero-points li {
  display: flex; align-items: flex-start; gap: .65rem;
  color: #E9EEF5; font-size: 1.02rem; font-weight: 600;
}
.hero-points svg { flex: none; margin-top: 3px; }

.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }

/* Phone is the PRIMARY action on MOBILE ONLY.
   Desktop: tel: is a weak affordance — a click does nothing useful or opens an
   app picker — so the number is rendered as readable text and the hero FORM
   stays the desktop conversion.
   Mobile: a full-width tap-to-call sits above the form. */
.call-desktop { display: inline-flex; }
.call-mobile  { display: none; }

.hero-tel {
  display: inline-flex; align-items: center; gap: .55rem; margin: 0;
  color: #E9EEF5; font-size: 1.04rem; font-weight: 700;
}
.hero-tel svg { flex: none; }
/* Bright green on the dark hero — 9:1 against the overlay, and it keeps the
   colour-by-action rule (green = call) consistent with the header pill. */
.hero-tel a { color: #4ADE80; font-weight: 800; text-decoration: none; }
.hero-tel a:hover { text-decoration: underline; }

/* ---------- Google review proof ----------
   A rating set as plain text gets READ; a rating carrying the real Google mark
   gets SCANNED, and third-party proof is the strongest trust element on a paid
   landing page.

   TWO VARIANTS — choose per page, see the Google review badge rules in
   CLAUDE.md:
     .ginline  the mark sits INSIDE the existing line of copy, inheriting its
               colour and type. Right when the page already has a strong visual
               system and a card would read as a widget bolted on top.
     .gbadge   a self-contained light card with the score at display size. Right
               when the rating has to dominate rather than blend.

   TAK uses .ginline: the hero is a dark photographic band with its own type
   palette, so a white card floated on it looked like a third-party embed. The
   card styles are kept below because the choice is per-page, not per-project.

   Both obey the same rules: official four-colour mark, NEVER wrapped in a link
   (an outbound click drops paid traffic onto a listing ringed with
   competitors), and never applied to non-Google content. Nominative use. */

/* ---- Variant A: inline (used here) ---- */
.ginline {
  /* Bottom margin matters now that this sits BETWEEN the subhead and the
     bullets rather than at the foot of the column — without it the rating
     collides with the first bullet and reads as part of the list. */
  margin: 1.35rem 0 1.45rem;
  display: flex; align-items: center; gap: .55rem; flex-wrap: wrap;
  font-size: .96rem; font-weight: 600;
  color: inherit;              /* sits inside the design, not on top of it */
}
.ginline .g-logo { width: 20px; height: 20px; flex: none; }
.gi-score { font-size: 1.2rem; font-weight: 800; line-height: 1; }
.ginline .stars { font-size: 1.05rem; line-height: 1; }
.gi-meta strong { font-weight: 800; }
.ginline-centre { justify-content: center; text-align: center; margin: 0 auto 26px; }

/* On the dark hero it adopts the hero's light-on-dark palette, and the stars
   need the BRIGHT gold — --star is tuned for light backgrounds. */
.hero .ginline { color: #E9EEF5; }
.hero .ginline .stars { color: var(--star-on-dark); }
.hero .gi-meta strong, .hero .gi-score { color: #fff; }

/* ---- Variant B: card (available, not used on this client) ---- */
.gbadge {
  margin-top: 1.6rem;
  display: inline-flex; align-items: center; gap: .8rem;
  background: #fff; border-radius: var(--radius);
  padding: 12px 18px; box-shadow: 0 6px 22px rgba(0,0,0,.28);
}
.gbadge .g-logo { width: 26px; height: 26px; flex: none; }
.gb-score { font-size: 1.75rem; font-weight: 800; line-height: 1; color: var(--ink); }
.gb-right { display: flex; flex-direction: row; align-items: center; gap: .55rem; min-width: 0; }
.gb-right .stars { font-size: 1.05rem; line-height: 1; }
/* Contrast is computed against the WHITE card, not whatever sits behind it. */
.gb-meta { font-size: .82rem; font-weight: 600; color: var(--text-soft); line-height: 1.2; }
.gb-meta strong { color: var(--ink); font-weight: 800; }

/* ---- The mark on its own: trust bar. Suits any layout. ---- */
.t-mark-g { display: flex; align-items: center; justify-content: center; }
.t-mark-g .g-logo { width: 30px; height: 30px; }
.stars { color: var(--star); letter-spacing: 1px; font-size: 1.05rem; }

/* ---------- Hero form card ---------- */
/* 22px, not 26. The hero card is the tallest element on the page and every
   pixel of its chrome pushes the submit button toward the fold. */
.form-card {
  background: #fff; border-radius: var(--radius-lg); padding: 22px 24px;
  box-shadow: var(--shadow-lg);
}
/* Clears the sticky header when the mobile CTA scrolls a form into view. */
.form-card { scroll-margin-top: calc(var(--header-h) + 12px); }
.form-card h2 { font-size: 1.32rem; margin-bottom: .3rem; }
.form-card .form-intro { font-size: .93rem; color: var(--text-soft); margin-bottom: .85rem; }

.field { margin-bottom: 10px; }
/* Name + phone share a row so the hero card stays short enough that the copy
   column and the form balance instead of leaving dead space. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.field-row .field { margin-bottom: 10px; }
.field label {
  display: block; font-size: .85rem; font-weight: 700; margin-bottom: 5px; color: var(--text);
}
.field .opt { font-weight: 500; color: var(--text-soft); }
.field input, .field select, .field textarea {
  width: 100%; font-family: inherit; font-size: 1rem; color: var(--text);
  padding: 13px 14px; border: 1.5px solid var(--line); border-radius: 10px;
  background: #fff; transition: border-color .15s ease, box-shadow .15s ease;
}
.field textarea { resize: vertical; min-height: 76px; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(30,123,196,.16);
}
/* Proof at the point of action — the highest-value place on the page for the
   Google mark, because it is the last thing read before the visitor commits.
   The mark leads the GOOGLE facts only; "Owner-led since 2021" is split into
   its own span so the mark never implies Google verified a claim it did not. */
.form-trust {
  display: flex; align-items: center; gap: .45rem; justify-content: center;
  flex-wrap: wrap;
  background: var(--paper-2); border-radius: 10px; padding: 8px 12px;
  font-size: .85rem; font-weight: 700; color: var(--text); margin-bottom: 11px;
}
/* Given its own line rather than left to wrap: at the card's width it wrapped
   anyway, and a mid-line separator then landed at the START of the second line
   looking like a bullet. Deliberate beats accidental, and the break reinforces
   that this is a SEPARATE credential from the Google rating above it. */
.ft-own {
  flex-basis: 100%; text-align: center;
  font-weight: 600; color: var(--text-soft); font-size: .8rem;
}
.form-note {
  font-size: .82rem; color: var(--text-soft); text-align: center; margin: 11px 0 0;
}
.form-note a { color: var(--blue-dark); }
.hp { position: absolute; left: -9999px; }

/* ---------- Trust bar ---------- */
.trustbar { background: var(--paper-2); padding: 26px 0; border-bottom: 1px solid var(--line); }
.trust-items { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; align-items: center; }
.trust-item {
  display: flex; align-items: center; gap: 12px; justify-content: center;
  padding: 0 8px; border-right: 1px solid var(--line);
}
.trust-item:last-child { border-right: 0; }

/* Every credential sits in a fixed-height mark box — badge image, "A+" and the
   star row alike. One shared box height is what puts five marks of different
   aspect ratios on a single optical line and starts every label beside them at
   the same place. Sizing lives here, never inline: an inline height:52px beats
   the mobile media query and was leaving badges at desktop size on phones. */
.trust-item .t-mark {
  flex: none; display: flex; align-items: center; justify-content: center;
  height: 50px;
}
.trust-item .t-mark img { max-height: 100%; max-width: 100%; width: auto; height: auto; }
.t-mark-wide { width: 108px; }   /* FortisBC is a wide lockup, not a round seal */
.t-mark-txt { font-size: 1.6rem; font-weight: 800; color: var(--brand-dark); line-height: 1; }
/* .t-mark-stars removed — the trust bar's rating now carries the Google mark
   (.t-mark-g) instead of a row of plain text stars. */
/* min-width:0 lets the label shrink inside its grid/flex cell — without it the
   longest label forces the whole page to scroll sideways on narrow phones. */
.trust-item { min-width: 0; }
.trust-item .t-lab { font-size: .86rem; font-weight: 700; line-height: 1.3; min-width: 0; overflow-wrap: anywhere; }
.trust-item .t-sub { display: block; font-size: .77rem; font-weight: 500; color: var(--text-soft); }

/* ---------- Promise (the core angle) ---------- */
.promise { background: var(--ink); color: #fff; }
.promise h2 { color: #fff; }
.promise .sec-head p { color: #C7D0DC; }
.promise-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.promise-card {
  background: var(--ink-2); border: 1px solid rgba(255,255,255,.09);
  border-radius: var(--radius); padding: 26px;
}
.promise-card h3 { color: #fff; }
.promise-card p { color: #C7D0DC; margin: 0; font-size: .97rem; }
.p-icon {
  width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--brand), var(--brand-2)); margin-bottom: 14px;
}

/* ---------- Umbrella page: 4-up equipment grid + deep links ----------
   The only cross-page links on the site. They exist because the umbrella page
   serves visitors who DO know what they need — the card's primary CTA is still
   the on-page form, so an undecided visitor is never pushed off the page. */
/* Two classes, so this beats the 3-up `.systems-grid` base and its media
   queries on specificity rather than depending on source order. */
.systems-grid.systems-grid-4 { grid-template-columns: repeat(4, 1fr); gap: 20px; }
@media (max-width: 1180px) { .systems-grid.systems-grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .systems-grid.systems-grid-4 { grid-template-columns: 1fr; } }

.sys-link {
  display: inline-block; margin-top: 14px;
  color: var(--brand-cta); font-weight: 800; font-size: .95rem;
  text-decoration: none; border-bottom: 2px solid transparent;
}
.sys-link:hover { border-bottom-color: var(--brand-cta); }

/* ---------- Real-work band (full-bleed ultra-wide strip) ---------- */
.workband { background: var(--paper-2); padding: 64px 0 0; }
.band-claims {
  list-style: none; margin: 0 0 34px; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 30px;
}
.band-claims li {
  display: flex; align-items: center; gap: .5rem;
  font-size: .95rem; font-weight: 700; color: var(--text);
}
.band-claims svg { flex: none; }
/* Edge-to-edge: the strip is 6.4:1, so constraining it to the container would
   shrink every frame to postage-stamp size. */
.band-img {
  width: 100%; height: 210px; object-fit: cover; object-position: center;
  display: block;
}

/* ---------- Symptoms ---------- */
.symptom-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.symptom {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px 20px; box-shadow: var(--shadow);
}
.symptom h3 { font-size: 1.04rem; margin-bottom: .35rem; }
.symptom p { font-size: .92rem; color: var(--text-soft); margin: 0; }
.s-icon {
  width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center;
  background: rgba(30,123,196,.1); margin-bottom: 13px;
}

/* ---------- Offer / tune-up specials ----------
   Amber is reserved for the offer: it must not read as the brand "book" red or
   the "call" green, or the colour-by-action rule breaks down. */
.offer {
  background:
    radial-gradient(1100px 420px at 50% -10%, rgba(247,148,29,.16), transparent 65%),
    linear-gradient(160deg, var(--ink) 0%, var(--ink-3) 100%);
  color: #fff;
}
.offer h2 { color: #fff; }
.offer .sec-head p { color: #C7D0DC; }

.offer-flag {
  display: inline-block; margin-bottom: .8rem;
  background: linear-gradient(90deg, var(--brand-2), #FFB84D);
  color: #2A1A00; font-size: .74rem; font-weight: 900;
  letter-spacing: .16em; text-transform: uppercase;
  padding: 6px 14px; border-radius: 999px;
}

.offer-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px; max-width: 860px; margin: 0 auto;
}
.offer-card {
  position: relative; background: var(--ink-2);
  border: 1px solid rgba(255,255,255,.12); border-radius: var(--radius-lg);
  padding: 34px 28px 28px; text-align: center;
  /* Column layout so both CTAs sit on the same baseline even though the
     heating card carries two extra lines of rebate maths. */
  display: flex; flex-direction: column;
}
.offer-card .offer-sub { margin-top: auto; }
.offer-card.featured {
  border-color: var(--brand-2);
  box-shadow: 0 0 0 1px var(--brand-2), 0 18px 44px rgba(247,148,29,.16);
  background: linear-gradient(180deg, rgba(247,148,29,.09), var(--ink-2) 55%);
}
/* "+ tax" rides with every displayed price — TAK's price is $119 plus tax. */
.offer-price .tax {
  font-size: .34em; font-weight: 700; letter-spacing: .02em;
  color: #B9C4D2; vertical-align: middle; margin-left: .15em;
}

/* The FortisBC rebate is FortisBC's to grant, not ours. It is shown as a
   conditional note rather than subtracted from the price, so nobody arrives
   expecting $69 and is told they didn't qualify. */
.offer-rebate {
  margin: 10px 0 0; padding: 11px 14px;
  border-radius: 10px;
  border: 1px dashed rgba(247,148,29,.45);
  background: rgba(247,148,29,.07);
  color: #E3E9F1; font-size: .88rem; line-height: 1.55;
}
.offer-rebate strong { color: var(--brand-2); }

.offer-badge {
  position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  background: var(--brand-2); color: #2A1A00;
  font-size: .72rem; font-weight: 900; letter-spacing: .12em; text-transform: uppercase;
  padding: 5px 14px; border-radius: 999px; white-space: nowrap;
}
.offer-card h3 { color: #fff; font-size: 1.12rem; margin-bottom: .9rem; }

.offer-math { margin: 0 0 .3rem; font-size: .96rem; color: #A9B4C2; }
.offer-math .was { text-decoration: line-through; }
.offer-math .minus { display: block; color: var(--brand-2); font-weight: 700; }

.offer-price {
  margin: 0 0 .6rem; font-size: 3.4rem; font-weight: 900; line-height: 1;
  color: #fff; letter-spacing: -.03em;
}
.offer-price .cur { font-size: 1.7rem; vertical-align: super; margin-right: 2px; }
.offer-price .pre {
  display: block; font-size: .8rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--brand-2); margin-bottom: .3rem;
}
.offer-sub { font-size: .93rem; color: #B4BFCD; margin: 0 0 1.4rem; }

.btn-ghost-dark {
  background: transparent; color: #fff; border-color: rgba(255,255,255,.4);
}
.btn-ghost-dark:hover { background: rgba(255,255,255,.1); }

.offer-terms {
  max-width: 860px; margin: 20px auto 0; text-align: center;
  font-size: .82rem; color: #8E99A8;
}

.offer-benefits {
  list-style: none; margin: 40px 0 0; padding: 26px 0 0;
  border-top: 1px solid rgba(255,255,255,.12);
  display: flex; flex-wrap: wrap; justify-content: center; gap: 14px 40px;
}
.offer-benefits li { display: flex; align-items: center; gap: .6rem; color: #E9EEF5; font-size: 1rem; }
.offer-benefits svg { flex: none; }

/* ---------- Systems ---------- */
.systems { background: var(--paper-2); }
.systems-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.system-card {
  background: #fff; border-radius: var(--radius); padding: 28px; border: 1px solid var(--line);
}
.system-card h3 { margin-bottom: .4rem; }
.system-card p { font-size: .95rem; color: var(--text-soft); margin: 0 0 .9rem; }
.system-card ul { margin: 0; padding-left: 1.1rem; font-size: .92rem; color: var(--text-soft); }
.system-card li { margin-bottom: .3rem; }
.makes {
  margin-top: 34px; text-align: center; font-size: 1rem; font-weight: 700; color: var(--text);
  background: #fff; border: 1px dashed var(--line); border-radius: var(--radius); padding: 20px;
}
.makes span { display: block; font-weight: 500; color: var(--text-soft); font-size: .93rem; margin-top: .3rem; }

/* ---------- Real work gallery ---------- */
.work-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.work-item {
  margin: 0; background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow);
}
/* First tile spans two columns â€” the servicing shot is the strongest of the set. */
.work-item:first-child { grid-column: span 2; }
.work-item img { width: 100%; height: 260px; object-fit: cover; }
/* Both tiles on the first row share a taller image so the row aligns. */
.work-item:first-child img,
.work-item:nth-child(2) img { height: 340px; }
.work-item figcaption {
  padding: 14px 18px; font-size: .9rem; color: var(--text-soft); line-height: 1.45;
}

/* ---------- Process ---------- */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; counter-reset: s; }
.step { position: relative; padding-top: 8px; }
.step-n {
  width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-cta); color: #fff; font-weight: 800; font-size: 1.15rem; margin-bottom: 15px;
}
.step h3 { margin-bottom: .35rem; }
.step p { color: var(--text-soft); font-size: .96rem; margin: 0; }

/* Photo closing the process section — reinforces "finished on the first visit". */
.process-photo {
  margin: 46px auto 0; max-width: 900px;
  border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--shadow);
}
.process-photo img { width: 100%; height: auto; display: block; }
.process-photo figcaption {
  padding: 14px 18px; font-size: .92rem; color: var(--text-soft);
  background: #fff; text-align: center;
}

/* ---------- Meet Tony (editorial split) ---------- */
.owner { background: var(--ink); color: #fff; }
.owner-grid { display: grid; grid-template-columns: 400px minmax(0,1fr); gap: 52px; align-items: center; }
.owner-photo { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-lg); }
.owner h2 { color: #fff; }
.owner p { color: #C7D0DC; }
.owner .quote {
  border-left: 3px solid var(--brand-2); padding-left: 18px; margin: 22px 0;
  font-size: 1.1rem; font-style: italic; color: #EDF1F6;
}
.owner-sig { font-weight: 800; color: #fff; font-style: normal; display: block; margin-top: .6rem; font-size: .95rem; }
.owner-sig span { display: block; font-weight: 500; color: #A9B4C2; font-size: .85rem; }

/* ---------- Reviews ---------- */
.reviews { background: var(--paper-2); }
.review-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.review {
  background: #fff; border-radius: var(--radius); padding: 24px; border: 1px solid var(--line);
  display: flex; flex-direction: column;
}
.review .stars { margin-bottom: .6rem; }
.review p { font-size: .96rem; color: var(--text); margin: 0 0 1rem; flex: 1; }
.review .who { font-weight: 800; font-size: .93rem; }
.review .who span { display: block; font-weight: 500; color: var(--text-soft); font-size: .82rem; margin-top: 2px; }
/* The source label was already the words "Google review"; adding the mark to it
   upgrades an element the visitor READS into one they SCAN, without introducing
   a new element or more clutter. Deliberately small (14px) and subordinate to
   the aggregate badge — a trust mark repeated at full size on six cards stops
   being a mark and becomes wallpaper. The single most common objection to
   on-page testimonials is "the business wrote these", and a per-quote source
   mark is the cheapest possible answer to it. */
.review .who-src { display: flex; align-items: center; gap: .4rem; }
.g-mark-sm { width: 14px; height: 14px; flex: none; }
.review-foot { text-align: center; margin-top: 30px; font-weight: 700; }
.review-foot > span { display: block; font-weight: 500; color: var(--text-soft); font-size: .93rem; margin-top: .3rem; }

/* ---------- Booking section ---------- */
.book { background: linear-gradient(135deg, var(--ink) 0%, var(--ink-3) 100%); color: #fff; }
.book-grid { display: grid; grid-template-columns: minmax(0,1fr) 440px; gap: 52px; align-items: start; }
.book h2 { color: #fff; }
/* The form card sits on white â€” undo the dark-section heading colour. */
.book .form-card h2 { color: var(--text); }
.book-copy > p { color: #C7D0DC; font-size: 1.05rem; }
.book-list { list-style: none; padding: 0; margin: 26px 0 0; display: grid; gap: 15px; }
.book-list li { display: flex; gap: .8rem; align-items: flex-start; color: #E4EAF2; }
.book-list svg { flex: none; margin-top: 3px; }
.book-list strong { display: block; color: #fff; }
.book-list em { font-style: normal; color: #B4BFCD; font-size: .93rem; }
.book-call {
  margin-top: 30px; padding: 20px; border-radius: var(--radius);
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.14);
}
.book-call p { margin: 0 0 .8rem; color: #E4EAF2; font-weight: 600; }

/* ---------- Service area ---------- */
/* Branded van above the city list — says "we drive to you" faster than the copy. */
.area-van {
  display: block; width: 100%; max-width: 520px; height: auto;
  margin: 0 auto 34px;
}
.area-list {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; max-width: 860px; margin: 0 auto;
}
.area-list span {
  background: var(--paper-2); border: 1px solid var(--line); border-radius: 999px;
  padding: 9px 18px; font-size: .93rem; font-weight: 600;
}
.area-list span.primary { background: var(--brand-cta); color: #fff; border-color: var(--brand-cta); }

/* ---------- FAQ ---------- */
.faq { background: var(--paper-2); }
.faq-list { max-width: 820px; margin: 0 auto; }
.faq-item {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: 12px; overflow: hidden;
}
.faq-q {
  width: 100%; text-align: left; background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 1.04rem; font-weight: 700; color: var(--text);
  padding: 20px 54px 20px 22px; position: relative; line-height: 1.4;
}
.faq-q:focus-visible { outline: 3px solid var(--blue); outline-offset: -3px; }
.faq-q::after {
  content: ""; position: absolute; right: 22px; top: 50%;
  width: 11px; height: 11px; margin-top: -7px;
  border-right: 2.5px solid var(--brand); border-bottom: 2.5px solid var(--brand);
  transform: rotate(45deg); transition: transform .2s ease;
}
.faq-q[aria-expanded="true"]::after { transform: rotate(-135deg); margin-top: -3px; }
.faq-a { padding: 0 22px; max-height: 0; overflow: hidden; transition: max-height .28s ease, padding .28s ease; }
.faq-a[data-open="true"] { padding: 0 22px 20px; }
.faq-a p { margin: 0; color: var(--text-soft); font-size: .98rem; }
.faq-a p + p { margin-top: .8rem; }

/* ---------- Final CTA ---------- */
.final { background: var(--ink); color: #fff; text-align: center; }
.final h2 { color: #fff; }
.final > .wrap > p { color: #C7D0DC; max-width: 620px; margin: 0 auto 1.8rem; font-size: 1.08rem; }
.final-actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ---------- Footer ---------- */
.site-footer { background: #070A0E; color: #9DA8B6; padding: 52px 0 34px; font-size: .93rem; }
.footer-grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 36px; margin-bottom: 32px; }
.site-footer img { height: 40px; width: auto; margin-bottom: 14px; }
.site-footer h3 { color: #fff; font-size: .95rem; margin-bottom: .8rem; }
.site-footer a { color: #C3CCD8; text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.footer-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,.1); padding-top: 22px;
  display: flex; flex-wrap: wrap; gap: 12px 26px; justify-content: space-between; font-size: .86rem;
}
.footer-legal { display: flex; gap: 22px; flex-wrap: wrap; }
.disclaimer { margin-top: 14px; font-size: .8rem; color: #75808E; max-width: 780px; }

/* ---------- Draft verification labels (preview only) ---------- */
.label-flag {
  display: inline-block; background: #FFF3CD; color: #6B4E00; border: 1px solid #E0C97A;
  border-radius: 5px; padding: 1px 7px; font-size: .72rem; font-weight: 800;
  letter-spacing: .02em; vertical-align: middle;
}

/* ---------- Sticky mobile bar ---------- */
.mobile-bar { display: none; }

/* ---------- Scroll reveal (no-JS safe) ---------- */
.js .reveal { opacity: 0; transform: translateY(16px); }
.js .reveal.in { opacity: 1; transform: none; transition: opacity .5s ease, transform .5s ease; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  .js .reveal { opacity: 1; transform: none; }
  .btn:hover, .btn:active { transform: none; }
  /* Movement goes; the colour and border feedback stays, because for a
     reduced-motion visitor that is the only confirmation a tap happened. */
  .form-card .needchip:hover,
  .form-card .needchip:active,
  .form-card .btn-book:active,
  .form-card .fback:active,
  .form-card .fskip:active { transform: none; }
  .form-card .needchip:hover .nc-go { transform: rotate(-45deg); }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1040px) {
  .nav { display: none; }
  .trust-items { grid-template-columns: repeat(3, 1fr); gap: 18px 12px; }
  .trust-item:nth-child(3) { border-right: 0; }
  .hero-in { grid-template-columns: minmax(0,1fr) 380px; gap: 36px; }
  .promise-grid, .systems-grid, .review-grid, .steps { grid-template-columns: repeat(2, 1fr); }
  .symptom-grid { grid-template-columns: repeat(2, 1fr); }
  .work-grid { grid-template-columns: repeat(2, 1fr); }
  .owner-grid { grid-template-columns: 320px minmax(0,1fr); gap: 36px; }
  .book-grid { grid-template-columns: minmax(0,1fr) 380px; gap: 36px; }
}

@media (max-width: 860px) {
  body { font-size: 16px; }
  section { padding: 54px 0; }

  .hero-bg { background-position: 50% 45%; }
  .h-gauges      .hero-bg { background-image: url("../assets/hero-gauges-mobile.webp"); }
  .h-heatpump    .hero-bg { background-image: url("../assets/hero-heatpump-mobile.webp"); }
  .h-furnace     .hero-bg { background-image: url("../assets/hero-furnace-mobile.webp"); }
  .h-waterheater .hero-bg { background-image: url("../assets/hero-waterheater-mobile.webp"); }
  .h-crew        .hero-bg { background-image: url("../assets/hero-crew-van-mobile.webp"); }
  .hero-bg::after {
    background:
      linear-gradient(180deg, rgba(11,14,19,.90) 0%, rgba(11,14,19,.72) 40%, rgba(11,14,19,.94) 78%, rgba(11,14,19,.98) 100%);
  }
  .hero-in { grid-template-columns: 1fr; padding: 40px 20px 44px; gap: 28px; }
  .hero-copy { max-width: none; }

  /* Mobile: the PHONE is the primary action (Paul, 2026-07-30). A full-width
     tap-to-call sits directly above the hero form, and the readable-number line
     used on desktop is hidden. To avoid a third call surface, the sticky bottom
     bar drops its Call button — the sticky header pill is the persistent one. */
  .hero-actions { display: block; margin-bottom: 1.2rem; }
  .call-mobile  { display: inline-flex; }
  .call-desktop { display: none; }

  /* Reorder the hero on mobile so the call button clears the fold. Left in DOM
     order it sits below four bullets and lands ~620px down — off the first
     screen on a shorter phone, which defeats making the phone primary.
     Google badge goes directly above it: proof at the point of action. */
  .hero-copy { display: flex; flex-direction: column; }
  .hero-copy .eyebrow { order: 1; }
  .hero-copy h1       { order: 2; }
  .hero-sub           { order: 3; margin-bottom: 1rem; }
  /* Was .hero-rating — same slot, now the Google badge. align-self:flex-start
     stops the inline-flex card stretching to the full column width. */
  .ginline            { order: 4; margin: 0 0 .9rem; }
  .hero-actions       { order: 5; }
  .hero-points        { order: 6; margin-bottom: 0; }

  /* Show the real number on mobile too — "Call Now" gives the visitor nothing
     to recognise, remember, or read back. The number is the CTA. */
  .header-call { padding: 10px 14px; font-size: .94rem; gap: .4rem; }

  .promise-grid, .systems-grid, .review-grid, .steps,
  .owner-grid, .book-grid, .footer-grid, .work-grid,
  .offer-grid, .field-row { grid-template-columns: 1fr; }
  .offer-grid { gap: 30px; }
  .offer-price { font-size: 3rem; }
  .offer-benefits { gap: 12px 0; flex-direction: column; align-items: center; }
  .work-item:first-child { grid-column: span 1; }
  .work-item img, .work-item:first-child img { height: 210px; }

  /* Photo gallery and reviews become swipeable rows on phones. Stacked, they ran
     2.4 and 2.6 screens — nearly a quarter of the entire page spent on two sets
     of similar items. Horizontal scroll-snap is the native mobile gesture for
     browsing a set, and costs about half a screen instead. */
  .work-grid, .review-grid {
    display: flex; gap: 12px;
    overflow-x: auto; scroll-snap-type: x mandatory;
    padding-bottom: 10px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .work-grid::-webkit-scrollbar,
  .review-grid::-webkit-scrollbar { display: none; }

  /* 84% leaves a sliver of the next card showing — the affordance that tells the
     thumb there is more to the right. */
  .work-item, .review { flex: 0 0 84%; scroll-snap-align: start; }
  .work-item:first-child { grid-column: auto; }

  /* Footer links were 20–22px tall, under the 44px tap floor — and the phone
     number down there is a real conversion control, not decoration. */
  .footer-list a, .footer-legal a {
    display: inline-flex; align-items: center; min-height: 44px;
  }

  /* Symptoms become a compact, scannable 2-up grid on phones â€” the detail line
     is desktop-only so the section stays skimmable instead of 8 screens long. */
  .symptom-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .symptom { padding: 16px 14px; }
  .symptom h3 { font-size: .95rem; }
  .symptom p { display: none; }
  .s-icon { width: 34px; height: 34px; margin-bottom: 10px; }

  .review { padding: 20px; }

  /* A 6.4:1 strip squeezed into 375px makes every frame unreadable — serve the
     tighter cooling-equipment crop instead and give it more height. */
  .workband { padding-top: 48px; }
  /* The file swap now happens in <picture><source media>, which is resolved
     before the fetch. Doing it here with content:url() made phones download the
     desktop strip AND the mobile crop, because the src request had already
     started by the time this rule applied. Height only from here on. */
  .band-img { height: 180px; }
  .band-claims { gap: 10px 18px; }
  .band-claims li { font-size: .88rem; }

  .owner-grid { gap: 28px; }
  .owner-photo { max-width: 340px; margin: 0 auto; }

  /* Phones: stack each credential (mark above, label below) instead of keeping
     the desktop mark-beside-label row. Side by side inside a ~175px half-column
     there is no room left for the label, so each one wrapped at a different
     word and every mark pushed its text to a different x — logos looked lined
     up while the text next to them read as noise. Stacked and centred, the
     marks share one baseline and the labels centre under them.
     Columns are 1fr (not auto) so both cells are equal whatever the mark width;
     align-items:start keeps a 2-line and a 3-line label starting level. */
  .trust-items {
    grid-template-columns: repeat(2, 1fr);
    justify-items: stretch; align-items: start;
    gap: 22px 14px;
  }
  .trust-item {
    flex-direction: column; border-right: 0;
    justify-content: flex-start; text-align: center; gap: 9px; padding: 0 2px;
  }
  /* Five items in a 2-up grid orphans the fifth — span it and centre instead. */
  .trust-item:last-child { grid-column: 1 / -1; }
  .trust-item .t-mark { height: 42px; }
  .t-mark-wide { width: 96px; }
  .trust-item .t-lab { font-size: .82rem; }
  .trust-item .t-sub { font-size: .74rem; }

  /* Sticky mobile action bar — the FORM action only.
     Call already has two surfaces on mobile (the sticky header pill and the
     full-width hero button), so repeating it here would be the third and turn
     the first screen into a choice between identical buttons. */
  .mobile-bar {
    display: grid; grid-template-columns: 1fr; gap: 10px;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 120;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
    background: rgba(11,14,19,.97); border-top: 1px solid rgba(255,255,255,.12);
  }
  /* Single full-width action — room for a benefit-worded label now that it
     isn't sharing the row with a call button. */
  .mobile-bar .btn { padding: 15px 10px; font-size: 1rem; gap: .35rem; white-space: nowrap; }
  body { padding-bottom: 78px; }

  .footer-bottom { flex-direction: column; }
}


/* ---------- Service self-select: tap buttons instead of a dropdown ----------
   A native <select> is the worst control on a phone: it opens an OS picker and
   costs scroll + select + confirm on the FIRST thing the form asks. A button is
   one tap. With ~70% of paid traffic on mobile this is the cheapest friction
   we can remove.

   Progressive enhancement: .needchips is display:none by default and script.js
   adds .on, so with JavaScript off the original <select> is what renders and
   the form behaves exactly as before. */
/* Tile styling lives in the scoped lead-form block at the end of this file. */

/* ---------- Mobile: put the first form question above the fold ----------
   Measured before this rule at 390px: form card top 722px, first field 946px —
   nearly two screens of scrolling before a form-inclined visitor could begin,
   against a usable fold of roughly 660px. The bullets sat between the call
   button and the form and pushed everything down.

   .hero-copy becomes display:contents so its children join .hero-in's flex flow
   directly. That lets the FORM CARD be ordered ahead of the bullets even though
   they live in different containers — no DOM change, so desktop is untouched.
   (display:contents drops the wrapper box only; inherited colour still passes
   to the children, and the element is a plain div so there is no a11y cost.) */
@media (max-width: 860px) {
  /* gap MUST be 0 here: with display:contents the hero-in gap would apply
     between all seven children instead of the original two, adding ~140px and
     pushing the form further down than before. Spacing comes from each
     element's own margins, exactly as it did pre-reorder. */
  .hero-in { display: flex; flex-direction: column; gap: 0; }
  .hero-copy { display: contents; }

  .hero-copy .eyebrow  { order: 1; }
  .hero-copy h1        { order: 2; }
  .hero-sub            { order: 3; }
  .ginline             { order: 4; margin: 0 0 .9rem; }
  .hero-actions        { order: 5; }
  .hero-in .form-card  { order: 6; margin-top: 4px; }   /* the conversion action, before the pitch */
  .hero-points         { order: 7; margin: 1.4rem 0 0; }

  /* The hero already shows the Google rating ~120px above this one. Two
     identical ratings inside half a screen is duplication, not reinforcement,
     and it costs 71px of fold. The booking form lower down keeps its strip —
     there is no rating near that one. */
  .hero-in .form-card .form-trust { display: none; }
}

/* ---------- Two-step lead form ----------
   Step 1 is the service buttons and nothing else, so the card is SHORT and the
   whole question fits on the first mobile screen. Step 2 collects contact
   details. Same single Netlify form, every input stays in the DOM.

   Progressive enhancement: without JS neither step is hidden, so the form
   renders exactly as the old single-step version and still submits. script.js
   adds .fsteps to the form, and only then does the stepping apply. */
.fstep { display: block; }
form.fsteps .fstep { display: none; }
form.fsteps .fstep.on { display: block; }

/* ==========================================================================
   LEAD FORM — redesigned 2026-09-06
   Everything below is scoped to .form-card and drives off a local token set,
   so the form can be restyled without touching the rest of the page and
   nothing here leaks into it.
   ========================================================================== */
.form-card {
  --f-navy:      #14263D;   /* offer header */
  --f-on-navy:   #FFFFFF;
  --f-on-navy-2: #D6E0EB;
  --f-ink:       #172B42;   /* questions, labels, answers */
  --f-ink-2:     #526174;   /* descriptions, helper text */
  --f-line:      #CBD5E1;   /* tile + input borders */
  --f-line-2:    #E2E8F0;   /* card border, progress track */
  --f-hover:     #F8FAFC;
  --f-action:    #C62828;   /* actions, progress fill, selection */
  /* Green is CONFIRMATION, never an action. Red asks you to do something;
     green tells you something is already done. The step-1 echo on step 2 is
     the only place it appears, so it keeps that meaning unambiguous. */
  --f-ok:        #15803D;
  --f-ok-bg:     #EFF9F2;
  --f-ok-line:   #BFE3CC;
  --f-action-h:  #A61F1F;
  --f-sel:       #FFF4F3;   /* selected tile only */
  --f-focus:     #2563EB;

  width: 100%;
  max-width: 560px;
  /* Centred, so that on the stacked (<=860px) hero the card does not sit
     left-aligned against 170px of dead space once it hits the 560px cap. In a
     fixed grid column the auto margins are inert. */
  margin-inline: auto;
  padding: 0;                       /* padding now belongs to head + body */
  background: #fff;
  border: 0;
  border-radius: 16px;
  /* The edge is an INSET RING, not a border. See the seam note further down:
     a real border cannot be covered by the header band, because overflow:hidden
     clips to the padding box. The ring is added per context below. */
  box-shadow: 0 16px 40px rgba(20, 38, 61, .14);
}

/* ---------- Offer header ----------
   A solid navy band, full card width, constant across BOTH steps. It is the
   only thing on the card allowed to be louder than the current question. */
.form-card .fcard-head {
  background: var(--f-navy);
  border-radius: 15px 15px 0 0;     /* 16px card radius minus the 1px border */
  padding: 28px;
  text-align: left;
}
.form-card .fcard-head h2 {
  margin: 0; max-width: 440px;
  color: var(--f-on-navy);
  font-size: 24px; font-weight: 700; line-height: 1.22;
}
.form-card .fcard-head .form-intro {
  margin: 10px 0 0; max-width: 440px;
  color: var(--f-on-navy-2);
  font-size: 15px; font-weight: 400; line-height: 1.5;
}

/* ---------- Body ----------
   container-type makes the tile grid respond to the CARD's width rather than
   the viewport's, so the same form is correct in the 420px hero column, the
   440px booking column and at the 560px cap. */
.form-card .fcard-body {
  padding: 28px;
  container-type: inline-size;
  container-name: fcard;
}

.form-card .form-trust { margin: 0 0 18px; }

/* Step counter + track, on ONE row.
   Stacked, these were two readings of the same fact costing 31px of the first
   screen. Side by side they read as a single component: the label names the
   step, the track shows how far through it is. .fstepn keeps its element and
   its data-fstepn hook because script.js rewrites its text on each step. */
.form-card .fprog-row {
  display: flex; align-items: center; gap: 12px; margin: 0;
}
.form-card .fstepn {
  flex: none; margin: 0;
  font-size: 12px; font-weight: 600; color: var(--f-ink-2);
  font-variant-numeric: tabular-nums;   /* "1 of 2" -> "2 of 2" must not shift */
}
.form-card .fprog {
  flex: 1 1 auto; height: 4px; margin: 0; border-radius: 99px;
  background: var(--f-line-2); overflow: hidden;
}
.form-card .fprog-fill {
  display: block; height: 100%; width: 50%;
  border-radius: 99px; background: var(--f-action);
  transition: width .2s ease;
}

/* ---------- The current question ---------- */
.form-card .fq {
  display: block; margin: 24px 0 0;
  font-size: 21px; font-weight: 700; line-height: 1.3; color: var(--f-ink);
}
.form-card .fq:focus { outline: none; }
.form-card .fq:focus-visible { outline: 3px solid var(--f-focus); outline-offset: 3px; }
.form-card .fq-help {
  margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--f-ink-2);
}

/* ---------- Answer tiles ----------
   Progressive enhancement is unchanged: .needchips is display:none until
   script.js adds .on, so with JavaScript off the native <select> is what
   renders and the form still submits. */
.form-card .needchips { display: none; }
.form-card .needchips.on {
  display: grid; grid-template-columns: 1fr; gap: 12px; margin: 16px 0 0;
}
/* Two columns only once the tile area itself is wide enough — measured on the
   card, not the viewport, so a narrow card never gets a cramped pair. */
@container fcard (min-width: 400px) {
  .form-card .needchips.on { grid-template-columns: 1fr 1fr; }
  /* With an odd number of options the last one would sit alone in the left
     column looking like a layout bug. It takes the full row instead — which
     also suits it, since the odd one out is always the catch-all ("I'm not
     sure", "Not sure - need advice"). */
  .form-card .needchips.on > .needchip:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
.form-card .needchip {
  display: flex; align-items: center; gap: 12px; text-align: left;
  width: 100%; min-height: 84px; padding: 14px 16px;
  font: inherit; color: var(--f-ink);
  background: #fff; border: 1px solid var(--f-line); border-radius: 12px;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.form-card .nc-txt { flex: 1 1 auto; min-width: 0; }
.form-card .nc-t {
  display: block; font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--f-ink);
}
.form-card .nc-d {
  display: block; margin-top: 4px;
  font-size: 14px; font-weight: 400; line-height: 1.4; color: var(--f-ink-2);
}
/* Chevron: selecting a tile advances the form, so the tile has to look like it
   goes somewhere. It becomes a check once the tile is the chosen one. */
.form-card .nc-go {
  flex: none; width: 9px; height: 9px; margin-right: 3px;
  /* #64748B, not the lighter #94A3B8: the chevron says "this advances the
     form", so it is meaningful UI, and it needs 3:1 against white. 94A3B8
     measures 2.56:1; this measures 4.76:1. */
  border-right: 2px solid #64748B; border-bottom: 2px solid #64748B;
  transform: rotate(-45deg);
  transition: border-color .15s ease, transform .15s ease, width .15s ease, height .15s ease;
}
/* ---------- Tile feedback ----------
   Three distinct states, because the tile is the page's primary control and it
   has to answer "did that register?" instantly on both input methods.

   HOVER is gated to real pointers. Ungated, a touch browser latches :hover on
   the last thing tapped, so a tile stays lit after the visitor has moved on.
   ACTIVE is not gated, because it is the only feedback a finger ever gets. */
@media (hover: hover) and (pointer: fine) {
  .form-card .needchip:hover {
    /* #F1F5F9, not #F8FAFC. The old hover was a 2.9% luminance shift off white
       and simply could not be seen; this one is ~7% and is carried by the lift
       and the border rather than by the fill alone. */
    background: #F1F5F9;
    border-color: var(--f-ink-2);
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(20, 38, 61, .13);
  }
  .form-card .needchip:hover .nc-go {
    border-color: var(--f-ink);
    transform: translate(2px, 0) rotate(-45deg);   /* the chevron leans the way it goes */
  }
}

/* The press. Instant, and tinted with the SELECTION red, so the moment a finger
   lands the tile previews the state it is about to enter. Overrides the hover
   lift on desktop, so a click reads as lift-then-push rather than one movement. */
.form-card .needchip:active {
  background: var(--f-sel);
  border-color: var(--f-action);
  box-shadow: inset 0 0 0 1px var(--f-action);
  transform: translateY(1px);
  transition-duration: 0s;                          /* pressed state never eases in */
}
.form-card .needchip:active .nc-go { border-color: var(--f-action); }

/* Backstop: if a browser drops :active on touch, the platform highlight still
   flashes, and it flashes in the brand red rather than the OS grey. */
.form-card .needchip { -webkit-tap-highlight-color: rgba(198, 40, 40, .14); }

.form-card .needchip:focus-visible { outline: 3px solid var(--f-focus); outline-offset: 2px; }
.form-card .needchip[aria-checked="true"] {
  background: var(--f-sel); border-color: var(--f-action);
  box-shadow: inset 0 0 0 1px var(--f-action);   /* weight without resizing the tile */
}
.form-card .needchip[aria-checked="true"] .nc-go {
  width: 12px; height: 6px; margin-right: 2px;
  border-right: 0; border-left: 2.5px solid var(--f-action); border-bottom: 2.5px solid var(--f-action);
}
.form-card .neederr {
  margin: 10px 0 0; font-size: 14px; font-weight: 700; color: var(--f-action-h);
}
/* The tile group is the whole step, not a field in a stack, so it does not
   carry the shared field spacing. */
.form-card .fstep[data-fstep="1"] > .field { margin-bottom: 0; }

.form-card .fhint {
  margin: 14px 0 0; font-size: 14px; line-height: 1.5; color: var(--f-ink-2); text-align: center;
}

/* ---------- Inputs ---------- */
.form-card .field { margin: 0 0 16px; }
/* The row owns the 16px gap in BOTH axes; its children own none, or the
   stacked mobile layout would double it to 32px. */
.form-card .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 0 0 16px; }
.form-card .field-row .field { margin-bottom: 0; }
.form-card .field > label:not(.fq) {
  display: block; margin: 0 0 6px;
  font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--f-ink);
}
.form-card .field .opt { font-weight: 400; color: var(--f-ink-2); }
.form-card .field .fhelp {
  display: block; margin: -2px 0 6px;
  font-size: 13px; line-height: 1.4; color: var(--f-ink-2);
}
.form-card .field input,
.form-card .field select,
.form-card .field textarea {
  width: 100%; min-height: 50px; padding: 13px 14px;
  font-family: inherit; font-size: 16px; color: var(--f-ink);
  background: #fff; border: 1px solid var(--f-line); border-radius: 8px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.form-card .field input::placeholder,
.form-card .field textarea::placeholder { color: #8A97A8; }
.form-card .field input:focus,
.form-card .field select:focus,
.form-card .field textarea:focus {
  outline: 3px solid var(--f-focus); outline-offset: 0;
  border-color: var(--f-focus); box-shadow: none;
}
.form-card .field input:user-invalid { border-color: var(--f-action); }

/* Step 2 fields start immediately under the question, not 24px further down. */
.form-card .fstep[data-fstep="2"] .fq-help { margin-bottom: 18px; }

/* ---------- Step chrome ---------- */
.fstep { display: block; }
form.fsteps .fstep { display: none; }
form.fsteps .fstep.on { display: block; }

/* Back stays deliberately quiet — it is an escape hatch, not an action that
   should compete with the tiles or the submit button. */
.form-card .fback {
  /* 44px tall for the tap target, but the padding that buys it is absorbed
     back out with negative margins so it does not read as a 40px hole above
     the question. */
  display: inline-flex; align-items: center; margin: 0 -2px; padding: 8px 2px;
  min-height: 44px; background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--f-ink-2);
  text-decoration: underline; text-underline-offset: 3px;
}
.form-card .fback:hover { color: var(--f-ink); }
.form-card .fskip {
  display: block; width: 100%; margin: 8px 0 0; padding: 8px 2px;
  min-height: 44px; background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--f-ink-2);
  text-decoration: underline; text-underline-offset: 3px; text-align: center;
}
.form-card .fback:active,
.form-card .fskip:active { color: var(--f-ink); transform: translateY(1px); transition-duration: 0s; }
.form-card .fback:focus-visible,
.form-card .fskip:focus-visible { outline: 3px solid var(--f-focus); outline-offset: 3px; }

/* Back and the answer echo share one row above the question. */
.form-card .fstep2-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 0 0 2px;
}

/* Echo of the step-1 answer — deliberately subordinate to the question. */
.form-card .fpicked {
  display: inline-flex; align-items: center; gap: .4rem;
  margin: 0; padding: 6px 10px; border-radius: 8px;
  background: var(--f-ok-bg); border: 1px solid var(--f-ok-line);
  font-size: 13px; font-weight: 600; color: var(--f-ok);
}
.form-card .fpicked::before { content: "\2713"; color: var(--f-ok); font-weight: 900; }

/* ---------- Final action ---------- */
.form-card .fpre {
  margin: 4px 0 12px; font-size: 14px; line-height: 1.5; color: var(--f-ink-2);
}
.form-card .btn-book {
  display: block; width: 100%; min-height: 52px; padding: 14px 20px;
  background: var(--f-action); color: #fff;
  font-size: 17px; font-weight: 700; line-height: 1.2;
  border: 0; border-radius: 8px; box-shadow: none;
}
.form-card .btn-book:hover { background: var(--f-action-h); box-shadow: none; }
/* The global .btn:hover lifts 2px; the press puts it back down and past, so the
   button physically gives under the cursor instead of only changing colour. */
.form-card .btn-book:active {
  background: var(--f-action-h);
  transform: translateY(1px);
  transition-duration: 0s;
}
.form-card .btn-book { -webkit-tap-highlight-color: rgba(255, 255, 255, .22); }
.form-card .btn-book:focus-visible { outline: 3px solid var(--f-focus); outline-offset: 3px; }
.form-card .form-note {
  margin: 12px 0 0; font-size: 13px; line-height: 1.5; color: var(--f-ink-2); text-align: center;
}
.form-card .form-note a { color: var(--f-focus); }

/* ---------- Mobile ----------
   The offer header is the one thing that costs real first-screen height on a
   phone (155px measured at 390x844), so everything around it is trimmed to
   what it is actually worth. The headline and the supporting promise both
   stay at their specified sizes; the savings come from chrome. */
@media (max-width: 480px) {
  .form-card .fcard-head { padding: 18px 20px; }
  .form-card .fcard-head h2 { font-size: 24px; line-height: 1.25; }
  .form-card .fcard-head .form-intro { margin-top: 8px; }
  .form-card .fcard-body { padding: 20px; }
  .form-card .fq { font-size: 20px; margin-top: 18px; }
  .form-card .needchip { padding: 14px 16px; }
  /* Name and phone stack: two 147px inputs on a 390px phone is tighter than
     either field deserves. */
  .form-card .field-row { grid-template-columns: 1fr; }

  /* Hero card only: each tile is visibly tappable and carries an advance
     chevron, so on the first screen this line costs 27px and says nothing the
     control does not already say. The booking form lower down keeps it, where
     height is free. */
  .hero-in .form-card .fstep[data-fstep="1"] .fq-help { display: none; }
}

/* The card never needs a mobile max-width override: width:100% already caps it
   at its container, and the hero's own 20px gutter clears the 16px minimum
   the spec asks for on a phone. */

@media (min-width: 861px) {
  /* The copy column is anchored to the top of the grid, not centred in it.
     Centred, it re-centred every time the card changed height — and the card
     grows 165px when a visitor taps an option and step 2 opens. Measured, that
     slid the H1, the bullets, the Google rating and the phone number 83px DOWN
     the page at the exact moment the visitor was looking at the card. The whole
     left half of the hero moved because they tapped something on the right.
     Anchored, the card grows downward and nothing else moves. */
  .hero-in { align-items: start; }

  /* 60/68 of hero padding was set when the card was short. The card now sets
     the hero's height, so this padding is pure addition to a section that was
     overflowing a laptop screen. */
  .hero-in { padding-top: 44px; padding-bottom: 48px; }
}

/* ---------- Header band: no light edge above it ----------
   overflow:hidden keeps the header's corners inside the card's 16px radius.
   Because the card's edge is now an inset ring rather than a border, there is
   no border box between the band and the card edge, so the band simply paints
   over the ring on the three sides it touches. No negative margins needed. */
.form-card { overflow: hidden; }
.form-card .fcard-head { margin: 0; border-radius: 16px 16px 0 0; }

/* The hero card gets NO edge: it sits on a dark photograph, where the drop
   shadow already separates it and a light hairline reads as a stray outline.
   The booking card lower down does get one, because it sits on a flat section
   where the shadow alone is not enough to define where the card stops. */
.book .form-card {
  box-shadow: 0 16px 40px rgba(20, 38, 61, .14),
              inset 0 0 0 1px var(--f-line-2);
}

/* ==========================================================================
   DESKTOP (>=861px) — the card beside the copy column
   --------------------------------------------------------------------------
   This replaces FOUR consecutive @media (min-width: 861px) blocks, each of
   which was a separate attempt at the same job (bring the card's height down
   to the 536px copy column beside it) and each of which shaved the SAME
   properties again. Compounded, they left desktop type one to two steps
   SMALLER than the phone's on a column only 20% wider:

        head 20 / intro 13.5 / question 18 / tile 15 / description 12.5
   phone     24 /       15   /          20 /      16 /             14

   A 12.5px description is under any readable floor, and the tiles were not
   even tight: 8px of vertical padding inside a box held open to 88px by
   min-height, so ~38px of text floated with 25px of slack either side. The
   card was simultaneously cramped and loose.

   So the height comes out of duplication and slack instead, all of it measured:
     -31px  step label and progress bar merged onto one row
     -16px  stray .field bottom margin under the last tile
     -16px  tile min-height 84 -> 76, which the content never needed
     -55px  (step 2) Back and the answer echo merged onto one row
   and the type goes UP, toward the phone's scale rather than away from it.
   ========================================================================== */
@media (min-width: 861px) {
  /* The copy column shows the identical 5.0 / 192 Google rating about 300px to
     the left, visible at the same moment, so a second copy inside the card is
     not reinforcement at that distance. It stays on mobile, where the hero
     rating has scrolled away by the time the card is reached, and on the
     booking form lower down. */
  .hero-in .form-card .form-trust { display: none; }

  .form-card .fcard-head { padding: 18px 24px; }
  /* 20px, not 21: at the card's 372px measure this headline holds ONE line at
     20px and wraps to two at 21, and a wrapped three-sentence headline costs
     32px and reads weaker than the single confident line. This is the one
     size in the card set by its measure rather than by the scale. */
  .form-card .fcard-head h2 { font-size: 20px; line-height: 1.25; }
  .form-card .fcard-head .form-intro { margin-top: 6px; font-size: 14px; line-height: 1.45; }

  .form-card .fcard-body { padding: 18px 24px 20px; }

  .form-card .fq { margin-top: 16px; font-size: 19px; }
  .form-card .fq-help { margin-top: 4px; font-size: 13.5px; }

  .form-card .needchips { margin-top: 12px; }
  .form-card .needchips.on { gap: 10px; }
  /* 76px still clears the 44px tap floor by a wide margin, and now the text
     sets the height rather than floating inside it. */
  .form-card .needchip { min-height: 76px; padding: 12px 16px; }
  .form-card .nc-t { font-size: 16px; }
  .form-card .nc-d { font-size: 13.5px; margin-top: 2px; }

  .form-card .fskip { margin-top: 4px; }
  .form-card .fhint { margin-top: 10px; }
}
