/**
 * Navigation.
 *
 * Two modes, one markup. Above 62rem the services item opens a panel on hover
 * and on focus; below it, everything lives in a drawer. The drawer is in the
 * document from the start and merely toggled, so a phone visitor without
 * working JavaScript still has a reachable menu rather than none at all.
 */

.bm-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--bm-space-6);
	min-block-size: 5.25rem;
}

/* --- desktop nav --------------------------------------------------------- */

.bm-nav { display: none; }

.bm-nav__list {
	display: flex;
	align-items: center;
	gap: var(--bm-space-6);
	list-style: none;
	margin: 0;
	padding: 0;
}
.bm-nav__item > a {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	padding-block: 1.5rem;
	color: var(--bm-text);
	text-decoration: none;
	font-size: 0.9375rem;
	font-weight: 500;
}
.bm-nav__item > a:hover { color: var(--bm-text-link); }
.bm-nav__chev { transition: transform 180ms ease; }

/* --- mega panel ---------------------------------------------------------- */

.bm-nav__item--has-children { position: relative; }

.bm-mega {
	position: absolute;
	inset-block-start: 100%;
	inset-inline-start: -1rem;
	z-index: 60;
	min-inline-size: 21rem;
	padding: var(--bm-space-3);
	background: var(--bm-surface-raised);
	border: 1px solid var(--bm-border-subtle);
	border-radius: var(--bm-radius-lg);
	box-shadow: var(--bm-shadow-lg);
	/* Not display:none — the panel animates, and a hidden-but-laid-out panel
	   keeps its links focusable in the right order. visibility handles the
	   pointer/AT side of it. */
	opacity: 0;
	visibility: hidden;
	transform: translateY(-6px);
	transition: opacity 160ms ease, transform 160ms ease, visibility 160ms;
}
.bm-nav__item--has-children:hover .bm-mega,
.bm-nav__item--has-children:focus-within .bm-mega {
	opacity: 1;
	visibility: visible;
	transform: none;
}
.bm-nav__item--has-children:hover .bm-nav__chev,
.bm-nav__item--has-children:focus-within .bm-nav__chev { transform: rotate(180deg); }

.bm-mega__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.bm-mega__list a {
	display: flex;
	flex-direction: column;
	gap: 0.1rem;
	padding: 0.7rem 0.85rem;
	border-radius: var(--bm-radius-md);
	text-decoration: none;
	color: var(--bm-text);
}
.bm-mega__list a:hover { background: var(--bm-surface-sunken); }
.bm-mega__title { font-weight: 600; font-size: 0.9375rem; }
.bm-mega__note { font-size: 0.8125rem; color: var(--bm-text-muted); }

/* --- burger -------------------------------------------------------------- */

.bm-burger {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	background: transparent;
	border: 1px solid var(--bm-action-secondary-border);
	border-radius: var(--bm-radius-md);
	padding: 0.6rem 0.85rem;
	cursor: pointer;
	font: inherit;
	color: var(--bm-text);
}
.bm-burger__label { font-size: 0.875rem; font-weight: 600; }
.bm-burger__bar {
	display: block;
	inline-size: 16px;
	block-size: 1.5px;
	background: currentcolor;
	transition: transform 200ms ease, opacity 200ms ease;
}
.bm-burger__bar:first-child { margin-block-end: 4px; }
.bm-burger[aria-expanded="true"] .bm-burger__bar:first-child { transform: translateY(2.75px) rotate(45deg); }
.bm-burger[aria-expanded="true"] .bm-burger__bar:nth-child(2) { transform: translateY(-2.75px) rotate(-45deg); }
.bm-burger:focus-visible { outline: 2px solid var(--bm-border-focus); outline-offset: 2px; }

/* --- drawer ---------------------------------------------------------------

   Slides in from the side rather than appearing. The old version toggled
   [hidden], which is instant: the panel was simply there, at full size, in one
   frame. On a phone that reads as the page jumping rather than as a menu
   opening, which is what "too fast and too harsh" meant.

   Three hundred milliseconds on a decelerating curve, so it arrives quickly and
   settles rather than slamming. The scrim fades slightly behind it, which is
   what makes the panel look like it is in front of the page instead of part of
   it.
   -------------------------------------------------------------------------- */

.bm-drawer {
	position: fixed;
	inset-block-start: 5.25rem;
	inset-block-end: 0;
	inset-inline-end: 0;
	z-index: 45;
	inline-size: min(23rem, 86vw);
	overflow-y: auto;
	background: var(--bm-surface);
	border-block-start: 1px solid var(--bm-border-subtle);
	border-inline-start: 1px solid var(--bm-border-subtle);
	box-shadow: -18px 0 44px rgb(6 23 47 / 14%);
	-webkit-overflow-scrolling: touch;

	/* Parked off the edge. The panel is laid out and painted before it is
	   asked to move, so the first frame of the animation is never a reflow. */
	transform: translateX(100%);
	transition: transform 300ms cubic-bezier(0.22, 0.61, 0.36, 1);
	will-change: transform;
}

.bm-drawer[hidden] { display: none; }
.bm-drawer.is-open { transform: translateX(0); }

/* Behind the panel. Clicking it closes the drawer, which is what everybody
   expects and what stops the page underneath feeling stuck. */
.bm-drawer-scrim {
	position: fixed;
	inset-block-start: 5.25rem;
	inset-block-end: 0;
	inset-inline: 0;
	z-index: 44;
	background: rgb(6 23 47 / 38%);
	opacity: 0;
	transition: opacity 300ms ease;
}
.bm-drawer-scrim[hidden] { display: none; }
.bm-drawer-scrim.is-open { opacity: 1; }

.bm-drawer__inner {
	display: flex;
	flex-direction: column;
	gap: var(--bm-space-2);
	padding: var(--bm-space-6) clamp(1.25rem, 5vw, 1.75rem) var(--bm-space-12);
}
.bm-drawer__label {
	margin: var(--bm-space-4) 0 0;
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: var(--bm-tracking-wide);
	text-transform: uppercase;
	color: var(--bm-text-muted);
}
.bm-drawer__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.bm-drawer__list a {
	display: block;
	padding-block: var(--bm-space-3);
	border-block-end: 1px solid var(--bm-border-subtle);
	font-family: var(--bm-font-display);
	font-size: 1.25rem;
	color: var(--bm-text);
	text-decoration: none;
}
.bm-drawer__list a:hover { color: var(--bm-text-link); }
/* Call and quote, side by side at the foot of the menu. A grid rather than a
   flex row so the two come out the same width whatever their labels, and
   auto-fit so a phone too narrow to give each 7.5rem, the width "Get a quote"
   needs, stacks them instead of squeezing the label out of its button. With
   no phone number the quote button is alone and takes the whole row. */
.bm-drawer__actions {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
	gap: var(--bm-space-3);
	margin-block-start: var(--bm-space-6);
}
.bm-drawer__actions .bm-btn { justify-content: center; padding-inline: 0.5rem; }

/* The burger crossing into an X, at the same pace as the panel it opens. */
.bm-burger__bar { transition: transform 300ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 200ms ease; }

/* Stop the page behind the drawer from scrolling underneath it. */
body.bm-drawer-open { overflow: hidden; }

/* Somebody who has asked their device to stop animating things means it. The
   drawer still opens and closes; it just stops sliding. */
@media (prefers-reduced-motion: reduce) {
	.bm-drawer,
	.bm-drawer-scrim,
	.bm-burger__bar { transition-duration: 1ms; }
}

/* --- breakpoint ---------------------------------------------------------- */

@media (min-width: 62rem) {
	.bm-nav { display: block; }
	.bm-burger { display: none; }
	.bm-drawer,
	.bm-drawer-scrim { display: none !important; }
}

@media (max-width: 61.99rem) {
	/* The header CTA duplicates the drawer CTA on small screens and crowds
	   the wordmark, so it steps aside there. */
	.bm-header__cta, .bm-header__phone { display: none; }
}

/* ============================================================================
 * MOBILE ADAPTATION
 * ============================================================================
 * The chrome is the one thing on every page, so a defect here is a defect
 * everywhere. Four of them: the page scrolls behind the open drawer, the tab
 * order leaks out of it, an 85px sticky header eats a quarter of a landscape
 * phone, and two controls are a couple of pixels under the 44px floor.
 *
 * Everything that changes geometry is inside `max-width: 79.99rem`, which
 * cannot match at 1280px. Desktop being unchanged is therefore arithmetic
 * rather than something a reviewer has to take on trust.
 * ========================================================================== */

/* --- global, and only from the whitelist ----------------------------------
   Neither of these changes geometry at any width, which is why they need no
   breakpoint. */

/* The drawer is the panel that scrolls, so the drawer is where the chain
   stops. base.css deliberately declines to put `overscroll-behavior: none` on
   html — that would switch off pull-to-refresh for every visitor at every
   width, which is a behaviour change well outside adapting the site to a
   phone — and leaves the containment to whichever file owns the panel. This
   one. Without it a flick past the end of the menu carries on into the page
   underneath. */
.bm-drawer { overscroll-behavior: contain; }

/* The other half of the same defect, and the reason `body { overflow: hidden }`
   is not enough. iOS does not honour it for a touch-drag: the page rubber-bands
   behind the open drawer. The scrim covers everything below the header, so
   refusing pan gestures on the scrim is what actually holds the page still.
   `overscroll-behavior` would do nothing here — the scrim is not a scroll
   container. Taps are unaffected: touch-action suppresses panning, not clicks,
   so clicking the scrim still closes the drawer.

   Deliberately NOT `position: fixed` on the body, which is the usual answer.
   It works, and it throws away the scroll position: the visitor closes the
   menu and finds themselves back at the top of the page. */
.bm-drawer-scrim { touch-action: none; }


@media (max-width: 79.99rem) {

	/* --- the header height, said once ---------------------------------
	   The same 5.25rem this file has always set, read from the token rather
	   than written out — `--bm-header-h` is declared at exactly 5.25rem in
	   tokens.css, so this is a zero delta at every width it matches.

	   What it buys is the block at the bottom of this file. The header
	   height and the two offsets that hang off it were three separate
	   literals; a short viewport has to shrink all three or the drawer
	   detaches from the header it sits under. Now there is one number. */
	.bm-header__inner { min-block-size: var(--bm-header-h); }

	.bm-drawer,
	.bm-drawer-scrim { inset-block-start: var(--bm-header-h); }

	/* --- the burger ---------------------------------------------------
	   It clears 44px today only because of its "Menu" label: 0.6rem of
	   padding either side of a 14px line box is 41.6px, and the label is
	   the only reason the box is tall enough. Nothing guarantees that, and
	   the label is exactly the sort of thing a redesign drops. Make the
	   floor explicit rather than incidental. */
	.bm-burger {
		min-block-size: var(--bm-tap-min);
		min-inline-size: var(--bm-tap-min);

		/* Without JavaScript this is a visible control that does nothing:
		   the scrim it needs is built by nav.js, so the burger opens
		   nothing at all. The site does not lose its navigation — the
		   drawer is in the document, and the footer carries the full
		   sitemap, which is the documented fallback — but a dead control
		   is worse than no control.

		   `visibility` and not `display`, so the box stays reserved and
		   revealing it shifts nothing. nav.js is deferred and in the
		   footer, so on a slow connection the header can paint before the
		   burger appears; a fade-in is the acceptable half of that trade,
		   a header that jumps sideways is not. */
		visibility: hidden;
	}

	.bm-nav-js .bm-burger { visibility: visible; }

	/* --- the mega panel -----------------------------------------------
	   336px in a 280px content box. Latent rather than live: `.bm-nav` is
	   display:none below 62rem, so the panel cannot overflow anything
	   today — it becomes an overflow the day somebody moves the nav
	   switch, which is the kind of breakage that arrives attached to a
	   change that looks unrelated.

	   The min() is inert where the panel is actually used: at 1280px
	   `100vw - 2rem` is 1248px, far past 21rem, so the panel is the same
	   panel. Only a viewport under 22rem wide ever picks the other term. */
	.bm-mega { min-inline-size: min(21rem, calc(100vw - 2rem)); }
}


/* --- short viewports, which means landscape --------------------------------
   740x360 is a real phone held sideways. 85px of sticky header is 24% of it,
   and the drawer and its scrim both start below that header, so the menu loses
   the same 24% a second time.

   The 3.5rem is written once. The three elements that need it read it back
   through `--bm-header-h`, which is the entire reason that token exists: the
   header cannot now shrink without the drawer and the scrim following it.

   Declared on the elements and not on `:root`, because `:root` belongs to the
   shared foundation and nothing outside these three consumes the token yet. A
   file that later wants its scroll-padding to follow the header on a short
   viewport should raise that with the owner of tokens.css rather than
   redeclare the token here.

   ANDed with the desktop line so the guarantee stays arithmetic. A 1280px-wide
   window that happens to be 480px tall is not a phone in landscape, and it
   keeps precisely the header it has today.
   ------------------------------------------------------------------------- */
@media (max-width: 79.99rem) and (max-height: 30rem) {
	.bm-header__inner,
	.bm-drawer,
	.bm-drawer-scrim { --bm-header-h: 3.5rem; }
}

/* The primary nav's own targets, between the mode switch and the desktop line.
 *
 * From 62rem the drawer gives way to the horizontal nav, and from there a link
 * is exactly as wide as its label. "About" measures 43.5px — a rounding away
 * from the 44px floor, and 62rem to 80rem is tablet territory, where the
 * pointer is still a finger.
 *
 * A minimum rather than padding, and a minimum is what makes this invisible:
 * it moves only the links already narrower than 44px, and it moves the widest
 * of those by half a pixel. Nothing in the nav's spacing or rhythm changes. */
@media (min-width: 62rem) and (max-width: 79.99rem) {
	.bm-nav__item > a {
		min-inline-size: var(--bm-tap-min);
		justify-content: center;
	}
}

/* ============================================================================
 * THE MOBILE ACTION BAR
 * ============================================================================
 * Call us, Call me back, Free quote: one row pinned to the bottom of the
 * screen and split in three. Below the nav switch only, which is exactly where
 * the header's own phone and quote buttons step aside, so on a phone the three
 * are always one tap away and never on screen twice.
 *
 * display:none outside the query, so a desktop never lays it out at all and
 * nothing here can reach 1280px.
 * ========================================================================== */

.bm-dock { display: none; }

@media (max-width: 61.99rem) {

	.bm-dock {
		position: fixed;
		inset-inline: 0;
		inset-block-end: 0;

		/* Above the page, below the drawer's scrim (44) and panel (45), so an
		   open menu covers the bar the way it covers everything else. The
		   estimate dialog is in the top layer and needs no number. */
		z-index: 40;

		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: 1fr;

		/* The iPhone home indicator. The 0px fallback is spelled out because
		   a bare env() is an invalid value where it is unknown, and an
		   invalid value throws the whole declaration away. */
		padding-block-end: env(safe-area-inset-bottom, 0px);

		background: var(--bm-surface-raised);
		border-block-start: 1px solid var(--bm-border-subtle);
		box-shadow: 0 -6px 20px rgb(6 23 47 / 8%);
		transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1), visibility 0s;
	}

	/* Stepped aside by nav.js. visibility as well as the slide, so a bar out
	   of sight is also out of the tab order and cannot catch a tap; it flips
	   only once the slide has finished, and back at once on the way in. */
	.bm-dock.is-away {
		transform: translateY(100%);
		visibility: hidden;
		transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1), visibility 0s 240ms;
	}

	.bm-dock__item {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 0.2rem;
		min-block-size: var(--bm-dock-h);
		padding: 0.35rem 0.25rem;
		font-family: var(--bm-font-display);
		font-size: 0.8125rem;
		font-weight: 600;
		line-height: 1.2;
		text-align: center;
		text-decoration: none;
		color: var(--bm-text);
		-webkit-tap-highlight-color: transparent;
	}
	.bm-dock__item + .bm-dock__item { border-inline-start: 1px solid var(--bm-border-subtle); }
	.bm-dock__item svg { flex: none; color: var(--bm-text-link); }
	.bm-dock__item:active { background: var(--bm-surface-sunken); }
	.bm-dock__item:focus-visible { outline: 2px solid var(--bm-border-focus); outline-offset: -4px; }

	/* The questionnaire is the one filled: the site's primary action, the
	   same navy and white as every "Get a quote". The focus ring turns
	   white on it, since teal on navy would all but vanish.

	   The bar's light top rule and the divider beside it both ran across the
	   navy as a pale line. The cell is pulled up 1px over the rule, and its
	   divider is transparent, which shows its own navy through it: a
	   background paints under the border by default. The divider rule is
	   matched with the same two-class selector that draws it, or it wins. */
	.bm-dock__item--primary {
		margin-block-start: -1px;
		background: var(--bm-action-bg);
		color: var(--bm-action-text);
	}
	.bm-dock__item + .bm-dock__item--primary { border-inline-start-color: transparent; }
	.bm-dock__item--primary svg { color: currentColor; }
	.bm-dock__item--primary:active { background: var(--bm-action-bg-hover); }
	.bm-dock__item--primary:focus-visible { outline-color: var(--bm-action-text); }

	/* Where the bar's in-page links land: below the sticky header, not under
	   it. Without this "Call me back" put the strip's heading, "Leave three
	   details.", behind the header. #enquiry is no longer one of the bar's
	   targets and stays for the contact page's own "Send a message", which
	   lands there and had the same fault. On the targets and not as
	   scroll-padding on html, because the questionnaire already offsets its
	   own scrolling by the header and the two would add up. */
	#bm-contact,
	#enquiry,
	#estimate { scroll-margin-block-start: var(--bm-header-h); }

	/* The page ends above the bar rather than under it, so the colophon and
	   the licence line can always be scrolled into view. */
	.bm-footer {
		padding-block-end: calc(var(--bm-space-12) + var(--bm-dock-h) + env(safe-area-inset-bottom, 0px));
	}

	/* Staff only, and pinned to the same edge. It sits on the bar instead of
	   behind it. */
	.bm-dock ~ .bm-staff-flag {
		inset-block-end: calc(1rem + var(--bm-dock-h) + env(safe-area-inset-bottom, 0px));
	}
}

@media (prefers-reduced-motion: reduce) {
	.bm-dock,
	.bm-dock.is-away { transition: none; }
}

/* A fixed bar prints on every sheet, and the page is under 62rem wide on
   paper. */
@media print {
	.bm-dock { display: none; }
}
