/* =============================================
   PLUNGE, A PLUMBING CO. — Main Stylesheet
   -----------------------------------------------
   COLOR PALETTE (60 / 30 / 10 Rule):
     60%  Dark grey #3a3a3a (--bg-dark) — page + section backgrounds;
          --grey rgb(168,168,168) is a supporting neutral, not the page bg
     30%  Blue   rgb(58, 100, 158)  — header, footer, nav, primary buttons
     10%  Orange rgb(235, 149, 74)  — CTAs, accents, highlights, hover states
   ============================================= */


/* =============================================
   CSS CUSTOM PROPERTIES (variables)
   ============================================= */
:root {

    /* -- Brand Colors -- */
    --grey:         rgb(168, 168, 168);   /* supporting neutral (page bg is --bg-dark) */
    --grey-mid:     rgb(155, 155, 155);
    --grey-dark:    rgb(120, 120, 120);

    --blue:         rgb(58, 100, 158);    /* 30% primary — header, footer, nav */
    --blue-dark:    rgb(38, 72, 120);

    --orange:       rgb(235, 149, 74);    /* 10% accent — CTAs, highlights */
    --orange-dark:  rgb(210, 122, 45);
    /* Orange used as TEXT on the dark navy (footer headings, the Get In
       Touch emergency link, review "Read more"). The fill orange --orange
       measures only 3.92:1 as text on --blue-dark, failing WCAG AA (needs
       4.5). This is a hair lighter — #f0a862 scores 4.60 on #264878 — so
       those words pass while still reading as the same brand orange. Used
       for TEXT ONLY; every orange fill/button stays --orange untouched.
       (owner, 14 Aug 2026: chose the minimal lift over a brighter option.) */
    --orange-on-dark: #f0a862;

    /* -- Neutrals -- */
    --bg-dark:      #3a3a3a;   /* uniform dark grey used across all page sections */
    --white:        #ffffff;
    --text-dark:    #2c2c2c;
    /* Text that sits ON the orange accent (Call Now, Book Online, Continue,
       Service Areas, the header/nav call buttons, and the Get In Touch card).

       ⚠️ WHAT THIS REPLACED, in case it is ever wanted back: this text was
       #ffffff (white) until 12 Aug 2026. White on the orange (--orange,
       rgb 235,149,74) measured 2.35:1, which FAILS WCAG AA (needs 4.5). The
       orange background was never the problem and has not changed — only the
       text colour.

       FINAL CHOICE (owner, 12 Aug 2026): #1f2733 — a charcoal with a faint
       blue cast, so it reads dark but harmonises with the brand navy. Scores
       6.40:1, passes. (Considered and rejected: the header navy #264878 at
       3.92 — too light to pass for button text; plain #2c2c2c grey at 5.94;
       black at 8.93.) The automatic high-contrast mode below overrides this
       to pure black. On the darker HOVER orange (--orange-dark) #1f2733 is
       ~4.7 — still above 4.5. */
    --text-on-orange: #1f2733;
    /* Success green — the one "good news" colour set on the site (booking
       status line, confirmation panel). Text on bg ≈ 7.6:1. */
    --success-bg:     #d4edda;
    --success-text:   #155724;
    --success-border: #b8dfc5;
    --text-mid:     #555555;

    /* -- Typography -- */
    --font-heading: 'Courier New', monospace;
    --font-body:    'Courier New', monospace;

    /* -- Spacing scale -- */
    --space-xs:  0.4rem;
    --space-sm:  0.875rem;
    --space-md:  1.5rem;
    --space-lg:  2.5rem;
    --space-xl:  4.5rem;
    /* Vertical padding shared by EVERY homepage section, so their spacing
       is standardized and tunable from one place (owner request, 14 Aug
       2026 — the old 4.5rem left too much air between sections). */
    --section-y: 3rem;

    /* -- Border radius -- */
    --radius:    6px;
    --radius-lg: 14px;
    --radius-xl: 20px;

    /* -- Shadows -- */
    --shadow-sm:  0 2px 8px  rgba(0, 0, 0, 0.14);
    --shadow-md:  0 4px 20px rgba(0, 0, 0, 0.18);

    /* -- Transitions -- */
    --ease:       all 0.25s ease;
}


/* =============================================
   RESET & BASE STYLES
   ============================================= */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    font-size: 16px;
    /* Stop iPhones from auto-inflating text when rotated to landscape */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

/* Anchor jumps land BELOW the sticky header (July 23 2026): without
   this, tapping "Book Online" (→ #booking) or any #-link buries the
   section title under the header bar. Applies to anything with an id —
   scroll-margin only affects where jumps land, never layout. The
   mobile block tightens it to match the smaller phone header. */
[id] {
    scroll-margin-top: 100px;
}

body {
    font-family: var(--font-body);
    background-color: var(--bg-dark);     /* Uniform dark gray throughout */
    color: var(--text-dark);
    line-height: 1.65;
    overflow-x: hidden;
}

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

/* =============================================
   KEYBOARD FOCUS INDICATOR (accessibility, July 27 2026)
   -----------------------------------------------
   WCAG 2.4.7 (Level AA): anyone navigating by keyboard must be able to
   SEE where they are. Before this the site showed nothing on Tab —
   buttons, links, and toggles were invisible to keyboard users, and
   missing focus indicators are a standard finding in ADA demand
   letters. (Form FIELDS already had their own blue border + glow; they
   keep it and gain this ring too.)

   `:focus-visible` (not `:focus`) is deliberate — the browser shows the
   ring for keyboard/assistive navigation but NOT for ordinary mouse
   clicks, so nothing changes visually for mouse users.

   Orange is chosen because it reads clearly on BOTH the dark page
   background and the white booking-form card.
   ============================================= */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--orange);
    outline-offset: 2px;
}

/* WCAG 1.4.11 fix (14 Aug 2026): the orange ring above is ~4.8:1 on the dark
   page (fine) but only 2.35:1 on the WHITE booking-form card — below the 3:1
   the rule requires, exactly where a keyboard user is booking. Buttons/links on
   that card get the brand navy instead (--blue-dark #264878 is ~9:1 on white).
   The text FIELDS are already fine: their focus indicator is a --blue border
   (~6:1 on white), not this outline. Keyboard-only (:focus-visible), so mouse
   users see no change. Higher specificity (.booking-form ...) wins the colour. */
.booking-form a:focus-visible,
.booking-form button:focus-visible,
.booking-form summary:focus-visible,
.booking-form [tabindex]:focus-visible {
    outline-color: var(--blue-dark);
}
/* ...EXCEPT inside the NAVY emergency/fallback box (added Sep 8 2026 with
   the owner's Call Now button): there the navy ring above is navy-on-navy —
   invisible, the opposite of the fix's intent. Restore the site's default
   orange ring, the documented ~4.8:1 pass on --blue-dark, matching every
   other button that sits on a dark background. */
.booking-form .booking-js-fallback a:focus-visible,
.booking-form .booking-js-fallback button:focus-visible {
    outline-color: var(--orange);
}

a {
    color: var(--blue);
    text-decoration: none;
    transition: var(--ease);
}

a:hover {
    color: var(--orange);
}

ul {
    list-style: none;
}

button {
    cursor: pointer;
}


/* =============================================
   UTILITIES
   ============================================= */

/* Centered, max-width page wrapper */
.container {
    width: 90%;
    max-width: 1180px;
    margin: 0 auto;
    padding: 0 var(--space-sm);
}

/* Section headings — blue per 30% rule */
.section-title {
    font-family: var(--font-heading);
    font-size: clamp(1.5rem, 3vw, 2rem);
    font-weight: 700;
    color: var(--blue);
    margin-bottom: var(--space-sm);
    text-align: center;
}

/* Left-aligned variant used in cards */
.section-title--left {
    text-align: left;
    font-size: 1.4rem;
}

/* "Book a Service", "Expert Work by Our Skilled Technicians" and "Find Us" —
   sized and colored to match the "About Plunge" heading (font-family already
   matches via --font-heading, so only size + color need to change here).
   Scoped to these sections so every other .section-title elsewhere on the
   site is unaffected. (#find-us added 14 Aug 2026 — the map's own section.) */
#booking .section-title,
#gallery .section-title,
#find-us .section-title {
    font-size: clamp(2.2rem, 6vw, 3.8rem);
    color: var(--white);
}

.section-subtitle {
    text-align: center;
    color: rgba(255, 255, 255, 0.88);
    font-size: 1.05rem;
    margin-bottom: var(--space-lg);
    max-width: 620px;
    margin-left: auto;
    margin-right: auto;
}

/* Required field asterisk */
.required {
    color: var(--orange);
    font-weight: 700;
}

/* Blue variant — marks the "request, not a guarantee" note under the
   booking form's submit button (owner request, July 2026). */
.required--blue {
    color: var(--blue);
}


/* =============================================
   BUTTON SYSTEM
   60/30/10:
     btn-call   = orange (10% CTA accent)
     btn-book   = blue   (30% primary action)
     btn-submit = blue   (30% primary)
     btn-review = white  (neutral)
   ============================================= */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.875rem 1.75rem;
    border-radius: var(--radius);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1rem;
    border: none;
    cursor: pointer;
    text-decoration: none;
    transition: var(--ease);
    white-space: nowrap;
}

/* Call Now — orange (10% accent) */
.btn-call {
    background-color: var(--orange);
    color: var(--text-on-orange);
}
.btn-call:hover {
    background-color: var(--orange-dark);
    color: var(--text-on-orange);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

/* Book Online — orange */
.btn-book {
    background-color: var(--orange);
    color: var(--text-on-orange);
}
.btn-book:hover {
    background-color: var(--orange-dark);
    color: var(--text-on-orange);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

/* Form submit — blue (30%) */
.btn-submit {
    background-color: var(--blue);
    color: var(--white);
    font-size: 1.1rem;
    padding: 1rem 2.5rem;
    width: 100%;
    justify-content: center;
    margin-top: var(--space-sm);
}
.btn-submit:hover {
    background-color: var(--blue-dark);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}
.btn-submit:disabled {
    opacity: 0.65;
    cursor: not-allowed;
    transform: none;
}

/* Google Review — orange (matches the site's 10% accent) */
.btn-review {
    background-color: var(--orange);
    color: var(--text-on-orange);
    border: 2px solid var(--orange);
    padding: 0.75rem 1.5rem;
    font-size: 0.95rem;
}
.btn-review:hover {
    background-color: var(--orange-dark);
    border-color: var(--orange-dark);
    color: var(--text-on-orange);
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm);
}


/* =============================================
   HEADER
   Background: blue (30%)
   Logo + company name centered together.
   Menu button pinned top-right.
   ============================================= */
.site-header {
    background-color: var(--blue-dark);
    color: var(--white);
    position: sticky;
    top: 0;
    z-index: 900;
    display: flex;
    align-items: center;
    justify-content: center;        /* centers the brand group */
    padding: 0.6rem var(--space-lg);
    min-height: 78px;
    box-shadow: var(--shadow-md);
}

/* Logo + name link wrapper */
.header-brand-link {
    text-decoration: none;
    color: inherit;
    display: flex;
    align-items: center;
    cursor: pointer;
    transition: opacity 0.2s ease;
}
.header-brand-link:hover {
    opacity: 0.85;
}

/* Logo + name — horizontally side by side, centered in header */
.header-brand {
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

.header-logo-wrap {
    width: 65px;
    height: 65px;
    flex-shrink: 0;
}

.header-logo {
    width: 100%;
    height: 100%;
    display: block;
}

.header-title {
    display: flex;
    flex-direction: column;
}

.header-company-name {
    font-family: var(--font-heading);
    font-size: 1.85rem;
    font-weight: 800;
    color: var(--white);
    line-height: 1.1;
    letter-spacing: -0.5px;
}

.header-tagline {
    font-family: var(--font-heading);
    font-size: 0.9rem;
    font-weight: 400;
    color: rgba(255, 255, 255, 0.72);
    letter-spacing: 0.5px;
}

/* Menu button — absolute top-left, orange (10% accent) */
.menu-btn {
    position: absolute;
    left: var(--space-lg);
    top: 50%;
    transform: translateY(-50%);
    height: 38px;
    background: var(--orange);
    color: var(--text-on-orange);
    border: none;
    border-radius: var(--radius);
    padding: 0.5rem 1rem;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    transition: var(--ease);
}
.menu-btn:hover {
    background: var(--orange-dark);
    box-shadow: var(--shadow-sm);
}

.menu-icon {
    font-size: 1.2rem;
    line-height: 1;
}

/* Call Now button — absolute top-right, mirrors the menu button
   so it's always present on every page (desktop & mobile) */
.header-call-btn {
    position: absolute;
    right: var(--space-lg);
    top: 50%;
    transform: translateY(-50%);
    height: 38px;               /* same height as the menu button */
    background: var(--orange);
    color: var(--text-on-orange);
    border: none;
    border-radius: var(--radius);
    padding: 0.5rem 1rem;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.9rem;
    line-height: normal;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    text-decoration: none;
    transition: var(--ease);
}
.header-call-btn:hover {
    background: var(--orange-dark);
    color: var(--text-on-orange);
    box-shadow: var(--shadow-sm);
}


/* =============================================
   NAVIGATION OVERLAY & DRAWER
   Drawer slides in from the right when Menu is clicked.
   Background: dark blue (30% zone)
   ============================================= */

/* Dim overlay behind open drawer */
.nav-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 950;
    opacity: 0;
    transition: opacity 0.3s ease;
}
.nav-overlay.active {
    display: block;
    opacity: 1;
}

/* Slide-in drawer */
.city-nav {
    position: fixed;
    top: 0;
    left: 0;
    /* Hidden off-screen by its OWN width (translateX(-100%)) rather than a
       fixed pixel offset — on mobile the drawer is 88vw wide, so a fixed
       -350px left a sliver of it visible along the left edge of larger
       phones. The extra 40px keeps its drop shadow off-screen too. */
    transform: translateX(calc(-100% - 40px));
    width: 320px;
    height: 100%;
    height: 100dvh;                    /* phones: cover the full screen even as the browser toolbar collapses */
    background: var(--blue-dark);
    color: var(--white);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    padding: 1.1rem var(--space-md);   /* tighter top/bottom than the sides, to help everything fit without scrolling */
    overflow-y: auto;                  /* safety net for unusually short screens — kept tight so it's rarely needed */
    transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: 4px 0 30px rgba(0, 0, 0, 0.4);
}
.city-nav.open {
    transform: translateX(0);
}

.city-nav-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: 0.6rem;
    margin-bottom: 0.4rem;
    border-bottom: 2px solid var(--orange);   /* orange accent line */
}

.city-nav-header h2 {
    font-family: var(--font-heading);
    font-size: 1.2rem;
    color: var(--white);
}

.close-nav-btn {
    background: none;
    border: none;
    color: var(--white);
    font-size: 2rem;
    line-height: 1;
    padding: 0.2rem 0.5rem;
    transition: color 0.2s;
}
.close-nav-btn:hover { color: var(--orange); }

.city-nav-subtitle {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.55);
    margin-bottom: 0.75rem;
}

/* City link list */
.city-nav-list {
    flex: 1;
}

.city-nav-list li {
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.city-nav-list a {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.4rem;   /* tighter top/bottom spacing — text/icon size unchanged */
    color: rgba(255, 255, 255, 0.88);
    font-size: 0.98rem;
    transition: var(--ease);
}
.city-nav-list a:hover {
    color: var(--orange);
    padding-left: 0.9rem;
}
/* Appearing arrow on the drawer links (owner request, July 23 2026):
   the same inline arrow as the city service lists and blog lists —
   snug after the name, its space always reserved (opacity 0 idle) so
   nothing shifts when it appears. Spacing comes from the row's
   existing flex gap. On touch (no hover) it simply never shows. */
.city-nav-list a::after {
    content: "\f061";                     /* fa-arrow-right */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    font-size: 0.8em;
    color: var(--orange);
    opacity: 0;
    transform: translateX(-0.3em);
    transition: var(--ease);
}
.city-nav-list a:hover::after {
    opacity: 1;
    transform: translateX(0);
}

.city-nav-list .fa {
    color: var(--orange);
    font-size: 0.8rem;
    flex-shrink: 0;
    width: 14px;
}

/* Two-paw-print "trail" icon — styled after the 🐾 emoji, but drawn as a
   plain shape so it can be recolored (an actual emoji character ignores
   CSS color). Sits diagonally via the 45° rotation. Use this class
   wherever the paw-print icon appears; each usage keeps its own local
   color and size by inheriting from its surrounding context (fill:
   currentColor + em-based sizing), same as the old Font Awesome icon did. */
.paw-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
    transform: rotate(45deg);
    flex-shrink: 0;
}

/* Sidebar instance — fixed size + orange, same as before */
.city-nav-list .nav-paw-icon {
    color: var(--orange);
    width: 16px;
    height: 16px;
}

/* Blog link icon (open book + pen) — a stroke SVG, so it takes orange from
   currentColor. Sized to match the map-marker and paw icons beside it. */
.city-nav-list .nav-blog-icon {
    color: var(--orange);
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* Bottom of drawer — "Areas" pill stacked on top of "Call Now" */
.city-nav-footer {
    margin-top: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* "Return to Homepage" — a QUIET text link above the View All Areas pill
   (owner request, July 28 2026). Deliberately unstyled beyond the colour:
   no frame, no background — plain orange text matching .about-gallery-link
   ("See our work in action" on the homepage), and specifically NOT another
   button. Revised same day at the owner's request: LEFT-aligned, flush with
   the outer edges of the pill and Call Now button below it (they stretch to
   the same flex column, so plain text-align does it). The arrow was revised
   AGAIN later that day — it now uses the drawer's own APPEARING pattern
   (same ::after as .city-nav-list a above: space always reserved, opacity 0
   at rest, fades in on hover; on touch it simply never shows), replacing an
   always-visible icon with a 3px nudge. The markup carries no <i> icon —
   the arrow lives entirely here.
   It appears on every page EXCEPT the homepage, which is done in the
   MARKUP — the generator strips it from index.html's drawer (nav-drawer
   sync, generate.py) — rather than by hiding it with CSS or JS. The flex
   column above supplies the gap beneath it. */
.city-nav-home-link {
    text-align: left;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--orange);
}
.city-nav-home-link::after {
    content: "\f061";                     /* fa-arrow-right */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    font-size: 0.8em;
    display: inline-block;                /* transforms need a box */
    margin-left: 0.4em;                   /* city rows get this from flex gap */
    /* inherit, not a fixed colour (owner request, July 28 2026): the arrow
       only ever shows while hovered, and on hover the text is WHITE — so the
       arrow follows the text by construction. (The city links' arrows stay
       fixed orange; this link differs because its hover colour differs.) */
    color: inherit;
    opacity: 0;
    transform: translateX(-0.3em);
    transition: var(--ease);
}
.city-nav-home-link:hover {
    color: var(--white);
}
.city-nav-home-link:hover::after {
    opacity: 1;
    transform: translateX(0);
}

/* "View All Areas" — a dashed, translucent pill (not another solid
   orange/blue button) with a map icon. Same corner radius as the Call
   Now button below it. Fills and brightens on hover so it reads as
   clearly interactive, not a quiet text link. */
.city-nav-all-link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    white-space: nowrap;      /* keep icon + text on one line */
    text-align: center;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--white);
    background: rgba(255, 255, 255, 0.08);
    border: 1.5px dashed rgba(255, 255, 255, 0.55);
    border-radius: var(--radius);    /* matches the Call Now button's corner radius */
    padding: 0.85rem;
    transition: var(--ease);
}
.city-nav-all-link .fa {
    color: var(--orange);
    font-size: 0.95rem;
}
.city-nav-all-link:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: var(--orange);
    border-style: solid;
}

/* Call Now inside drawer — reverted to its original styling */
.call-btn-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background: var(--orange);
    color: var(--text-on-orange);
    padding: 0.85rem;
    border-radius: var(--radius);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1rem;
    transition: var(--ease);
}
.call-btn-nav:hover {
    background: var(--orange-dark);
    color: var(--text-on-orange);
}


/* =============================================
   ABOUT US SECTION
   Background: dark gray
   ============================================= */
.about-section {
    background: var(--bg-dark);
    color: var(--white);
    padding: var(--section-y) var(--space-md);
    position: relative;
    overflow: hidden;
}

.about-content {
    position: relative;
    z-index: 1;
}

/* About Us headline — Courier New */
.about-headline {
    font-family: 'Courier New', monospace;
    font-size: clamp(2.2rem, 6vw, 3.8rem);
    font-weight: 800;
    line-height: 1.1;
    /* Small gap: the .about-subtitle tagline sits right beneath and
       carries the section's bottom spacing instead. */
    margin-bottom: 0.4rem;
    text-align: center;
    color: var(--white);
    letter-spacing: -1px;
}

/* Tagline under the About headline — same treatment as .section-subtitle */
.about-subtitle {
    text-align: center;
    color: rgba(255, 255, 255, 0.88);
    font-size: 1.05rem;
    margin: 0 0 var(--space-lg);
}

/* Full-width text layout */
/* Two columns: About text on the left, team photo on the right. The
   existing responsive rule further down collapses this to a single
   column on small screens (photo stacks below the text). */
.about-grid {
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    gap: var(--space-lg);
    align-items: start; /* text top sits level with the top of the team photo */
    margin-bottom: var(--space-xl);
}

/* Team photo — rounded corners only; deliberately NO border or shadow
   accents (owner's design call, July 2026). */
.about-photo {
    margin: 0;
    min-width: 0;
    /* Markup order is photo-then-text (for the phone float layout);
       these pins keep desktop exactly as it always was: text left,
       photo right (July 23 2026). */
    grid-column: 2;
    grid-row: 1;
}
.about-text {
    grid-column: 1;
    grid-row: 1;
}
.about-photo img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-lg);
}
/* The About copy's Show more / Show less control is a PHONE-ONLY
   affordance (the collapse rules live in the mobile block; desktop
   shows the full text beside the photo, so there's nothing to
   reveal). Hidden here, unhidden there. */
.about-toggle {
    display: none;
}
.about-photo figcaption {
    margin-top: 0.5rem;
    text-align: center;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.65);
}

.about-text {
    font-size: 1rem;
    line-height: 1.75;
    color: rgba(255, 255, 255, 0.88);
}

.about-text p {
    margin-bottom: var(--space-sm);
}

.about-text p:first-child {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--white);
    margin-bottom: 1rem;
}

/* Text link from the About copy down to the before & after gallery — same
   orange arrow-link language as the blog's "View All" links (hover: text
   goes white, arrow nudges right). */
.about-gallery-link {
    /* inline (not inline-flex) on purpose: when the link text wraps on
       narrow screens, flex pushed the arrow to the far right margin —
       inline keeps it snug after the last word, matching the site's
       other arrows (mobile pass 1, July 23 2026). Desktop rendering is
       identical (the link never wraps there). */
    display: inline;
    margin-top: 0.25rem;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--orange);
}
.about-gallery-link .fa {
    font-size: 0.8rem;
    margin-left: 0.4rem;
    transition: transform 0.2s ease;
}
.about-gallery-link:hover {
    color: var(--white);
}
.about-gallery-link:hover .fa {
    transform: translateX(3px);
}

/* Stats boxes */
.about-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
}

.stat-box {
    background: rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-lg);
    padding: 1.2rem var(--space-sm);
    text-align: center;
    border: 1px solid rgba(255, 255, 255, 0.2);
}

.stat-number {
    font-family: var(--font-heading);   /* Courier New — matches the rest of the site */
    font-size: 2.2rem;
    font-weight: 800;
    color: var(--orange);
    line-height: 1;
    margin-bottom: 0.4rem;
}

.stat-label {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.72);
    font-weight: 600;
}

/* CTA buttons below about text */
.about-cta {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
}

/* Slightly larger CTA buttons */
.about-cta .btn {
    font-size: 1.15rem;
    padding: 1.05rem 2.1rem;
}


/* =============================================
   BOOKING SECTION
   Background: dark grey (--bg-dark)
   Form card: white with orange top + left border
   ============================================= */
.booking-section {
    background: var(--bg-dark);
    padding: var(--section-y) 0;
}

.booking-form {
    background: var(--white);
    border-radius: var(--radius-lg);
    padding: var(--space-md) var(--space-lg);
    box-shadow: var(--shadow-md);
    max-width: 860px;
    margin: 0 auto;
    border-top: 5px solid var(--orange);    /* orange accent bars — top + left */
    border-left: 5px solid var(--orange);
}

.booking-step-continue {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-sm);
}
/* Same guard as .form-row[hidden] above: the flex rule would otherwise
   override the `hidden` attribute set by the emergency reroute
   (script.js syncEmergency), leaving the Continue button visible. */
.booking-step-continue[hidden] {
    display: none;
}

/* Step 2 progressive reveal — the CSS grid 0fr→1fr trick
   animates the container's height smoothly with no fixed
   max-height guesswork. The inner wrapper must have
   overflow:hidden and min-height:0 for the collapse to work. */
.booking-step-2 {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.45s ease, opacity 0.4s ease;
}
.booking-step-2.open {
    grid-template-rows: 1fr;
    opacity: 1;
    margin-top: var(--space-sm);
}
.booking-step-2-inner {
    overflow: hidden;
    min-height: 0;
}

.form-row {
    display: flex;
    gap: 1rem;
    margin-bottom: 0.9rem;
}
/* Without this, the flex rule above would override the `hidden`
   attribute and the row would stay visible. Needed by the emergency
   reroute, which hides the date/time/new-customer row (script.js
   syncEmergency). */
.form-row[hidden] {
    display: none;
}

.form-group {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* Anchor each field to the bottom of its group. Fields in a row stretch
   to equal height, so when some labels wrap to more lines than others
   (on narrow/tall windows) every input and dropdown still lines up along
   the same baseline — fully responsive to any window shape. */
.form-group input,
.form-group select,
.form-group textarea {
    margin-top: auto;
}

/* One explicit height for every single-line field so the date input and
   the dropdowns are always exactly the same height as one another. */
.form-group input,
.form-group select {
    height: 2.75rem;
}

/* Double-width field */
.form-group--wide { flex: 2; }

/* Narrow field (state, zip) */
.form-group--small { flex: 0.6; }

/* Section label within the form */
.form-section-label {
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--blue);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: var(--space-sm) 0 0.5rem;
    padding-bottom: 0.3rem;
    border-bottom: 2px solid var(--grey);
}

label {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-dark);
}

input,
select,
textarea {
    width: 100%;
    padding: 0.65rem 0.9rem;
    border: 1.5px solid var(--grey-mid);
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-size: 1rem;
    color: var(--text-dark);
    background: #f8f8f8;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}

input:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 3px rgba(58, 100, 158, 0.14);
    background-color: var(--white);   /* background-color (not the shorthand) so the custom select arrow survives focus */
}

/* Dropdown arrows — replace the native OS arrow with a custom chevron so
   it can be positioned the same distance from the right edge (0.9rem) as
   the option text sits from the left (0.9rem), for a balanced look. The
   native pop-up list still opens normally; only the closed box is restyled.
   Applies on desktop and mobile for a consistent appearance. */
select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23555555' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.9rem center;
    background-size: 14px;
    padding-right: 2.2rem;            /* keeps option text clear of the arrow */
    cursor: pointer;
}

/* Date field — iPhones/iPads render date inputs with their own native
   look, ignoring the input styles above (no border, own size/shape) and
   letting the box spill outside the form card. Stripping the native
   appearance makes the date field obey the same styling as every other
   field. Tapping it still opens the phone's native date picker. */
input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    display: block;
    min-width: 0;
    text-align: left;
}

/* iOS centers the chosen date inside the field — align it left to match
   the text in all the other fields. */
input[type="date"]::-webkit-date-and-time-value {
    text-align: left;
    margin: 0;
}

/* Phones/tablets leave an EMPTY date field completely blank (desktop
   browsers show a "mm/dd/yyyy" hint), which looks broken next to the
   dropdowns' "Select a…" placeholders. Supply our own hint text until a
   date is picked (the field is required, so empty = :invalid). Applies
   only to touch devices; desktop browsers show their own hint text and
   ignore ::before on inputs anyway. */
@media (pointer: coarse) {
    input[type="date"][required]:invalid::before {
        content: 'Select a date\2026';
    }
}

/* Booking fields turn bold once they hold a value. The .is-filled
   class is toggled by JS (see script.js) so this works uniformly for
   text inputs, the date picker, dropdowns, and the textarea. */
.booking-form .is-filled {
    font-weight: 700;
}

/* State field is locked to AZ — always bold, and inert to the mouse
   so it doesn't look like a broken/editable input. */
.state-locked {
    pointer-events: none;
    font-weight: 700;
}

textarea {
    resize: vertical;
    min-height: 100px;
}

/* Submission status message */
/* Fallback shown INSTEAD of the booking form when JS is off (<noscript>)
   or script.js failed to initialize (the inline watchdog in index.html).
   Catalog A1, _generator/failure-modes.md. */
.booking-js-fallback {
    background: var(--blue-dark);
    /* Same accent language as the site's other cards: orange top + left
       edges with a soft shadow (not a full four-sided border). */
    border-top: 4px solid var(--orange);
    border-left: 4px solid var(--orange);
    box-shadow: var(--shadow-sm);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    margin: 0 0 var(--space-md);
    font-size: 1.05rem;
    line-height: 1.7;
    color: var(--white);
    text-align: center;
}
.booking-js-fallback .fa-wrench,
.booking-js-fallback .fa-phone {
    color: var(--orange);
    margin-right: 0.35rem;
}

/* The emergency reroute box (shown when "Emergency Service" is picked —
   see index.html #booking-emergency-note) reuses the fallback styling
   above; it only needs breathing room from the fields above it. */
.booking-emergency {
    margin-top: var(--space-md);
}

/* =============================================
   BOOKING CONFIRMATION PANEL (owner request, 14 Sep 2026; restyled the
   same day to the owner's spec)
   After a successful request script.js adds .is-confirmed to the form:
   the two steps (and the emergency box) hide, the card turns the same
   navy as the Get In Touch card (orange top/left accents stay), the
   headline sentence sits in a green card, and "Request another booking"
   is the site's standard orange button, right-aligned. Class-driven on
   purpose — the `hidden` attribute loses to .booking-step-2's own
   display:grid (failure-modes C2 trap). Tokens only.
   ⚠️ Focus rings: the form's navy ring (WCAG 1.4.11 fix above) would be
   navy-on-navy here, so the confirmed state takes the orange ring —
   the same exception the emergency box uses. */
.booking-confirmation {
    display: none;
}
.booking-form.is-confirmed .booking-step,
.booking-form.is-confirmed .booking-step-2,
.booking-form.is-confirmed .booking-emergency {
    display: none;
}
.booking-form.is-confirmed {
    background: var(--blue-dark);   /* same navy as .getintouch-card */
    color: var(--white);
}
.booking-form.is-confirmed .booking-confirmation {
    display: block;
    padding-bottom: var(--space-sm);
}
.booking-confirmation .form-section-label {
    color: var(--orange-on-dark);            /* 4.60:1 on navy */
    border-bottom-color: rgba(255, 255, 255, 0.25);
}
.booking-confirmation-lead {
    background: var(--success-bg);
    color: var(--success-text);
    border: 1px solid var(--success-border);
    border-radius: var(--radius);
    padding: var(--space-sm) var(--space-md);
    font-family: var(--font-heading);
    /* Monospace heading font runs wide. Scale with the viewport so the line
       fills the green card on phones without becoming a tall slab, and keep the
       approved 1.25rem on desktop (clamp max). The 1.1rem floor is the largest
       size that still wraps a typical booking to ~4 lines on mainstream phone
       widths (375-430px); narrower screens / long service names wrap more — an
       exact line count can't be pinned because it rides both width and copy
       length. Matches the site's heading clamp idiom. */
    font-size: clamp(1.1rem, 4vw, 1.25rem);
    font-weight: 700;
    line-height: 1.4;
    margin: var(--space-sm) 0 var(--space-md);
}
.booking-confirmation-next {
    font-size: 1.05rem;
    line-height: 1.6;
    color: var(--white);
    margin: 0 0 var(--space-md);
}
.booking-confirmation-next a {
    color: var(--orange-on-dark);
    font-weight: 700;
}
.booking-confirmation-next a:hover {
    color: var(--white);
}
.booking-confirmation-actions {
    display: flex;
    justify-content: flex-end;
}
.booking-form.is-confirmed a:focus-visible,
.booking-form.is-confirmed button:focus-visible,
.booking-form.is-confirmed [tabindex]:focus-visible {
    outline-color: var(--orange);
}

/* Honeypot spam trap (see the hp-field comment in index.html): parked
   far off-screen rather than display:none, so form-filling bots still
   "see" and complete it while humans and screen readers never do. */
.booking-form .hp-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* =============================================
   ADDRESS SUGGESTIONS DROPDOWN (Places migration, July 2026)
   Our own suggestion list for Google's NEW Places system, anchored to
   the address field's .form-group (script.js initAddressAutocomplete,
   Layer 1). The classic fallback renders Google's own .pac-container
   instead — the two can never appear together.
   ============================================= */
.form-group.addr-enhanced {
    position: relative;
}
.addr-suggest {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin-top: 2px;
    background: var(--white);
    border: 1px solid var(--grey);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    z-index: 60;
    max-height: 250px;
    overflow-y: auto;
    text-align: left;
}
.addr-suggest-item {
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    font-size: 0.9rem;
    line-height: 1.4;
}
.addr-suggest-item.is-active {
    background: rgba(235, 149, 74, 0.14);   /* soft orange, matches hover language */
}
.addr-suggest-main {
    font-weight: 600;
}
.addr-suggest-secondary {
    font-size: 0.8rem;
    color: var(--text-mid);
}
/* Google's required attribution when Places data shows without a map */
.addr-suggest-attrib {
    padding: 0.3rem 0.75rem;
    font-size: 0.68rem;
    color: var(--text-mid);
    text-align: right;
    border-top: 1px solid var(--grey);
}
.booking-js-fallback a {
    color: var(--orange);
    font-weight: 700;
    font-size: 1.2rem;
    white-space: nowrap;
}
.booking-js-fallback a:hover {
    color: var(--white);
}
/* ⚠️ LOAD-BEARING: the emergency box's "Call Now" BUTTON (owner, 2026-09-08)
   must keep the standard .btn-call colors. Without this override, the
   `.booking-js-fallback a` link rule above outranks `.btn-call`
   (class+element beats lone class) and would paint the button's text
   ORANGE on the orange fill — invisible — and WHITE on the hover orange
   (2.99:1, fails WCAG AA). Also restores .btn's font size over the link
   rule's 1.2rem, and gives the button breathing room between the two
   text lines. */
.booking-js-fallback a.btn,
.booking-js-fallback a.btn:hover {
    color: var(--text-on-orange);
    font-size: 1rem;
    margin: 0.4rem 0 0.55rem;
}
/* ...and the phone ICON inside that button too: the box's orange
   `.fa-phone` accent rule above still caught it, and orange on the
   button's orange fill is invisible (owner spotted it on the Sep 9
   preview — only a faint ghost appeared on hover). `inherit` keeps the
   icon the same color as the button's own text at rest AND on hover;
   the margin is cleared because .btn's flex gap already spaces it. */
.booking-js-fallback a.btn .fa-phone {
    color: inherit;
    margin-right: 0;
}

.booking-status {
    padding: 0.8rem 1rem;
    border-radius: var(--radius);
    margin: 0.75rem 0;
    font-weight: 600;
    font-size: 0.95rem;
    display: none;
}
.booking-status.success {
    display: block;
    background: var(--success-bg);
    color: var(--success-text);
    border: 1px solid var(--success-border);
}
.booking-status.error {
    display: block;
    background: #f8d7da;
    color: #721c24;
    border: 1px solid #f1b0b7;
}

.form-disclaimer {
    font-size: 0.8rem;
    color: var(--text-mid);    /* dark text — the disclaimer sits on the white form card */
    text-align: left;
    margin-top: 0.5rem;
}

/* Passive consent notice above the Request Booking button (owner request,
   July 2026 — "by requesting…" wording, no checkbox). Same small print as
   .form-disclaimer, with room to breathe before the button. */
.form-consent {
    font-size: 0.8rem;
    color: var(--text-mid);
    line-height: 1.6;
    text-align: left;
    margin: 0.75rem 0;
}
.form-consent a {
    color: var(--blue);
    text-decoration: underline;
}
.form-consent a:hover {
    color: var(--orange);
}


/* =============================================
   BUSINESS HOURS & CONTACT SECTION
   Background: dark grey
   ============================================= */
.hours-section {
    background: var(--bg-dark);
    padding: var(--section-y) 0;
}

/* Base card shell */
.info-card {
    border-radius: var(--radius-lg);
    padding: var(--space-lg) var(--space-md);
    display: flex;
    flex-direction: column;
}
.info-card .section-title--left {
    margin-bottom: var(--space-md);
}
.info-card-body {
    display: flex;
    flex-direction: column;
}

/* Get In Touch — single centered card on dark blue with an orange
   accent bar across the top and a soft orange-tinted glow. */
.getintouch-card {
    max-width: 560px;
    margin: 0 auto;
    background: var(--blue-dark);
    color: var(--white);
    border-top: 6px solid var(--orange);   /* orange accent bars — top + left */
    border-left: 6px solid var(--orange);
    box-shadow: var(--shadow-md);          /* standard card shadow (no orange halo) */
    text-align: center;
}
.getintouch-card .section-title--left {
    color: var(--white);
    text-align: center;
}

/* Buttons + social row + hours note spacing inside the card */
.getintouch-card .info-card-body {
    gap: var(--space-sm);
    align-items: center;
}
.getintouch-card .btn-call--large {
    width: 100%;
    margin: 0;
}

/* Icon sits inline with the top line as one centered row; both
   text lines are centered. */
.getintouch-card .btn-call--large span {
    text-align: center;
}
/* Center the icon vertically against the text (centers aligned, not
   baselines), so the icon's bottom sits a little lower than the text. */
.getintouch-card .btn-call--large strong {
    display: flex;
    align-items: center;
    justify-content: center;
}
.getintouch-card .btn-call--large strong i {
    margin-right: 0.4rem;
}
/* The phone glyph is naturally taller than the envelope at the same
   font size; shrink it a touch so both icons read as equal height. */
.getintouch-card .btn-call--large .fa-phone {
    font-size: 0.9rem;
}

/* Hours table */
.hours-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: var(--space-sm);
}
.hours-table th,
.hours-table td {
    padding: 0.6rem 0.5rem;
    text-align: left;
    border-bottom: 1px solid var(--grey-mid);
    font-size: 0.94rem;
}
.hours-table th {
    font-weight: 700;
    color: var(--blue);
    width: 45%;
}

/* Emergency row — orange accent (10%) */
.row-emergency th,
.row-emergency td {
    color: var(--orange);
    font-weight: 700;
    border-top: 2px solid var(--orange);
}

/* Large Call Now button in contact card */
.btn-call--large {
    width: 100%;
    justify-content: center;
    font-size: 1.1rem;
    padding: 1.1rem 1.5rem;
    margin: var(--space-sm) 0 var(--space-md);
    gap: 0.85rem;
}
.btn-call--large span {
    text-align: left;
}
.btn-call--large strong {
    display: block;
    font-size: 1.2rem;
    line-height: 1.2;
}
.btn-call--large small {
    display: block;
    font-size: 0.85rem;
    font-weight: 400;
    opacity: 0.88;
    /* Tight line box (was inheriting ~1.7): the loose spacing made the
       small line's glyphs sit high in their box, so the whole label
       read as top-heavy with dead space under it — desktop and phone
       alike (owner catch, July 23 2026). */
    line-height: 1.25;
    margin-top: 0.15rem;
}

/* Social media links (Get In Touch card) — centered row */
.social-icons {
    display: flex;
    gap: 0.7rem;
    flex-wrap: wrap;
    justify-content: center;
}

/* Hours summary at the bottom of the Get In Touch card */
.getintouch-hours {
    margin-top: var(--space-xs);
    line-height: 1.55;
}
.getintouch-hours p {
    margin: 0;
}
.getintouch-hours .hours-everyday {
    color: var(--white);
    font-weight: 700;
}
.getintouch-hours .hours-emergency {
    color: var(--orange-on-dark);
    font-weight: 700;
}
/* Words that must never be separated by a line break (e.g. the
   emergency line's "Available 24/7" — owner request, July 23 2026). */
.keep-together {
    white-space: nowrap;
}
/* The emergency line is a tappable phone link (owner request, July 23
   2026): keeps its orange look, flips white on hover like the site's
   other orange links. */
.getintouch-hours .hours-emergency a {
    color: inherit;
    transition: var(--ease);
}
.getintouch-hours .hours-emergency a:hover {
    color: var(--white);
}

.social-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    color: var(--white) !important;
    font-size: 1.05rem;
    transition: var(--ease);
    flex-shrink: 0;
}
.social-icon:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-sm);
}

/* Platform brand colors */
.social-icon--facebook  { background: #1877F2; }
.social-icon--instagram { background: linear-gradient(45deg, #f09433, #e6683c, #dc2743, #cc2366, #bc1888); }
.social-icon--yelp      { background: #D32323; }
.social-icon--nextdoor  { background: #00B246; }
.social-icon--youtube   { background: #FF0000; }


/* =============================================
   GOOGLE REVIEWS SECTION
   Background: dark grey (--bg-dark)
   ============================================= */
.reviews-section {
    background: var(--bg-dark);
    padding: var(--section-y) 0;
}

/* Find Us — the business-location map's own section (owner request,
   14 Aug 2026). Same dark ground and spacing as every other section, so it
   sits in line with them; the map wrapper keeps its own sizing. */
.find-us-section {
    background: var(--bg-dark);
    padding: var(--section-y) 0;
}

/* Title + badge row */
/* Heading on the left, Google badge on the right, one row. This is the
   DESKTOP layout and it is deliberately untouched — a July 28 2026 pass
   briefly made this a full-width column at every size and the owner asked
   for desktop to go back exactly as it was. The full-width badge lives in
   the phone block only; see the ≤620px override. */
.reviews-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
    margin-bottom: var(--space-lg);
}

.reviews-title {
    font-family: var(--font-heading);
    font-size: clamp(2rem, 4vw, 3rem);
    font-weight: 800;
    color: var(--white);
    margin: 0;
    line-height: 1.1;
}

/* Title + subtitle stack on the left; rating badge stays to the right */
.reviews-heading {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.reviews-subtitle {
    font-size: 1.05rem;
    color: rgba(255, 255, 255, 0.78);
    margin: 0;
}

/* Overall rating badge */
/* Dark-blue badge (matches the review cards). Logo + stars are orange;
   score + count are white, with the count aligned under the rating text. */
.google-rating-badge {
    display: flex;
    align-items: center;           /* logo centered against the rating block */
    gap: 1.1rem;
    background: var(--blue-dark);
    border-radius: var(--radius-xl);
    border-top: 4px solid var(--orange);   /* orange accent bars — top + left */
    border-left: 4px solid var(--orange);
    padding: 1rem 1.75rem;
    box-shadow: var(--shadow-sm);
    flex-shrink: 0;
}

.google-logo {
    width: 3.4rem;
    height: 3.4rem;
    flex-shrink: 0;
}

/* Score, stars, and review count stacked + left-aligned together */
.rating-info {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.overall-rating {
    font-family: var(--font-heading);
    font-size: 2.4rem;
    font-weight: 800;
    color: var(--white);
    line-height: 1;
}

.star-rating {
    display: flex;
    gap: 2px;
    color: var(--orange);          /* orange */
    font-size: 1.1rem;
}

.review-count {
    font-size: 0.82rem;
    color: var(--white);
    font-weight: 700;              /* bold */
}
/* The "See our reviews on Google" link that shows here BEFORE the live rating
   loads (and if it never does) sits on the navy rating badge. Left to the
   site's default light-blue link colour it all but vanished (1.53:1). WHITE
   (owner request, 14 Aug 2026) reads cleanly on the navy at 9.21:1 — the
   owner specifically wanted THIS link white while the widget-failure link
   ("Read our reviews on Google") stays orange. The live rating count that
   normally replaces this text is already plain white, so the two states now
   match. */
.review-count a {
    color: var(--white);
}

/* Review cards grid — fixed 3 columns */
.reviews-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.1rem;
    margin-bottom: var(--space-lg);
}

.review-card {
    background: var(--blue-dark);
    border-radius: var(--radius-lg);
    padding: 1.2rem;
    box-shadow: var(--shadow-sm);
    border-top: 4px solid var(--orange);   /* orange accent bars — top + left */
    border-left: 4px solid var(--orange);
}

.review-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.65rem;
}

.reviewer-avatar {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.reviewer-avatar-placeholder {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: var(--blue);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1.1rem;
    flex-shrink: 0;
}

.reviewer-info strong {
    display: block;
    font-size: 0.92rem;
    color: var(--white);
    line-height: 1.3;
}

.reviewer-info span {
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.65);
}

.review-stars {
    display: flex;
    gap: 2px;
    color: var(--orange);
    margin-bottom: 0.5rem;
    font-size: 0.88rem;
}

.review-text {
    font-size: 0.88rem;
    color: rgba(255, 255, 255, 0.85);
    line-height: 1.55;
}

.review-read-more {
    background: none;
    border: none;
    color: var(--orange-on-dark);
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0;
    margin-top: 0.35rem;
    display: inline-block;
    cursor: pointer;
    transition: color 0.2s;
}
.review-read-more:hover { color: var(--white); }

.reviews-loading {
    text-align: center;
    /* Orange for the fallback/failure states (owner request, July 2026) */
    color: var(--orange);
    padding: var(--space-lg);
    grid-column: 1 / -1;
    font-size: 1rem;
    line-height: 1.9;
}
.reviews-loading a {
    color: var(--orange);
    font-family: var(--font-heading);
    font-weight: 700;
}
.reviews-loading a .fa {
    font-size: 0.85rem;
    transition: transform 0.2s ease;
}
.reviews-loading a:hover {
    color: var(--white);
}
.reviews-loading a:hover .fa {
    transform: translateX(3px);
}

/* 6th slot CTA card */
.review-card-cta {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 0.75rem;
    background: var(--blue-dark);
    border-radius: var(--radius-lg);
    padding: 1.2rem;
    box-shadow: var(--shadow-sm);
    border-top: 4px solid var(--orange);   /* orange accent bars — top + left */
    border-left: 4px solid var(--orange);
}

.review-card-cta p {
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.92rem;
    line-height: 1.5;
    margin: 0;
}

.review-card-cta .btn-review {
    font-size: 0.85rem;
    padding: 0.5rem 1rem;
}

/* Google Maps embed iframe container */
.maps-embed-wrapper {
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-md);
    border-top: 5px solid var(--blue);     /* blue accent bars — top + left */
    border-left: 5px solid var(--blue);
    margin-bottom: var(--space-lg);
}

.maps-embed-wrapper iframe {
    display: block;
    width: 100%;
    height: 400px;
}

/* Leave a Review box — blue background (30%) */
.leave-review-box {
    background: var(--blue);
    color: var(--white);
    border-radius: var(--radius-lg);
    padding: var(--space-lg) var(--space-md);
    text-align: center;
}

.leave-review-box h3 {
    font-family: var(--font-heading);
    font-size: 1.45rem;
    margin-bottom: 0.4rem;
}

.leave-review-box p {
    color: rgba(255, 255, 255, 0.82);
    margin-bottom: var(--space-md);
    max-width: 460px;
    margin-left: auto;
    margin-right: auto;
    font-size: 0.95rem;
}

.leave-review-box .btn-review {
    margin: 0 auto;
}


/* =============================================
   PHOTO GALLERY SECTION
   Background: dark grey (--bg-dark), same as every section
   ============================================= */
.gallery-section {
    background: var(--bg-dark);
    padding: var(--section-y) 0;
}

/* (The "Stories from the Field" story card lives on the CITY pages —
   styles in city.css, markup in _generator/city-template.html.) */

/* ---- Before / After carousel ---- */
.ba-carousel {
    position: relative;
}

/* Clipping window — slides move within this */
.ba-viewport {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-lg);

    /* ⚠️ THE SLIVER OF THE PREVIOUS PHOTO DOWN THE LEFT EDGE — DO NOT REMOVE.
       Reported again 11 August 2026 from a phone; it has come back more than
       once before, because every previous fix treated it as an arithmetic
       mistake and the arithmetic was never wrong.

       What actually happens: this window is rarely a whole number of pixels
       wide. On a 393px phone it measures 327.4453125. The track moves exactly
       one window per slide, and getBoundingClientRect confirms the slides
       abut perfectly — zero overlap, every slide, every time. But 327.4453125
       multiplied by a phone's 3x pixel density is 982.336 DEVICE pixels, so
       the seam between two slides never lands on a real pixel. When the
       browser composites the moving layer it samples across that boundary and
       paints a hairline of the neighbouring photo.

       `overflow: hidden` does not stop it: the fringe is produced BY the
       compositor at the very edge it is clipping to. So the seam is moved out
       of sight instead — one pixel of the window is thrown away on each side,
       which is far more than any fringe and far less than the clearance the
       photos already keep (they are centred and capped below full width, see
       .ba-single/.ba-pair). `round` keeps the corner radius that
       `overflow: hidden` was giving us.

       Guarded by run_site_checkup() in _generator/generate.py, which fails the
       build if this declaration goes missing. */
    clip-path: inset(0 1px round var(--radius-lg));
    /* Horizontal drags here belong to the gallery, not to the page (fix,
       July 28 2026 — swipes were scrolling the page instead of changing
       slides). `pan-y` still lets a vertical drag scroll the page normally,
       so visitors can always scroll PAST the gallery; it only stops the
       browser treating a sideways drag as a page pan. The matching axis
       lock lives in initCarouselSwipe() in script.js — this declares the
       intent before any JS has loaded, that enforces it per gesture.
       `pinch-zoom` is listed explicitly and must STAY: a bare `pan-y` also
       switches off pinch-to-zoom over this element, which would mean anyone
       who zooms to see a photo loses that the moment their fingers land on
       the gallery. The swipe handler ignores multi-touch for the same
       reason. (An older browser that rejects the two-value syntax drops the
       whole declaration and behaves as it did before this fix — the JS axis
       lock still does the real work there.) */
    touch-action: pan-y pinch-zoom;
}

/* The sliding track holds all steps side by side */
.ba-track {
    display: flex;
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

/* One step fills the viewport width; content centers within it */
.ba-slide {
    min-width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;

    /* CLEARANCE FOR THE SEAM CLIP on .ba-viewport — read that comment first.
       The clip throws away 1px at each edge of the window, so nothing may sit
       flush against it. This padding is what guarantees that, and it lives
       HERE, on the slide, rather than on the photos: a before/after PAIR is
       sized to exactly 100% on phones (`50% - 0.25rem` twice plus a 0.5rem
       gap, further down this file), so capping the photos would have to be
       done again at every breakpoint and would be missed the next time one is
       added — it was missed on the first attempt at this fix.
       `box-sizing: border-box` is global here, so the slide's OUTER width is
       still exactly one window and the track's 100%-per-step arithmetic is
       untouched. */
    padding: 0 4px;
}

/* Before + After photos side by side — each at its OWN native size */
.ba-pair {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1rem;
    width: 100%;
}

/* Caption — sits in the controls row between the arrows */
.ba-caption {
    text-align: center;
    color: rgba(255, 255, 255, 0.85);
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 600;
    margin-top: 0;
    /* RESERVED HEIGHT (July 24 2026): captions vary in length, and since
       this sits in the same row as the arrows, a longer one used to grow
       the row and shove the dots + arrows downward as slides changed.
       Reserving two lines' worth keeps the controls perfectly still —
       the vertical twin of the fixed width on .ba-controls-center. */
    line-height: 1.5;
    min-height: 3em;
}

.ba-photo {
    /* CAROUSEL PHOTOS ARE FRAMELESS (owner request, July 2026): no matting,
       no background box — the photograph itself IS the element, shown whole
       at its own native aspect ratio. This rule applies ONLY to the moving
       galleries (.ba-*); photos elsewhere on the site keep their standard
       frames (see _generator/design-guide.md §5). */
    position: relative;   /* anchors the Before/After badge */
}

.ba-pair .ba-photo {
    max-width: calc(50% - 0.5rem);   /* two-up: each photo keeps to its half */
}

/* One-off slides (July 23 2026): a single centered photograph — techs at
   work, finished jobs — sharing the pairs' frameless treatment and the
   same max-height clamp on the image, so slide heights stay in family. */
.ba-single {
    display: flex;
    justify-content: center;
    width: 100%;
}
.ba-single .ba-photo {
    max-width: 88%;
}

.ba-photo img,
.ba-photo video {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: clamp(280px, 34vw, 440px);
    border-radius: var(--radius-lg);   /* corner rounding only — no border */
    box-shadow: var(--shadow-sm);
}
/* The one-off VIDEO slide (owner, 10 Sep 2026) shares the frameless photo
   treatment above; it is never tappable-to-zoom, so it gets no zoom cursor. */
.ba-photo video {
    object-fit: contain;
    background: var(--blue-dark);   /* letterbox matches the placeholder tone */
}

/* Before / After badge */
.ba-badge {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 2;
    background: var(--orange);
    color: var(--white);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.3rem 0.75rem;
    border-radius: var(--radius);
}

/* Placeholder slot shown until a real photo is added — placeholders DO
   keep the dashed frame box (there's no photo to shape them otherwise) */
.ba-photo--placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    width: min(480px, 100%);
    aspect-ratio: 4 / 3;
    background: var(--blue-dark);
    border: 2px dashed rgba(255, 255, 255, 0.3);
    border-radius: var(--radius-lg);
    color: rgba(255, 255, 255, 0.6);
}

.ba-placeholder-icon {
    font-size: 2.2rem;
    color: rgba(255, 255, 255, 0.4);
}

.ba-placeholder-text {
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 600;
}

/* Controls row BELOW the photos: [prev arrow] [caption + dots] [next arrow]
   — arrows deliberately never overlay the photographs (owner request,
   July 2026). */
.ba-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-md);
    margin-top: var(--space-sm);
}
.ba-controls-center {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* FIXED width (not content-sized) so the arrows sit in the SAME spot on
       every slide — captions vary in length, and without this the buttons
       would drift and the user would have to chase them between clicks
       (owner request, July 2026). Long captions wrap instead of pushing. */
    width: clamp(200px, 50vw, 420px);
}

/* The arrows ship with the `hidden` attribute (dead buttons without JS —
   catalog C2); this explicit rule is REQUIRED because the display:flex
   below would otherwise override the browser's built-in [hidden] handling
   and the arrows would show anyway (caught by the owner, July 2026). */
.ba-arrow[hidden] {
    display: none;
}

.ba-arrow {
    flex-shrink: 0;
    width: 46px;
    height: 46px;
    border: none;
    border-radius: 50%;
    background: var(--blue);
    color: var(--white);
    font-size: 1.8rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-sm);
    transition: var(--ease);
}
.ba-arrow:hover {
    background: var(--orange);
    transform: scale(1.08);
}

/* Progress dots below the carousel — the active one widens
   into a pill with a fill bar that tracks the time until the
   next slide (like a video/story progress indicator). */
.ba-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    /* 14px, not 0.5rem (24 Aug 2026): tap-target spacing. The dots are 11px —
       below the 24px accessibility minimum — but an undersized target passes
       when its CENTER sits ≥24px from its neighbours'. 11px dot + 14px gap =
       25px centre-to-centre. Growing the dots themselves would mean reworking
       the active pill's fill animation; the wider gap is invisible-risk. */
    gap: 14px;
    margin-top: 0.6rem;
}

.ba-dot {
    position: relative;
    width: 11px;
    height: 11px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.3);
    overflow: hidden;
    transition: width 0.4s ease;
}
.ba-dot.active {
    width: 46px;     /* widen into a pill */
}

/* The fill bar — animates 0 → 100% over one interval.
   Only top/left are pinned (not right) so the width animation
   isn't over-constrained and can actually grow. */
.ba-dot-fill {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 0;
    background: var(--orange);
    border-radius: inherit;
}
.ba-dot.active .ba-dot-fill {
    animation: ba-fill var(--ba-interval, 5s) linear forwards;
}

/* Freeze the progress bar while the visitor hovers the carousel */
.ba-carousel.is-paused .ba-dot.active .ba-dot-fill {
    animation-play-state: paused;
}

@keyframes ba-fill {
    from { width: 0; }
    to   { width: 100%; }
}


/* =============================================
   FOOTER
   Background: dark blue (30%)
   ============================================= */
.site-footer {
    background: var(--blue-dark);
    color: rgba(255, 255, 255, 0.82);
    padding-top: var(--space-lg);
}

/* Three-column footer grid */
.footer-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    gap: var(--space-lg);
    padding-bottom: var(--space-lg);
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.footer-logo {
    width: 58px;
    height: 58px;
    object-fit: contain;
    margin-bottom: 0.6rem;
}

.footer-company-name {
    font-family: var(--font-heading);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--white);
    margin-bottom: 0.2rem;
}

.footer-tagline,
.footer-license {
    font-size: 0.82rem;
    color: rgba(255, 255, 255, 0.62);   /* 0.55 measured 4.03 on navy — just under AA; 0.62 = 4.6 */
    margin-top: 0.15rem;
}

/* Column headings — orange accent (10%) */
.footer-links h3,
.footer-contact h3 {
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--orange-on-dark);
    margin-bottom: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.footer-links ul li {
    margin-bottom: 0.28rem;   /* condensed so 7 links occupy ~the same height 6 did, keeping Quick Links level with the Contact column */
}

.footer-links a,
.footer-contact a {
    color: rgba(255, 255, 255, 0.75);
    font-size: 0.88rem;
    transition: color 0.2s;
    /* inline-flex so the keyboard focus outline is a clean RECTANGLE. As plain
       inline links, the taller icons (esp. the book+pen blog glyph) pushed the
       outline into a "bulb" protrusion on the icon side; centering icon + text
       in a flex box gives every footer link a tidy four-sided highlight.
       (owner request, 14 Aug 2026) */
    display: inline-flex;
    align-items: center;
}
.footer-links a:hover,
.footer-contact a:hover {
    color: var(--orange);
}

/* Quick Links icons (owner request, July 2026): every footer link
   carries a small orange icon. Fixed width so the labels line up in a
   clean column despite different glyph widths. .footer-blog-icon is the
   nav drawer's book+pen SVG (nav-blog-icon) reused at link size. */
.footer-links ul a i {
    color: var(--orange);
    display: inline-block;
    width: 1.25em;
    text-align: center;
    margin-right: 0.3rem;
}
.footer-links ul a .footer-blog-icon {
    color: var(--orange);
    /* The book+pen drawing carries empty padding inside its own square
       canvas, so it needs a taller box than the Font Awesome glyphs to
       LOOK the same size (owner-tuned, July 2026). Width stays 1.25em —
       the same slot as the other icons — so the label column stays put. */
    width: 1.25em;
    height: 1.5em;
    vertical-align: -0.45em;
    margin-right: 0.3rem;
}

.footer-contact p {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.45rem;
    font-size: 0.88rem;
}

.footer-contact .fa {
    color: var(--orange);
    width: 14px;
}

/* Footer social icon row */
.footer-social {
    display: flex;
    gap: 0.55rem;
    margin-top: 0.75rem;
}

.footer-social a {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.1);
    color: rgba(255, 255, 255, 0.75) !important;
    font-size: 0.85rem;
    transition: var(--ease);
}
.footer-social a:hover {
    background: var(--orange);
    color: var(--white) !important;
}

/* Bottom copyright bar */
.footer-bottom {
    background: rgba(0, 0, 0, 0.22);
    padding: 1rem var(--space-md);
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-lg);
    flex-wrap: wrap;
    text-align: center;
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.55);   /* 0.45 measured 3.72 on the darker bottom bar — 0.55 = 4.6 */
}

.footer-bottom a {
    color: rgba(255, 255, 255, 0.55);   /* 0.50 measured 4.22 — 0.55 = 4.7 */
}
.footer-bottom a:hover {
    color: var(--orange);
}


/* =============================================
   CUSTOM DATE PICKER  (desktop only)
   -----------------------------------------------
   A small calendar popup built by initCustomDatePicker()
   in script.js. It is layered on top of the real
   <input type="date"> — the native input still holds and
   submits the value, so if the JS ever fails the native
   browser picker takes over with no loss of function.
   Mobile (≤ 620px) keeps its native picker untouched:
   the trigger icon below is hidden and the calendar is
   never opened on small screens.
   ============================================= */

/* Our own calendar icon that opens the popup. Hidden by default
   (mobile); shown only on desktop inside the media query below. */
.date-trigger-icon {
    display: none;
    position: absolute;
    right: 0.6rem;
    bottom: 0.55rem;       /* sits inside the input, clear of the label above */
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    background: transparent
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23555555' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E")
        no-repeat center;
    cursor: pointer;
    border-radius: var(--radius);
    transition: background-color 0.2s ease;
}
.date-trigger-icon:hover {
    background-color: rgba(58, 100, 158, 0.1);
}

/* The popup calendar card. Matches the white cards: rounded corners,
   white background, no frame, blue accent bar across the top. */
.date-popup {
    position: absolute;
    /* Fully BELOW the field with a small gap (owner request, 8 Sep 2026) so
       the calendar never covers the date box — you can see what you picked or
       typed while it's open. (It previously overlapped the field on purpose,
       to sit higher on short viewports; the owner chose visibility over that.)
       While open it floats over whatever sits beneath, like any dropdown, and
       closes on selection, outside click, or Escape. */
    top: calc(100% + 0.4rem);
    left: 0;
    z-index: 50;
    width: 268px;
    max-width: calc(100vw - 2rem);
    background: var(--white);
    border: none;                          /* no thin frame */
    border-top: 5px solid var(--orange);   /* orange accent bars — top + left */
    border-left: 5px solid var(--orange);
    border-radius: var(--radius-lg);       /* rounded like the cards */
    box-shadow: var(--shadow-md);
    padding: 0.7rem;
    font-family: var(--font-body);
    user-select: none;
}
.date-popup[hidden] {
    display: none;
}

/* Header row: month/year label flanked by prev/next arrows */
.date-popup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.6rem;
}

.date-popup-title {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.92rem;
    color: var(--blue);
}

.date-popup-nav {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    color: var(--blue);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
}
.date-popup-nav:hover {
    background: rgba(58, 100, 158, 0.1);
}
.date-popup-nav:disabled {
    color: var(--grey);
    cursor: default;
    background: transparent;
}

/* Weekday header row (Su Mo Tu …) and the day grid share 7 columns */
.date-popup-weekdays,
.date-popup-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.date-popup-weekdays {
    margin-bottom: 4px;
}

.date-popup-weekday {
    text-align: center;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--grey-dark);
    padding: 0.2rem 0;
}

/* Individual day cells */
.date-popup-day {
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    color: var(--text-dark);
    font-family: var(--font-body);
    font-size: 0.84rem;
    font-weight: 700;              /* bold day numbers */
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.date-popup-day:hover {
    background: rgba(58, 100, 158, 0.12);
}

/* Empty leading cells before the 1st of the month */
.date-popup-day.is-empty {
    visibility: hidden;
    cursor: default;
}

/* Past / unavailable days (before today's minimum) */
.date-popup-day:disabled {
    color: var(--grey);
    cursor: default;
    background: transparent;
}

/* Today — orange accent so it stands out */
.date-popup-day.is-today {
    color: var(--orange-dark);
    font-weight: 700;
    position: relative;   /* anchors the emergency tooltip below */
}

/* Emergency tooltip on TODAY'S cell (owner request, July 2026): today is
   un-bookable because of the booking lead time, so hovering it explains
   what to do instead. Pure CSS — appears on hover, vanishes on mouse-off,
   nothing to click or dismiss. Works even though the cell is a disabled
   button (no pointer-events:none anywhere in this block — that would
   silence :hover). Text comes from the data-tip attribute script.js sets. */
.date-popup-day.is-today:hover::after {
    content: attr(data-tip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    width: 185px;
    background: var(--blue-dark);
    color: var(--white);
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.45;
    text-align: center;
    white-space: normal;
    padding: 0.5rem 0.7rem;
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    z-index: 20;
    pointer-events: none;   /* the bubble itself never traps the mouse */
}
/* Little arrow under the bubble */
.date-popup-day.is-today:hover::before {
    content: "";
    position: absolute;
    bottom: calc(100% + 3px);
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--blue-dark);
    z-index: 20;
    pointer-events: none;
}

/* The currently selected day — solid blue */
.date-popup-day.is-selected {
    background: var(--blue);
    color: var(--white);
    font-weight: 700;
}
.date-popup-day.is-selected:hover {
    background: var(--blue-dark);
}

/* Desktop only: reveal our icon, hide the browser's native date icon,
   and give the field room for our icon on the right. */
@media (min-width: 621px) {

    .form-group.date-enhanced {
        position: relative;
    }

    .form-group.date-enhanced input[type="date"] {
        padding-right: 2.5rem;
    }

    /* Hide the browser's built-in calendar icon — we supply our own */
    .form-group.date-enhanced input[type="date"]::-webkit-calendar-picker-indicator {
        display: none;
    }

    .date-trigger-icon {
        display: block;
    }

}


/* =============================================
   RESPONSIVE — TABLET  (≤ 900px)
   ============================================= */
@media (max-width: 900px) {

    .hours-contact-grid {
        grid-template-columns: 1fr;
    }

    .footer-grid {
        grid-template-columns: 1fr 1fr;
    }

    .footer-brand {
        grid-column: 1 / -1;
    }

}


/* =============================================
   RESPONSIVE — MOBILE  (≤ 620px)
   ============================================= */
@media (max-width: 620px) {

    .site-header {
        padding: 0.5rem 1rem;
        min-height: 64px;
    }

    .header-company-name { font-size: 1.3rem; }
    .header-tagline      { font-size: 0.78rem; }
    .header-logo-wrap    { width: 44px; height: 44px; }

    .menu-btn {
        left: 0.75rem;
        padding: 0.4rem 0.65rem;
        font-size: 0.82rem;
    }

    .header-call-btn {
        right: 0.75rem;
        padding: 0.4rem 0.65rem;
        font-size: 0.82rem;
    }

    /* Show only the icons on small screens — hide both button labels */
    .menu-label,
    .header-call-label { display: none; }

    .about-section {
        padding: var(--space-lg) var(--space-sm);
    }

    .about-grid {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    .about-cta {
        flex-direction: column;
        align-items: center;
    }

    .btn { width: 100%; justify-content: center; }

    .form-row {
        flex-direction: column;
    }

    .booking-form {
        padding: var(--space-sm) var(--space-sm);
    }

    /* Reviews header on phones. Started as the July 24 2026 owner layout —
       title across the top, then subtitle LEFT with a shrunken Google badge
       squeezed in on the RIGHT. REVISED July 28 2026: side by side, each
       got about 150px of a 375px screen, so the subtitle broke into "Kind
       words from the" / "community." and the badge's own label wrapped to
       "200+ Google" / "reviews" — two columns of stubs. They stack now, one
       per row, and both read on a single line at full width. The badge also
       gets its padding back, since it no longer has to survive in half a
       screen.
       `display: contents` dissolves the heading wrapper so the title and
       subtitle are independent rows of the header flex.
       The `text-align: center` below is LIVE again as of 11 August 2026: the
       quarantined left-justified-headings experiment that used to override it
       was removed that day, on the owner's decision, after they asked for the
       homepage headings to be centered on phones. Desktop was never part of
       that experiment and is untouched. */
    /* PHONES ONLY: the desktop row breaks into a column here — title,
       subtitle, then the Google badge stretched across the full width
       (owner request, July 28 2026). Desktop keeps its side-by-side row
       untouched; this override is the whole of that change. */
    .reviews-header {
        flex-direction: column;
        align-items: stretch;
        gap: 0.7rem;
    }
    .reviews-heading {
        display: contents;
    }
    .reviews-title {
        width: 100%;
        text-align: center;
        /* SAME SIZE AS THE OTHER BIG SECTION TITLES (owner request, July 28
           2026 — it was "much smaller than the others"): the identical clamp
           the Book a Service and gallery headings use (#booking/#gallery
           .section-title above), so all three track together at every phone
           width — 33px at 375px. This SUPERSEDES the shrunken one-line
           sizing tried earlier the same day: at this size the title wraps to
           two lines on phones ("Check Out Our" / "Google Reviews"), which
           matches both the desktop (accepted two-line, same day) and the
           gallery title (three lines at this size). If this clamp ever
           changes on the section titles, change it here too. */
        font-size: clamp(2.2rem, 6vw, 3.8rem);
    }
    .reviews-subtitle {
        flex: none;
        width: 100%;
        min-width: 0;
        font-size: 0.85rem;
        /* Centered on phones to sit under the centered .reviews-title above
           (owner request, 11 August 2026). It needs saying HERE rather than
           relying on the block that used to live at the end of this file:
           that block only ever named .section-subtitle and .about-subtitle,
           so this one was left-aligned by default and stayed that way when
           everything around it was centered. Desktop keeps the flex row with
           the title and subtitle ranged left beside the badge — the base rule
           at the top of this section is untouched. */
        text-align: center;
    }
    .google-rating-badge {
        gap: 0.8rem;
        padding: 0.7rem 1.05rem;
        border-radius: var(--radius-lg);
        border-top-width: 3px;
        border-left-width: 3px;
        max-width: none;
        /* Stretched across the full width by the column above, with its
           contents centred inside rather than stranded on the left edge.
           (A left-hugging and then a right-justified version were both
           tried the same day; the owner chose full width.) */
        justify-content: center;
    }
    .rating-info {
        min-width: 0;   /* long text (the no-JS fallback link) wraps inside */
    }
    .google-logo {
        width: 2rem;
        height: 2rem;
    }
    .overall-rating {
        font-size: 1.35rem;
    }
    .star-rating {
        font-size: 0.72rem;
    }
    .review-count {
        font-size: 0.62rem;
        font-weight: 600;
    }

    .reviews-grid {
        grid-template-columns: 1fr;
        gap: 0.75rem;
    }

    /* Reviews on phones (owner-approved, July 24 2026): show the newest
       THREE only — six stacked cards ran ~1,250px, well over a screen —
       and let the "Leave a Review" CTA card plus the Google link carry
       the rest. The cards are injected by script.js from Google, so
       hiding some costs nothing in search terms. The CTA card is always
       the last one and is deliberately excluded from the hide. */
    .reviews-grid .review-card:nth-of-type(n+4):not(.review-card-cta) {
        display: none;
    }

    /* ...and the surviving cards run a little tighter */
    .review-card {
        padding: 0.85rem 0.9rem;
    }
    .review-header {
        gap: 0.6rem;
        margin-bottom: 0.5rem;
    }
    .reviewer-avatar,
    .reviewer-avatar-placeholder {
        width: 36px;
        height: 36px;
    }
    .review-text {
        font-size: 0.85rem;
        line-height: 1.5;
    }

    .review-card-cta {
        grid-column: 1;
    }

    /* Phone map height. NOTE for anyone chasing the "Open in Maps" button
       sitting over the map's top-left corner, or the street labels clipped
       at its left edge (both raised July 28 2026): Google draws those
       INSIDE the embed's iframe. That's a different origin — no CSS here
       can move, restyle, or hide anything in there, and it never will. The
       only levers on our side are this box's size and the zoom/centre
       baked into the embed URL in index.html. Don't spend time on it. */
    .maps-embed-wrapper iframe {
        height: 300px;
    }

    /* SWIPE-ONLY on phones (owner request, July 24 2026): the arrow
       buttons come off and initCarouselSwipe() in script.js drives the
       gallery by finger instead. The dots still show position. */
    .ba-arrow {
        display: none;
    }
    /* Captions can run three lines in this narrow column — reserve the
       taller space here so the dots never shift as slides change. */
    .ba-caption {
        min-height: 4.5em;
    }

    /* Before/After pairs sit SIDE BY SIDE on phones too (owner request,
       July 23 2026 — they used to stack). The photos run small at half
       a phone's width, so every gallery photo is tappable to open
       full-screen (gallery lightbox in script.js) — that's the real
       viewing experience; the pair view is the overview. */
    .ba-pair {
        gap: 0.5rem;
    }
    .ba-pair .ba-photo {
        max-width: calc(50% - 0.25rem);
    }
    .ba-photo img {
        cursor: zoom-in;
    }

    /* Mobile footer: Quick Links and Contact stack as full-width rows
       (Quick Links on top, Contact beneath it), rather than side-by-side
       columns. The brand block (logo, name, license) still drops to the very
       bottom via order. Desktop layout is unaffected. */
    .footer-grid {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    /* Quick Links form two columns, filling DOWN each column first, so each
       column keeps the original top-to-bottom reading order.
       BALANCED (owner request, July 24 2026): this used to pin four rows
       per column, which split the homepage's six links an uneven 4 + 2.
       CSS multi-column evens the halves ITSELF and adapts to either page
       type — 3 + 3 on the homepage (6 links), 4 + 3 everywhere else (7,
       with the extra "Home" link) — with no row count to keep in sync.
       The width cap stops the two columns from drifting apart across the
       full footer width. */
    .footer-links ul {
        columns: 2;
        column-gap: var(--space-lg);
        max-width: 21rem;
    }
    .footer-links ul li {
        break-inside: avoid;   /* never split a link across the columns */
    }
    /* On pages that carry the extra "Home" link (everything except the
       homepage — 7 items), Home spans the full width on its own row and
       the remaining six balance 3 + 3 beneath it (owner request, July 24
       2026). The `:first-child:nth-last-child(7)` pair is a count test:
       it matches ONLY when the list holds exactly seven items, so the
       homepage's six-link list is untouched and still reads 3 + 3. */
    .footer-links ul li:first-child:nth-last-child(7) {
        column-span: all;
    }

    .footer-brand {
        grid-column: 1 / -1;
        order: 1;              /* pushed past Quick Links + Contact, to the bottom */
        margin-top: var(--space-xs);
    }

    .footer-bottom {
        flex-direction: column;
        gap: 0.4rem;
    }

    /* ---- FOOTER TAP TARGETS (July 28 2026) ----
       The footer LOOKED comfortably spaced and wasn't: the links carried
       generous gaps between them, but only the text itself was tappable —
       15px tall for a quick link, 22px for a contact line, 13px for the
       legal links, against the ~44px a fingertip actually needs. Every gap
       between them was dead space that swallowed taps. Measured before the
       fix: quick links 15px tall on a 29px pitch, contact 22px on 28px.

       So the anchors grow to 44px and CENTER their text, rather than the
       text growing. Type size, colour, and icons are all untouched — this
       is invisible except that the taps land. It does make the footer
       taller (~130px), which is the honest price of the gaps becoming
       part of the targets instead of dead zones between them; the links
       also end up more evenly spaced than the old 29px pitch.
       Phones only: a mouse hits a 15px link fine. */
    .footer-links ul a,
    .footer-contact a {
        display: flex;
        align-items: center;
        min-height: 44px;
    }
    /* The old margins spaced the TEXT; the 44px boxes do that job now, and
       leaving both would just add gaps back between the targets. */
    .footer-links ul li {
        margin-bottom: 0;
    }
    .footer-contact p {
        margin-bottom: 0;
    }
    .footer-social a {
        width: 44px;
        height: 44px;
    }
    /* The same 44px rule for the two links in the reviews block that a
       visitor can actually end up tapping: the "See our reviews on Google"
       line inside the rating badge (what shows before/instead of a live
       rating) and the "Read our reviews on Google" link the widget puts up
       when it fails. They were 11px and 17px tall — the smallest targets
       on the page, on the two elements that only ever appear when
       something has already gone wrong. */
    .review-count a,
    .reviews-loading a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
    /* Legal links sit side by side on one line — inline-flex keeps them
       there while still giving each a full-height box. */
    .footer-bottom a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    .city-nav {
        width: 88vw;
    }

}


/* =============================================
   MOBILE FORMATTING — PASS 1 (owner-approved, July 23 2026)
   Type scale + vertical rhythm for phones. Desktop is untouched —
   everything here lives behind the site's standing 620px breakpoint.
   Two knobs do most of the work:
     1. The root font drops 16px → 15px, which proportionally shrinks
        every rem-based size on the site (text, headings, paddings,
        buttons) by ~6% at once.
     2. The spacing variables compress further, tightening the rhythm
        between and inside sections without touching any one section.
   Before this pass the homepage stood ~5,700px tall on a phone (7+
   screens), with the About section alone at ~1,300px.
   ============================================= */
@media (max-width: 620px) {

    html {
        font-size: 15px;
    }

    :root {
        --space-xl: 2.75rem;
        --section-y: 2rem;
        --space-lg: 1.9rem;
        --space-md: 1.25rem;
    }

    /* Headings step down harder than the global ~6%. The About H1 is
       sized for the LONGER keyword headline (July 24 2026) — at the old
       size that line ran four deep and ate the whole first screen. */
    .about-headline {
        font-size: clamp(1.5rem, 6.4vw, 2rem);
    }
    .section-title {
        font-size: clamp(1.35rem, 5.5vw, 1.7rem);
    }
    .section-subtitle {
        font-size: 0.98rem;
    }

    /* Body copy: one step smaller and slightly tighter. The wide
       typewriter face stays comfortably readable at this size, and
       paragraphs stop towering. (Stepped down once more per owner
       calibration, July 23 2026.) The intro selector deliberately
       matches the specificity of desktop's `.about-text p:first-child`
       sizing rule — a bare `.about-intro` loses to it and silently
       does nothing. */
    .about-text p.about-intro {
        font-size: 0.95rem;
    }
    .about-text p {
        font-size: 0.9rem;
        line-height: 1.65;
    }

    /* iPhone anti-zoom: any form control under 16px makes iOS zoom
       the whole page into a focused field. Pin the booking form's
       controls at 16px regardless of the root-font shrink above. */
    .booking-form input,
    .booking-form select,
    .booking-form textarea {
        font-size: 16px;
    }

    /* Team photo: FULL-WIDTH HERO on phones (owner redesign, July 24
       2026 — replaced a small floated magazine cascade with tap-to-
       enlarge). The grid becomes normal flow; the photo leads the
       section at full width, the copy follows beneath it. */
    .about-grid {
        display: block;
    }
    /* Hero photo, INSET with rounded corners (owner call, July 24 2026 —
       a full-bleed edge-to-edge version was tried and reverted; the
       white space and rounding are wanted, just a little tighter). The
       modest negative side margins widen it ~2rem past the container's
       padding while keeping a margin at each screen edge. NOT
       tappable-to-enlarge (owner: keep off). */
    .about-photo {
        float: none;
        width: calc(100% + 2rem);
        margin: 0 -1rem var(--space-md);
    }
    .about-photo figcaption {
        font-size: 0.8rem;
    }

    /* About copy collapses behind a Show more toggle (owner redesign,
       July 24 2026). Deliberately NOT a card — no box, no accent bars;
       the fade blends into the section's own background so the text
       just softens out. Same reveal-on-init failsafe as the story
       cards: script.js adds .is-collapsed, never the HTML. */
    .about-collapse {
        position: relative;
    }
    .about-body {
        overflow: hidden;
        max-height: 120rem;          /* ≈ no limit; keeps the transition animatable */
        transition: max-height 0.5s ease;
    }
    .about-text.is-collapsed .about-body {
        /* Shorter cut + taller fade below: the text dissolves well
           before the clip line, so it reads as "there's more" rather
           than a sentence chopped mid-word (owner request, July 24). */
        max-height: 7.5rem;
    }
    .about-fade {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 4.5rem;
        background: linear-gradient(to bottom, rgba(58, 58, 58, 0), var(--bg-dark));
        opacity: 0;
        pointer-events: none;
        transition: var(--ease);
    }
    .about-text.is-collapsed .about-fade {
        opacity: 1;
    }
    .about-toggle {
        display: flex;
        align-items: center;
        gap: 0.4rem;
        /* Sits at the RIGHT edge of the column (owner request, July 24
           2026): fit-content stops the flex box from filling the width,
           and the auto left margin then pushes it over. */
        width: fit-content;
        margin: 0.5rem 0 0 auto;
        padding: 0;
        background: none;
        border: none;
        cursor: pointer;
        font-family: var(--font-heading);
        font-weight: 700;
        font-size: 0.9rem;
        color: var(--orange);
        transition: var(--ease);
    }
    .about-toggle .fa-chevron-down {
        transition: var(--ease);
    }
    .about-text:not(.is-collapsed) .about-toggle .fa-chevron-down {
        transform: rotate(180deg);
    }
    /* Breathing room between the Show more CONTROL and the gallery LINK
       below it — both are orange and bold, so stacked tight they read
       as one confusing pair (owner request, July 24 2026).
       TRIAL (July 24 2026): the gallery link wears a tight dashed
       outline — the same treatment as the drawer's "View All Areas"
       button (.city-nav-all-link) — so it reads as a button, not
       another orange text link. Orange here instead of white to keep
       it in the About section's palette. */
    .about-gallery-link {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        white-space: nowrap;     /* one line, always — no wrapped button */
        margin-top: var(--space-md);
        padding: 0.12rem 0.38rem;   /* very snug — frame hugs the text */
        border: 1.5px dashed rgba(235, 149, 74, 0.55);
        border-radius: var(--radius);
    }

    /* Phone header is shorter than desktop's — anchor jumps clear it
       with less margin (base rule near the top of this file). */
    [id] {
        scroll-margin-top: 78px;
    }

    /* Section titles sit closer to their subtitles (owner request,
       July 24 2026). The About headline is deliberately EXCLUDED —
       it keeps its generous gap above the intro paragraph. */
    .section-title,
    .reviews-title {
        /* Headings were inheriting BODY line spacing (1.65) — far too
           airy once a title wraps to 2–3 lines on a phone. 1.15 matches
           the About headline's own tight leading (owner request,
           July 24 2026). This is the space WITHIN a heading; the margin
           below is the gap to its subtitle. */
        line-height: 1.15;
    }
    /* Gap from a title down to its subtitle (reverted July 24 2026 to
       this value — the tighter 0.1rem trial was undone). */
    .section-title {
        margin-bottom: 0.35rem;
    }
    .reviews-header {
        row-gap: 0.35rem;
    }
    /* (The gallery title carried a larger bottom margin while it had no
       subtitle — that margin was the gap down to the photos. It gained a
       subtitle on July 24 2026, so it now uses the standard title→
       subtitle gap above, and the subtitle's own bottom margin provides
       the space before the carousel.) */

    /* Booking form: a smidge tighter (owner request, July 23 2026 —
       "seriously, just a tiny bit"): slimmer field padding and row
       gaps so more of the form fits one screen at a time. */
    .booking-form input,
    .booking-form select,
    .booking-form textarea {
        padding: 0.55rem 0.8rem;
    }
    .form-row {
        margin-bottom: 0.65rem;
    }
    .form-group {
        gap: 0.2rem;
    }
    .form-section-label {
        margin: 1rem 0 0.5rem;
    }
    /* Tighter seam under the form card — the section's bottom padding
       stacked on the next section's top padding read as dead air. */
    .booking-section {
        padding-bottom: 1.5rem;
    }

    /* Get In Touch card, compact (owner-approved, July 23 2026):
       smaller orange buttons, tighter card padding, and all five
       social circles on ONE row — they wrapped 4+1 on 375px-wide
       phones (iPhone SE/mini class; wider phones already fit).
       44px circles stay at the tap-target floor. */
    .info-card {
        padding: var(--space-md) 1rem;
    }
    .btn-call--large {
        font-size: 1rem;
        padding: 0.75rem 1rem;
        margin: 0.5rem 0 0.9rem;
        gap: 0.7rem;
    }
    .btn-call--large strong {
        font-size: 1.05rem;
    }
    .btn-call--large small {
        font-size: 0.78rem;
    }
    .social-icons {
        gap: 0.4rem;
    }
    .social-icon {
        width: 44px;
        height: 44px;
        font-size: 0.95rem;
    }

}

/* =============================================
   PHOTO LIGHTBOX (mobile pass 1, July 23 2026)
   Full-screen viewer for the gallery photos (pairs + one-offs; the team
   photo dropped out July 24 2026). Built and torn down by
   openPhotoLightbox in script.js; tapping anywhere (or Escape) closes.
   Not phone-gated in CSS — script.js only enables the tap on phone
   widths, where the gallery photos run small.
   ============================================= */
.photo-lightbox {
    position: fixed;
    inset: 0;
    z-index: 4000;
    background: rgba(0, 0, 0, 0.88);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.2rem;
    cursor: zoom-out;
}
.photo-lightbox img {
    max-width: 100%;
    max-height: 85vh;
    border-radius: var(--radius-lg);
}


/* =============================================
   AUTOMATIC HIGH-CONTRAST MODE (owner request, 12 August 2026)

   This is NOT a button on the page. Phones and computers have an
   "increase contrast" accessibility setting (iPhone: Settings → Accessibility
   → Display & Text Size → Increase Contrast; Mac and Windows have the same).
   A browser reports that as `prefers-contrast: more`, exactly the way it
   reports light/dark preference. When a visitor has asked their device for
   more contrast, everything below takes effect the instant they arrive, with
   nothing to find or click. Everyone else sees the normal design, untouched.

   It only PUSHES FURTHER what the default already does — it never invents a
   second look. So it can't drift out of step with the design: it just takes
   the same elements to the strongest, most legible version.
   ============================================= */
@media (prefers-contrast: more) {
    :root {
        /* Buttons: black on orange = 8.93:1 (default is already 5.94). */
        --text-on-orange: #000000;
    }
    /* The "reviews on Google" link on the navy badge → white (9.21:1). */
    .review-count a {
        color: var(--white);
        text-decoration: underline;
    }
    /* Orange links on the dark page → white, so they clear the bar for
       small text too; underline keeps them distinct from body text. */
    .city-nav-home-link,
    .story-blog-link {
        color: var(--white);
        text-decoration: underline;
    }
    /* A heavier keyboard focus ring, with a dark outline behind the orange
       so it stays visible on the white booking-form card as well as the
       dark page (the one spot the standard ring falls short). */
    a:focus-visible,
    button:focus-visible,
    summary:focus-visible,
    input:focus-visible,
    select:focus-visible,
    textarea:focus-visible,
    [tabindex]:focus-visible {
        outline: 3px solid var(--orange);
        outline-offset: 2px;
        box-shadow: 0 0 0 5px #000;
    }
}
