/* ==========================================================================
   Mobile corrections.

   Loaded site-wide after responsive.css. Kept separate from the approved
   stylesheets so every override here is visible in one place and reversible.

   THE BUG THIS FIXES

   The header row is: brand · nav · .header-cta. `.header-cta` is `flex:none`
   and `.brand` never shrinks, so below roughly 600px the row is wider than the
   viewport. Two things follow:

     1. The burger is pushed off-screen — measured at x 473–519 in a 390px
        viewport — so phone visitors cannot open the navigation at all.
     2. The document scrolls horizontally, which also clips the fixed bottom
        action bar, cutting "Free Quote" in half.

   Both reproduce identically in the original static HTML, so this is a defect
   in the approved design rather than something the WordPress conversion
   introduced. Verify with: node _src/build/probe.mjs <url>
   ========================================================================== */

/* Nothing may push the document wider than the screen. */
html,
body {
  max-width: 100%;
  /*
   * clip rather than hidden.
   *
   * overflow-x:hidden makes the element a scroll container, and a scroll
   * container disables position:sticky for everything inside it. That is
   * what stopped the site header sticking — it was declared correctly and
   * simply never engaged. overflow-x:clip prevents the sideways scroll
   * without creating the container.
   */
  overflow-x: clip;
}

@media (max-width: 1023px) {
  /* Let the brand block shrink instead of forcing the row wider. */
  .brand {
    min-width: 0;
    flex: 0 1 auto;
  }
  .brand__text {
    min-width: 0;
    overflow: hidden;
  }
  .brand__name,
  .brand__tag {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /*
   * The header CTA duplicates the fixed bottom bar, which already carries
   * "Free Quote" within thumb reach. Dropping it here is what gives the burger
   * room to exist — navigation matters more than a third copy of the same CTA.
   */
  .header-cta .btn--header {
    display: none;
  }
  .header-cta {
    flex: none;
    gap: 0;
  }

  .site-header__inner {
    gap: var(--s-4);
    min-height: 68px;
  }

  /* WCAG 2.1 AA: 44×44 minimum for a touch target. */
  .nav-burger {
    width: 46px;
    height: 46px;
    flex: none;
  }
}

@media (max-width: 640px) {
  /* The logo is 101px wide by default, which is a lot of a 390px row. */
  .brand__mark {
    width: 76px;
    height: 58px;
  }
  .brand__name {
    font-size: 1rem;
  }
  .brand__tag {
    font-size: 0.75rem;
  }
}

/* --------------------------------------------------------------------------
   Fixed bottom action bar — the primary conversion path on a phone.
   -------------------------------------------------------------------------- */
@media (max-width: 1023px) {
  .mobile-bar {
    max-width: 100vw;
  }
  .mobile-bar__btn {
    /* Comfortable one-handed tap, clear of the home indicator. */
    min-height: 56px;
    padding-inline: var(--s-3);
    font-size: var(--fs-sm);
  }

  /*
   * Reserve the bar's height so the last section is never trapped underneath
   * it — a fixed bar over a form's submit button is a silent conversion leak.
   */
  body {
    padding-bottom: calc(56px + env(safe-area-inset-bottom));
  }
}

@media (min-width: 1024px) {
  body {
    padding-bottom: 0;
  }
}

/* ==========================================================================
   Scroll depth.

   Measured before this block (390px viewport, node _src/build/mobile-audit.mjs):
   service pages ran to 26.2 screens, the homepage 23.9, average 15.4. On a
   phone that is the difference between reading the page and abandoning it.

   The section rhythm was tuned for desktop — clamp(4rem, 8.5vw, 7rem) resolves
   to 4rem top AND bottom on a 390px screen, so every section boundary costs
   128px of empty space. Tightening the rhythm and the grid gaps recovers that
   without touching a single word of content, a heading, or a link.
   ========================================================================== */
@media (max-width: 767px) {
  :root {
    --sec-y: 2.75rem;
  }

  /* Grids stack on mobile, so the gap is pure vertical cost. */
  .signs__grid,
  .svc-grid,
  .why-grid,
  .proc-grid,
  .loc-grid,
  .ba-grid,
  .bloglist__grid,
  .abt-valgrid,
  .abt-areagrid,
  .abt-revgrid,
  .pg-whygrid,
  .pg-galgrid,
  .pg-revgrid {
    gap: var(--s-4);
  }

  .abt-row + .abt-row,
  .abt-rail,
  .pg-galblock + .pg-galblock {
    margin-top: var(--s-7);
  }

  .section__head {
    margin-bottom: var(--s-5);
  }

  /* Cards carry desktop padding on every side; trim the vertical only. */
  .sign__body,
  .bcard__body,
  .abt-val,
  .pg-whycard {
    padding-block: var(--s-5);
  }
}

/* ==========================================================================
   Legibility.

   Nothing readable should sit below 13px on a phone. The eyebrow, tag and
   meta styles were 11–13px, which is fine at desktop reading distance and
   not at arm's length on a handset.
   ========================================================================== */
@media (max-width: 767px) {
  .eyebrow,
  .bacard__loc,
  .bacard__tag,
  .blogfil__n,
  .pg-filter__n,
  .artcta__eyebrow,
  .artnav__dir,
  .abt-val::before {
    font-size: 0.8125rem;
  }

  .field__hint,
  .quote-form__note,
  .ecard__meta,
  .bcard__date,
  .abt-rev figcaption,
  .pg-rev figcaption {
    font-size: 0.875rem;
  }
}

/* ==========================================================================
   Tap targets.

   WCAG 2.1 SC 2.5.8 (AA) requires 24×24 minimum; inline links inside a
   sentence are explicitly exempt. These are the standalone controls that were
   below that, or below the 44px comfort threshold — the ones a thumb has to
   find deliberately.
   ========================================================================== */
@media (max-width: 1023px) {
  .topbar__call,
  .blogfil__btn,
  .pg-filter__chip,
  .empty__links a,
  .pg-areas a,
  .editorial__tags a,
  .artshare__list a,
  .svcnav__list a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .pg-galblock__link,
  .bcard__more a,
  .ecard__more,
  .stat__link,
  .abt-area a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* The gallery zoom affordance is a real button, not decoration. */
  .pg-galbtn__zoom {
    width: 44px;
    height: 44px;
  }

  /* Give adjacent inline chips room so neighbours are not mis-tapped. */
  .blogfil__list,
  .pg-filter,
  .pg-areas ul,
  .empty__links {
    gap: var(--s-3);
  }
}

/* ==========================================================================
   Card rails.

   THE PROBLEM (measured, 390×844)

   Service pages ran to 27 screens of scroll and the homepage to 28. The cause
   is not padding — it is that every card grid stacks. Six "signs" cards, each
   with a 4:3 image and a paragraph, is six full screens for one section. A
   phone user has to scroll past roughly 24 cards to reach the quote form.

   THE FIX

   Below 640px the stacked grids become horizontal scroll-snap rails: one card
   visible at a time, swipe for the rest. A six-card section costs one screen
   instead of six.

   Why this is SEO-safe: nothing is hidden, removed or reordered. Every card
   stays in the DOM in source order, fully rendered and crawlable — this is a
   layout change only. No display:none, no JavaScript, no clones.
   ========================================================================== */
@media (max-width: 640px) {
  .signs__grid,
  .svc-grid,
  .why-grid,
  .proc-grid,
  .loc-grid,
  .bloglist__grid,
  .abt-valgrid,
  .abt-areagrid,
  .abt-revgrid,
  .pg-whygrid,
  .pg-galgrid,
  .pg-revgrid,
  .svccards {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 84%;
    grid-template-columns: none;
    gap: var(--s-4);

    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;

    /* Bleed to the screen edge so the next card peeks — the affordance that
       tells a thumb there is more without needing arrows or dots. */
    padding-inline: var(--s-4);
    margin-inline: calc(-1 * var(--s-4));
    padding-bottom: var(--s-2);

    scrollbar-width: none;
  }

  .signs__grid::-webkit-scrollbar,
  .svc-grid::-webkit-scrollbar,
  .why-grid::-webkit-scrollbar,
  .proc-grid::-webkit-scrollbar,
  .loc-grid::-webkit-scrollbar,
  .bloglist__grid::-webkit-scrollbar,
  .abt-valgrid::-webkit-scrollbar,
  .abt-areagrid::-webkit-scrollbar,
  .abt-revgrid::-webkit-scrollbar,
  .pg-whygrid::-webkit-scrollbar,
  .pg-galgrid::-webkit-scrollbar,
  .pg-revgrid::-webkit-scrollbar,
  .svccards::-webkit-scrollbar {
    display: none;
  }

  .signs__grid > *,
  .svc-grid > *,
  .why-grid > *,
  .proc-grid > *,
  .loc-grid > *,
  .bloglist__grid > *,
  .abt-valgrid > *,
  .abt-areagrid > *,
  .abt-revgrid > *,
  .pg-whygrid > *,
  .pg-revgrid > *,
  .svccards > * {
    scroll-snap-align: start;
    min-width: 0;
    height: auto;
  }

  /* The work gallery reads better two-up — they are photographs, not cards. */
  .pg-galgrid {
    grid-auto-columns: 62%;
  }

  /* Card media: 4:3 is a lot of height on a 390px screen. */
  .sign__media,
  .loc__media,
  .bcard__media,
  .pg-galbtn {
    aspect-ratio: 16 / 10;
  }
}

/* ==========================================================================
   Above the fold.

   The hero pushed its first CTA below the fold on every page. Tightening the
   hero band and the H1 scale gets the headline, one proof line and the quote
   button onto the first screen — which is the only screen most visitors see.
   ========================================================================== */
@media (max-width: 640px) {
  .hero,
  .hero--svc {
    padding-block: var(--s-7) var(--s-8);
  }

  .hero__stats {
    margin-top: var(--s-5);
  }

  /* Trim the display scale a step; it is set for a 1440px canvas. */
  h1,
  .hero h1,
  .hero--svc h1 {
    font-size: clamp(1.875rem, 8.4vw, 2.5rem);
  }

  .hero--svc__points li:nth-child(n + 3) {
    display: none;
  }

  .hero--svc__sub,
  .hero__sub {
    font-size: 1rem;
  }
}

/* Remaining stacked lists on the service pages — the two tallest sections
   measured 2.2 and 1.7 screens on their own. Same rail treatment, same
   guarantee: nothing hidden, nothing reordered. */
@media (max-width: 640px) {
  .cost__grid,
  .consent__list,
  .steps__grid,
  .whyus__grid,
  .gal__track,
  .abt-rail {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 84%;
    grid-template-columns: none;
    gap: var(--s-4);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    padding-inline: var(--s-4);
    margin-inline: calc(-1 * var(--s-4));
    padding-bottom: var(--s-2);
    scrollbar-width: none;
  }

  .cost__grid::-webkit-scrollbar,
  .consent__list::-webkit-scrollbar,
  .steps__grid::-webkit-scrollbar,
  .whyus__grid::-webkit-scrollbar,
  .abt-rail::-webkit-scrollbar {
    display: none;
  }

  .cost__grid > *,
  .consent__list > *,
  .steps__grid > *,
  .whyus__grid > *,
  .abt-rail > * {
    scroll-snap-align: start;
    min-width: 0;
  }

  /* The numbered rail's connector line only reads across a row. */
  .abt-step::after {
    display: none;
  }
}

/* ==========================================================================
   Swipe indicator.

   The rails peek the next card, which is the strongest affordance, but on a
   short rail that peek can be missed. A dot row directly under the rail says
   how many items there are and where you are, and a one-line prompt says what
   to do — the prompt fades out on first scroll, since by then it is noise.

   Injected by assets/js/main.js; nothing here renders without it.
   ========================================================================== */
.rail-hint {
  display: none;
}

@media (max-width: 640px) {
  .rail-hint {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.375rem;
    margin-top: var(--s-3);
  }

  .rail-hint__dots {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .rail-hint__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.22;
    transition: opacity 0.2s ease, transform 0.2s ease;
  }

  .rail-hint__dot.is-active {
    opacity: 0.75;
    transform: scale(1.4);
  }

  .rail-hint__label {
    font-size: 0.8125rem;
    letter-spacing: 0.01em;
    opacity: 0.6;
    transition: opacity 0.25s ease;
  }

  /* The arrow reinforces the direction without needing an icon file. */
  .rail-hint__label::after {
    content: ' \203A\203A';
    letter-spacing: -0.12em;
  }

  /* Once they have scrolled, the instruction has served its purpose. */
  .rail-hint.is-moved .rail-hint__label {
    opacity: 0;
  }

  /* A focused rail must show where focus is. */
  .signs__grid:focus-visible,
  .svc-grid:focus-visible,
  .why-grid:focus-visible,
  .proc-grid:focus-visible,
  .loc-grid:focus-visible,
  .bloglist__grid:focus-visible,
  .abt-valgrid:focus-visible,
  .abt-areagrid:focus-visible,
  .abt-revgrid:focus-visible,
  .pg-whygrid:focus-visible,
  .pg-galgrid:focus-visible,
  .pg-revgrid:focus-visible,
  .svccards:focus-visible,
  .cost__grid:focus-visible,
  .consent__list:focus-visible,
  .steps__grid:focus-visible,
  .whyus__grid:focus-visible,
  .abt-rail:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 4px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .rail-hint__dot,
  .rail-hint__label {
    transition: none;
  }
}

/* ==========================================================================
   Mega-menu columns as accordions.

   The drawer listed every service group expanded — around two screens of
   links before the next top-level item. Collapsed, the whole menu fits on one
   screen and stays scannable.

   Desktop is deliberately excluded: there the mega panel is a multi-column
   grid where everything visible at once is the point.
   ========================================================================== */
@media (max-width: 1023px) {
  .mega__col.is-collapsible .mega__head {
    padding: 0;
  }

  .mega__toggle {
    /* Reset the button chrome; the heading styling lives on the children. */
    display: flex;
    align-items: center;
    gap: var(--s-3);
    width: 100%;
    min-height: 48px;
    padding: var(--s-3) 0;
    background: none;
    border: 0;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
  }

  /* Chevron, drawn in CSS so there is no extra request or icon markup. */
  .mega__toggle::after {
    content: '';
    width: 9px;
    height: 9px;
    margin-left: auto;
    flex: none;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform 0.22s ease;
    opacity: 0.7;
  }

  .mega__col.is-open .mega__toggle::after {
    transform: rotate(-135deg) translate(-2px, -2px);
  }

  .mega__col.is-collapsible .mega__links,
  .mega__col.is-collapsible .mega__cta {
    display: none;
  }

  .mega__col.is-open .mega__links {
    display: block;
  }

  .mega__col.is-open .mega__cta {
    display: inline-flex;
  }

  /* The description is the column's own subtitle — redundant once collapsed. */
  .mega__col.is-collapsible:not(.is-open) .mega__desc {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mega__toggle::after {
    transition: none;
  }
}

/* ==========================================================================
   Mobile drawer: close button.

   Measured: the drawer sits at z-index 120 with the scrim at 110, so while it
   is open the burger is covered and there is no visible way to dismiss it.
   ========================================================================== */
.nav__close {
  display: none;
}

@media (max-width: 1023px) {
  .nav__close {
    display: grid;
    place-items: center;
    position: sticky;
    top: 0;
    /* Sits at the drawer's top-right, above the scrolling link list. */
    margin-left: auto;
    z-index: 2;
    width: 44px;
    height: 44px;
    padding: 0;
    background: var(--surface, #fff);
    border: 0;
    border-radius: 50%;
    color: inherit;
    cursor: pointer;
  }

  .nav__close svg {
    width: 22px;
    height: 22px;
  }

  .nav__close:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
  }
}

/* ==========================================================================
   Top bar fits one line.

   "24/7 Emergency | Call Now: 021 102 0360" wrapped to two lines on a 390px
   screen, which cost a whole row of height on every page. The phone number is
   the part that matters, so the label shrinks and the separator goes.
   ========================================================================== */
@media (max-width: 640px) {
  .topbar__inner {
    flex-wrap: nowrap;
    gap: var(--s-2);
  }

  .topbar__msg,
  .topbar__call {
    white-space: nowrap;
    font-size: 0.8125rem;
  }

  .topbar__sep {
    display: none;
  }

  /* "Call Now:" is redundant next to a tappable number. */
  .topbar__call-label {
    display: none;
  }

  .topbar__call {
    padding-inline: var(--s-3);
  }

  /*
   * The row is 44px because the call button keeps a WCAG-sized tap target.
   * The container padding on top of that is what made the bar 61px, so it
   * goes — the button's own height sets the rhythm.
   */
  .topbar__inner {
    padding-block: 0;
    min-height: 0;
  }
}

/* ==========================================================================
   Rail gutter.

   The rails bled to the screen edge by cancelling the section gutter, which
   left the first card flush against the left edge with no margin. Aligning
   them with the section content instead reads as deliberate, and the next
   card still peeks because the rail keeps overflowing to the right.
   ========================================================================== */
@media (max-width: 640px) {
  .signs__grid,
  .svc-grid,
  .why-grid,
  .proc-grid,
  .loc-grid,
  .bloglist__grid,
  .abt-valgrid,
  .abt-areagrid,
  .abt-revgrid,
  .pg-whygrid,
  .pg-galgrid,
  .pg-revgrid,
  .svccards,
  .cost__grid,
  .consent__list,
  .steps__grid,
  .whyus__grid,
  .abt-rail {
    padding-inline: 0;
    margin-inline: 0;
    /* Snap targets clear the edge rather than hiding under it. */
    scroll-padding-inline-start: 0;
  }
}

/* ==========================================================================
   Footer columns as accordions.
   ========================================================================== */
@media (max-width: 767px) {
  .ftc.is-collapsible .ftc__h {
    margin: 0;
  }

  .ftc__toggle {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    width: 100%;
    min-height: 48px;
    padding: var(--s-2) 0;
    background: none;
    border: 0;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
  }

  .ftc__toggle::after {
    content: '';
    width: 8px;
    height: 8px;
    margin-left: auto;
    flex: none;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform 0.22s ease;
    opacity: 0.65;
  }

  .ftc.is-open .ftc__toggle::after {
    transform: rotate(-135deg) translate(-2px, -2px);
  }

  .ftc__body {
    display: none;
  }

  .ftc.is-open .ftc__body {
    display: block;
    padding-bottom: var(--s-3);
  }

  .ftc.is-collapsible + .ftc.is-collapsible {
    border-top: 1px solid rgba(255, 255, 255, 0.12);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ftc__toggle::after {
    transition: none;
  }
}

/* ==========================================================================
   Drawer stacking.

   THE BUG

   .site-header is position:sticky with z-index:100, which makes it a stacking
   context. The drawer inside it declares z-index:120, but that value only
   orders it against its siblings *within* the header — the header as a whole
   still paints at root level 100.

   .nav-scrim is a direct child of <body> at z-index:110, so it lands in the
   root stacking context ABOVE the entire header, drawer included. Its
   rgba(4,39,14,.5) green and its 2px blur were being painted over the open
   menu, which is the green wash over the navigation.

   Raising the header above the scrim only while the drawer is open keeps the
   scrim over the page, where it belongs, and under the menu.
   ========================================================================== */
body.nav-locked .site-header {
  z-index: 120;
}

/* The top bar sits above the header in source order; without this it stays
   under the scrim and reads as a detached green band above the drawer. */
body.nav-locked .topbar {
  position: relative;
  z-index: 120;
}

/* ==========================================================================
   Footer accordion spacing.

   Measured: .ft__nav is a grid with row-gap:32px. Collapsed, each column is
   only 48px tall, so the pitch was 80px — more gap than content, which is the
   dead space between the headings.

   Collapsed rows need no gap at all; the divider already separates them. The
   gap comes back the moment a row is open.
   ========================================================================== */
@media (max-width: 767px) {
  .ft__nav {
    row-gap: 0;
  }

  .ftc.is-collapsible {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
  }

  /* The first column already has the section edge above it. */
  .ftc.is-collapsible:first-child {
    border-top: 0;
  }

  .ftc.is-open {
    padding-bottom: var(--s-3);
  }
}

/* ==========================================================================
   Mobile drawer: strip the desktop chrome.

   The mega panel is designed for a wide three-column grid, where an icon, a
   description and a pill CTA per column all help you scan. Stacked into a
   390px drawer they stop helping: one open column ran past the fold before
   reaching the next heading.

   What goes on mobile — icons, descriptions, the connector rule, the link
   underlines, and the CTA's pill. What stays — every link, in source order,
   in the DOM. This is styling only; nothing is removed from the document.
   ========================================================================== */
@media (max-width: 1023px) {
  /* Decorative: the heading already says what the group is. */
  .mega__icon,
  .mega__col.is-collapsible .mega__desc {
    display: none;
  }

  .mega__title {
    font-size: 1rem;
    font-weight: 600;
  }

  /* The rule and the indent were guides for a wide multi-column layout. */
  .mega__links {
    border-left: 0;
    padding-left: 0;
    margin-left: 0;
  }

  .mega__links li + li {
    margin-top: 0;
  }

  .mega__links a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0;
    font-size: 0.9375rem;
    text-decoration: none;
    border-bottom: 0;
  }

  /* A pill per column is three competing buttons in one drawer. */
  .mega__cta {
    min-height: 44px;
    padding: 0;
    background: none;
    border: 0;
    font-size: 0.9375rem;
    font-weight: 600;
  }

  .mega__toggle {
    min-height: 52px;
  }

  /* Group boundaries carry the structure now that the icons are gone. */
  .mega__col.is-collapsible + .mega__col.is-collapsible {
    border-top: 1px solid var(--line, rgba(0, 0, 0, 0.08));
  }

  .mega__col.is-open .mega__links {
    padding-bottom: var(--s-2);
  }
}

/* Two leftovers from the desktop mega panel, measured on the open drawer. */
@media (max-width: 1023px) {
  /*
   * The dotted underline is a decorative span::after, not text-decoration,
   * which is why setting text-decoration:none on the anchor did nothing.
   * At desktop it marks a hover affordance; on a touch screen there is no
   * hover, and four dotted rules in a column just read as noise.
   */
  .mega__links a > span::after {
    display: none;
  }

  /* .mega__col carries var(--s-3) top and bottom, which is the dead space
     under each collapsed heading. The toggle sets its own 52px height. */
  .mega__col.is-collapsible {
    padding-block: 0;
    padding-left: 0;
  }
}

@media (max-width: 1023px) {
  /*
   * The dashed rule is text-decoration on the inner span, not the ::after I
   * hid above (that one is the hover fill bar). Both are hover affordances
   * for a pointer; neither earns its keep on a touch screen.
   */
  .mega__links a > span {
    text-decoration: none;
  }
}

/* ==========================================================================
   Mobile navigation — premium pass.

   The markup is untouched: the drawer, the per-category toggles and the CTA
   all already exist. What was missing was hierarchy — every level shared
   roughly the same weight, so the eye had nothing to anchor to and the drawer
   read as a flat list of links rather than a structure.

   Three levels, three distinct treatments:

     top level   (Services, Service Areas)  20px / 700, brand green when open
     category    (Tree Services ...)        17px / 600 with its icon
     child link  (Tree Removal Auckland)    16px / 400, indented, bulleted

   Everything sits on a 24px horizontal rhythm. Animations run 260ms, inside
   the 250-300ms band asked for, and touch only transform, opacity and
   grid-template-rows so scrolling stays on the compositor.
   ========================================================================== */
@media (max-width: 1023px) {

  /* ---- drawer shell ---------------------------------------------------- */

  .nav {
    --nav-pad: 24px;
    padding: 0;

    /*
     * Scroll containment.
     *
     * The drawer scrolled, but reaching either end handed the gesture to the
     * page behind it — so a flick near the top or bottom moved the page
     * instead of the menu, which reads as scrolling being broken. contain
     * keeps the gesture inside the drawer.
     *
     * -webkit-overflow-scrolling restores momentum scrolling on older iOS,
     * where its absence makes a long menu feel stuck.
     */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }

  /*
   * Locking <body> alone does not hold on iOS Safari — the page behind still
   * scrolls under the drawer. The html element has to be locked too.
   */
  html:has(body.nav-locked) {
    overflow: hidden;
  }

  body.nav-locked {
    overflow: hidden;
    touch-action: none;
  }

  /* The drawer itself must stay scrollable inside that lock. */
  body.nav-locked .nav {
    touch-action: pan-y;
  }

  .nav__list {
    /* No pinned CTA any more, so only enough tail room to scroll the last
       row clear of the screen edge. */
    padding: 0 0 32px;
    margin: 0;
    list-style: none;
  }

  .nav__item {
    border: 0;
  }

  /* Hairlines rather than full-strength rules: same grouping, less clutter. */
  .nav__item + .nav__item {
    border-top: 1px solid rgba(4, 39, 14, 0.07);
  }

  /* ---- level 1: Services / Service Areas ------------------------------- */

  .nav__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    width: 100%;
    padding: 18px var(--nav-pad);
    min-height: 60px;
    font-family: var(--font-head);
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.25;
    letter-spacing: -0.01em;
    color: var(--ink-1);
    text-align: left;
    background: none;
    border: 0;
  }

  .nav__link--toggle[aria-expanded="true"] {
    color: var(--brand-deep);
  }

  .nav__link svg,
  .nav__link .ico {
    flex: none;
    transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1);
  }

  .nav__link--toggle[aria-expanded="true"] svg,
  .nav__link--toggle[aria-expanded="true"] .ico {
    transform: rotate(180deg);
  }

  /* ---- level 2: category rows ------------------------------------------ */

  /*
   * display:block on the grid.
   *
   * .mega__grid keeps its desktop column template on mobile, so each column
   * was sized to its own content — the tint behind an open category stopped
   * partway across the drawer and every category ended at a different x.
   * Stacking them as blocks makes each row full-bleed.
   */
  .mega,
  .mega__inner,
  .mega__grid {
    display: block;
    width: 100%;
    max-width: none;
    padding: 0;
    margin: 0;
    border: 0;
    background: none;
    box-shadow: none;
  }

  .mega__col {
    display: block;
    width: 100%;
    border: 0;
    padding: 0;
  }

  .mega__head {
    display: block;
    width: 100%;
  }

  .mega__col + .mega__col {
    border-top: 1px solid rgba(4, 39, 14, 0.07);
  }

  .mega__toggle {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    width: 100%;
    padding: 16px var(--nav-pad);
    min-height: 56px;
    font-family: var(--font-head);
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--ink-1);
    text-align: left;
    background: none;
    border: 0;
    cursor: pointer;
    transition: background-color 200ms ease;
  }

  /* The open category is tinted so its children read as one grouped block. */
  .mega__col.is-open .mega__toggle,
  .mega__col.is-open .mega__links {
    background: var(--brand-wash);
  }

  .mega__col.is-open .mega__toggle {
    color: var(--brand-deep);
  }

  /* Chevron drawn in CSS — this row carries no icon of its own. */
  .mega__toggle::after {
    content: "";
    width: 11px;
    height: 11px;
    margin-left: auto;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transform-origin: 60% 60%;
    opacity: 0.55;
    transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1), opacity 200ms ease;
  }

  .mega__col.is-open .mega__toggle::after {
    transform: rotate(225deg);
    opacity: 1;
  }

  .mega__icon {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    color: var(--brand-deep);
  }

  .mega__icon svg {
    width: 22px;
    height: 22px;
  }

  .mega__title {
    font: inherit;
    color: inherit;
    text-decoration: none;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* At 320px the longest labels pushed the drawer 12px wider than itself. */
  .nav,
  .nav__list,
  .mega__links a,
  .mega__cta {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* The column blurb is a desktop affordance; on a phone it is noise. */
  .mega__desc {
    display: none;
  }

  /* ---- level 3: child links -------------------------------------------- */

  /*
   * grid-template-rows 0fr -> 1fr animates to the content's own height with
   * no JavaScript measurement and no max-height guess, which either clips a
   * long list or adds dead easing to a short one.
   */
  .mega__links {
    display: grid;
    grid-template-rows: 0fr;
    margin: 0;
    padding: 0;
    list-style: none;
    overflow: hidden;
    transition: grid-template-rows 260ms cubic-bezier(0.4, 0, 0.2, 1);
  }

  .mega__col.is-open .mega__links {
    grid-template-rows: 1fr;
  }

  .mega__links > * {
    min-height: 0; /* required, or the 0fr row never collapses */
  }

  .mega__links li {
    margin: 0;
  }

  .mega__links li + li a {
    border-top: 1px solid rgba(4, 39, 14, 0.06);
  }

  .mega__links a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px var(--nav-pad) 13px calc(var(--nav-pad) + 20px);
    min-height: 48px;
    font-size: 1rem;
    font-weight: 400;
    color: var(--brand-deep);
    text-decoration: none;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 220ms ease 60ms, transform 220ms ease 60ms,
      background-color 160ms ease;
  }

  .mega__col.is-open .mega__links a {
    opacity: 1;
    transform: none;
  }

  .mega__links a::before {
    content: "";
    flex: none;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.5;
  }

  .mega__links a:active {
    background: rgba(4, 39, 14, 0.05);
  }

  /* ---- "View all ..." reads as a CTA, not another link ----------------- */

  .mega__cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 4px var(--nav-pad) 18px;
    padding: 10px 0;
    min-height: 44px;
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--brand-deep);
    text-decoration: none;
  }

  .mega__col:not(.is-open) .mega__cta {
    display: none;
  }

  .mega__cta svg {
    width: 16px;
    height: 16px;
    transition: transform 200ms ease;
  }

  .mega__cta:active svg {
    transform: translateX(3px);
  }

  /* ---- pinned quote CTA ------------------------------------------------ */

  /*
   * Hidden in the drawer, not removed.
   *
   * There are two of these — one per mega panel — and the same action is
   * already in the fixed bottom bar that sits over every page, plus the
   * header. Pinned over the menu it covered the last row of links and gave
   * the user two identical green buttons on screen at once. Still rendered
   * for desktop.
   */
  .mega__foot {
    display: none;
  }

  .mega__foot .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 54px;
    border-radius: 14px;
    font-size: 1.0625rem;
    font-weight: 700;
    box-shadow: 0 6px 18px rgba(11, 92, 42, 0.28);
    transition: transform 160ms ease, box-shadow 160ms ease;
  }

  .mega__foot .btn:active {
    transform: translateY(1px);
    box-shadow: 0 3px 10px rgba(11, 92, 42, 0.24);
  }

  /*
   * The emergency line is hidden in the drawer, not removed.
   *
   * The same number is already one tap away in the fixed bottom bar and in
   * the top bar, so a third copy under the quote button was competing with
   * the primary CTA rather than adding reach. Still rendered for desktop and
   * for screen readers elsewhere on the page.
   */
  .mega__emergency {
    display: none;
  }

  /* ---- close button ---------------------------------------------------- */

  /*
   * The drawer ships its own .nav__close. Restyling the burger as a floating
   * X put a second control on top of it — two overlapping crosses at the same
   * corner. The burger is hidden while the drawer is open and the real close
   * button gets the premium treatment.
   */
  .nav-burger[aria-expanded="true"] {
    opacity: 0;
    pointer-events: none;
  }

  .nav__close {
    position: absolute;
    top: 14px;
    right: 16px;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(4, 39, 14, 0.06);
    color: var(--ink-1);
    cursor: pointer;
    transition: background-color 180ms ease, transform 180ms ease;
  }

  .nav__close svg {
    width: 20px;
    height: 20px;
  }

  .nav__close:active {
    background: rgba(4, 39, 14, 0.12);
    transform: scale(0.94);
  }

  /* The close button overlaps the first row without this. */
  .nav__list {
    padding-top: 64px;
  }
}

@media (max-width: 1023px) and (prefers-reduced-motion: reduce) {
  .nav__link svg,
  .mega__toggle::after,
  .mega__links,
  .mega__links a,
  .mega__cta svg,
  .mega__foot .btn {
    transition: none;
  }
}
