/* ==========================================================================
   parts/header.css — site header (live header#brx-header; GLOBAL-SPEC §3). Mobile-first.
   Heights (final): 71 @390 ("Call now" wraps to 44px), 82.97 from ~680px up.
   Also the shared .menu-toggle component (burger + off-canvas close; "Menu toggle" below, DI-U-14).

   Fixed + compact header (replica enhancement, 2026-09-29; see "Fixed header" below): with JS the header is
   position:fixed at all times over an in-flow .site-header-spacer of the same height, fades down on load,
   and past its own height turns into a shorter, frosted bar. Without JS it is static, exactly as live.
   ========================================================================== */

:root {
  /* Compact (scrolled) bar: its height, and how much the logo / buttons / "Menu" group shrink into it.
     ≥479: 83 → 60 (the logo 56 → 40); <479: 71 → 56 ("Call now" wraps to 44px below 448). */
  --site-header-compact-h: 5.6rem;
  --site-header-logo-scale: .88;
  --site-header-btn-scale: .86;
  --site-header-menu-scale: .9;
  --site-header-dur: .5s;
  --site-header-ease: cubic-bezier(.22, 1, .36, 1);          /* ease-out quint: settles softly */
  --site-header-intro-ease: cubic-bezier(.16, 1, .3, 1);     /* ≈ GSAP expo.out */
  --site-header-fade-ease: cubic-bezier(.33, 1, .68, 1);     /* ≈ GSAP power2.out */
  /* frosted glass: translucent --secondary over a blurred, slightly saturated backdrop */
  --site-header-frost: color-mix(in srgb, var(--secondary) 62%, transparent);
  --site-header-frost-solid: color-mix(in srgb, var(--secondary) 95%, transparent);  /* no backdrop-filter */
  --site-header-frost-filter: blur(16px) saturate(140%);
  --site-header-hairline: color-mix(in srgb, var(--white) 8%, transparent);
}

.site-header {
  position: relative;
  width: 100%;
}

/* live div.header--xs.bg--secondary */
.site-header__bar {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  padding-block: var(--space-xs);
  padding-inline: var(--gutter);
  background-color: var(--secondary);
}

/* live #brxe-822b90: 3 equal columns, items centred in their column, rows stretch */
.site-header__inner {
  display: grid;
  grid-template-columns: var(--grid-3);
  justify-items: center;
  align-items: normal;
  width: var(--content-width);
  max-width: 100%;
  margin-inline: auto;
}

/* live a.brxe-logo.wlp-header__logo */
.site-header__logo {
  justify-self: start;
  width: 100%;
  max-width: 13rem;
  color: currentColor;
  font-size: 20px;          /* Bricks .brxe-logo box metrics (no visible text) */
  font-weight: 600;
  line-height: 1;
  aspect-ratio: 861 / 241;  /* the logo SVG's ratio: reserves the logo's height before the file arrives (live
                               grows the header by it on load, a layout shift). The loaded <img> fills the same
                               box, so the final layout and the rendered logo are unchanged (DI-B37) */
}
.site-header__logo-img {
  display: block;
  width: auto;
  height: auto;
}

/* live #brxe-ab8a07: row-reverse below 992, so an overflowing group grows to the LEFT (DI-D8) */
.site-header__actions {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  align-content: center;
  gap: var(--space-xs);
  max-width: 100%;
}
.site-header__action { max-width: 100%; }
.site-header__action--email { display: none; }

/* "Call now" / "Email us": v2 sm buttons (parts/header.php; docs/design/BUTTONS.md §3.1 H1/H2). Live's 2px border on
   the filled "Call now" (#brxe-gikbao) is now every v2 button's border, so .site-header__btn--call has no CSS (a hook
   only). The compact state never touches --btn-* (UPGRADE-SPEC C1): it scales the .site-header__action wrapper. */

/* live #brxe-rqgoln + #brxe-sotbow */
.site-header__menu {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-self: end;
  gap: 1rem;
}
.site-header__menu-label {
  color: var(--white);                                       /* Pete: white, same as the burger (live #798B9C) */
  font-size: 1.4rem;
  text-transform: uppercase;
}

/* ==========================================================================
   Menu toggle (DI-U-14, Pete 2026-09-29): the burger (.site-header__toggle) and the off-canvas close button
   (.offcanvas__close) are one component, a CSS-drawn icon of three rounded 2px bars instead of live's Font
   Awesome fa-bars / fa-xmark glyphs (parts/header.php, parts/offcanvas.php):
     <button class="menu-toggle …"><span class="menu-icon"><span class="menu-icon__bar"></span> ×3</span></button>
   - Box: live's glyph box, .875em × 1em of live's 28 / 32 / 40px icon (24.5×28 below 479, 28×32 to 991, 35×40
     from 992), so the header row and the "Menu" label keep live's layout exactly and the focus ring sits where it
     always did. The icon is a touch smaller (22 × 16 below 768: 5px gaps; 26 × 18 from 768: 6px gaps), centred.
     The hit area is a 54 × 54 ::after (≥ 44 × 44 even at the compact header's .84 scale).
   - Rest: three full-width white bars; hover (fine pointers) and :focus-visible make them bolder (2 → 3 px); they stay white, like the "Menu" word.
   - .is-open (main.js, on the burger and the close button while the menu is open): the X. The outer bars
     close in on the middle (translate), then turn ±45° (rotate); the middle one shrinks to the right and fades.
     Closing plays it backwards (rotate back first, then open out). 0.4 s cubic-bezier(.65, 0, .35, 1) in all,
     over the panel's 0.2 s slide: the close button (held over the burger by main.js) morphs as the panel
     slides in, the burger morphs back as it slides away.
   - Reduced motion and ?nomotion=1 (base.css html.nomotion): instant.
   The bar's <span> carries the X transform (translate / rotate about the icon centre); its ::before is the
   visible bar and carries the width (scale about its right end), so the two never fight over one origin.
   ========================================================================== */
:root {
  --menu-icon-w: 2.2rem;
  --menu-icon-gap: .5rem;
  --menu-icon-bar: .2rem;
  --menu-icon-ease: cubic-bezier(.65, 0, .35, 1);
  --menu-icon-hover: var(--white);                            /* Pete: stays white on hover, like the "Menu" word */
  --menu-icon-bold: 1.5;                                     /* hover: bars 2px → 3px (scaleY, compositor only) */
}

.menu-toggle {
  --menu-toggle-w: 2.45rem;                                  /* live's glyph box (see above) */
  --menu-toggle-h: 2.8rem;
  position: relative;
  display: flex;
  flex: none;
  justify-content: center;
  align-items: center;
  width: var(--menu-toggle-w);
  height: var(--menu-toggle-h);
  margin: 0;
  padding: 0;
  overflow: visible;
  border: 0;
  background-color: transparent;
  color: var(--white);
  font: inherit;
  line-height: 1;
  text-transform: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.menu-toggle::after {                                        /* hit area: 54 × 54, centred on the box */
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5.4rem;
  height: 5.4rem;
  translate: -50% -50%;
}

.menu-icon {
  --menu-icon-step: calc(var(--menu-icon-gap) + var(--menu-icon-bar));   /* bar centre to bar centre */
  position: relative;
  display: block;
  flex: none;
  width: var(--menu-icon-w);
  height: calc(3 * var(--menu-icon-bar) + 2 * var(--menu-icon-gap));
  pointer-events: none;
}
.menu-icon__bar {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: var(--menu-icon-bar);
  margin-top: calc(var(--menu-icon-bar) / -2);
}
.menu-icon__bar:nth-child(1) { translate: 0 calc(-1 * var(--menu-icon-step)); }
.menu-icon__bar:nth-child(3) { translate: 0 var(--menu-icon-step); }
.menu-icon__bar::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--menu-icon-bar);
  background-color: currentColor;
  transform-origin: 100% 50%;
  scale: var(--menu-bar-w, 1) var(--menu-bar-h, 1);
}
/* Pete (2026-09-29): three FULL-width bars at rest (the earlier 100 / 72 / 86 % stagger is retired). */

/* hover (fine pointers only: no sticky hover after a tap) and keyboard focus: even bars, ice tint */
@media (hover: hover) {
  .menu-toggle:hover { color: var(--menu-icon-hover); }
  .menu-toggle:hover .menu-icon__bar::before { --menu-bar-h: var(--menu-icon-bold); }
}
.menu-toggle:focus-visible { color: var(--menu-icon-hover); }
.menu-toggle:focus-visible .menu-icon__bar::before { --menu-bar-h: var(--menu-icon-bold); }

/* open: the X */
.menu-toggle.is-open .menu-icon__bar { translate: 0 0; }
.menu-toggle.is-open .menu-icon__bar:nth-child(1) { rotate: 45deg; }
.menu-toggle.is-open .menu-icon__bar:nth-child(3) { rotate: -45deg; }
.menu-toggle.is-open .menu-icon__bar::before { --menu-bar-w: 1; }
.menu-toggle.is-open .menu-icon__bar:nth-child(2)::before { --menu-bar-w: 0; opacity: 0; }

/* Motion (none under reduced motion; html.nomotion drops every transition). A transition is the one of the state
   being entered: .is-open = to the X (close in, then turn), otherwise back to the hamburger (turn back, then open
   out; the middle bar returns once the outer ones have turned back). */
@media (prefers-reduced-motion: no-preference) {
  .menu-toggle { transition: color .3s var(--menu-icon-ease); }
  .menu-icon__bar {
    transition:
      rotate .22s var(--menu-icon-ease),
      translate .2s var(--menu-icon-ease) .16s;
  }
  .menu-icon__bar::before {
    transition:
      scale .3s var(--menu-icon-ease) .1s,
      opacity .2s var(--menu-icon-ease) .1s;
  }
  .menu-toggle:not(.is-open) .menu-icon__bar:nth-child(2)::before {
    transition:
      scale .24s var(--menu-icon-ease) .18s,
      opacity .16s var(--menu-icon-ease) .18s;
  }
  .menu-toggle:focus-visible .menu-icon__bar::before { transition-delay: 0s; }
  .menu-toggle.is-open .menu-icon__bar {
    transition:
      translate .18s var(--menu-icon-ease),
      rotate .26s var(--menu-icon-ease) .14s;
  }
  .menu-toggle.is-open .menu-icon__bar::before {
    transition:
      scale .18s var(--menu-icon-ease),
      opacity .14s var(--menu-icon-ease);
  }
}
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  /* hovering in evens the bars at once (the middle one too; the toggle is never hovered while it closes: the
     panel covers it until it has slid away) */
  .menu-toggle:not(.is-open):hover .menu-icon__bar::before,
  .menu-toggle:not(.is-open):hover .menu-icon__bar:nth-child(2)::before { transition-delay: 0s; }
}
/* Reduced motion: none at all (base.css would otherwise run every property, the focus ring included, as a 0.01 ms
   transition, one frame late). */
@media (prefers-reduced-motion: reduce) {
  .menu-toggle,
  .menu-toggle::after,
  .menu-icon,
  .menu-icon__bar,
  .menu-icon__bar::before { transition: none; }
}
/* Forced colours (Windows contrast themes) replace background colours with Canvas, which would erase the bars:
   draw them in the button text colour instead. */
@media (forced-colors: active) {
  .menu-icon__bar::before {
    forced-color-adjust: none;
    background-color: ButtonText;
  }
}

@media (min-width: 479px) {
  :root {
    --site-header-compact-h: 6rem;
    --site-header-logo-scale: .72;
    --site-header-btn-scale: .9;
  }
  .site-header__logo { max-width: 20rem; }
  .site-header__menu { column-gap: var(--space-s); row-gap: 2rem; }
  .site-header__menu-label { font-size: 1.6rem; }
  .menu-toggle { --menu-toggle-w: 2.8rem; --menu-toggle-h: 3.2rem; }
}

@media (min-width: 768px) {
  :root {
    --menu-icon-w: 2.6rem;
    --menu-icon-gap: .6rem;
  }
  .site-header__action--email { display: block; }
}

@media (min-width: 992px) {
  :root { --site-header-menu-scale: .84; }
  .site-header__actions { flex-direction: row; }
  .menu-toggle { --menu-toggle-w: 3.5rem; --menu-toggle-h: 4rem; }
}

/* ==========================================================================
   Fixed header (html.js). Live's header is static; the replica keeps that layout at scroll-top and adds:
     1. position:fixed at all times (z 997: above the page and the preview banner, below the skip links and
        popups). .site-header-spacer (the next sibling, in the flow) reserves the header's exact height, so
        the page lays out and paints exactly as with the static header. Its height comes from the same rule
        as the header's (27.01 + max(logo box, 44 when "Call now" wraps)), and header.js then pins it to the
        header's measured height (ResizeObserver) in case fonts or widths ever make them differ.
     2. A fade-down entrance on load (header.js adds .is-intro): logo → buttons → "Menu", y -24px → 0 and
        opacity, 0.9s expo-out, as CSS animations (compositor-driven, so the load-time work of the reveal
        layer / ScrollSmoother on the main thread cannot make it stutter). Until then html.reveal-pending
        keeps them unpainted (no flash); with ?nomotion=1, reduced motion or no JS they are never hidden.
     3. .is-compact once the spacer has scrolled out (IntersectionObserver, so no scroll handler): the bar
        shrinks to --site-header-compact-h and turns frosted. The header's own box never changes size (the
        spacer and every layout stay put): only transforms move — the background layer (::before) slides up
        by the height difference, the logo / buttons / "Menu" rise and scale into the shorter bar — so the
        animation runs on the compositor. The header root and its wrappers take no pointer events (the band
        under the compact bar clicks through to the page); the bar background and the controls do.
   Nothing here may put a transform, filter, backdrop-filter, opacity, clip-path, contain or will-change on
   .site-header, .site-header__bar, __inner or __actions: they contain the off-canvas root, whose panel and
   backdrop are position:fixed to the viewport (such an ancestor would become their containing block), and
   backdrop-filter needs the header not to be a "backdrop root". The off-canvas now lives in the header's
   stacking context (997), which still puts it above everything but the skip links and popups.
   ========================================================================== */

.site-header-spacer { display: none; }

html.js .site-header {
  --site-header-shift: calc(var(--site-header-compact-h) - var(--site-header-h, 8.297rem));   /* ≤ 0 */
  --site-header-lift: calc(var(--site-header-shift) / 2);
  position: fixed;
  top: var(--site-header-top, 0px);
  left: 0;
  z-index: calc(var(--z-backdrop) - 1);
  pointer-events: none;
}

/* The bar's background moves to ::before (so it can slide and frost); ::after is the compact hairline + shadow. */
html.js .site-header__bar { background-color: transparent; }

html.js .site-header::before,
html.js .site-header::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
}
html.js .site-header::before {
  background-color: var(--secondary);
  pointer-events: auto;                     /* the visible bar is not click-through */
  /* leaving the compact state: the blur switches off only once the colour is opaque again */
  transition:
    translate var(--site-header-dur) var(--site-header-ease),
    background-color var(--site-header-dur) var(--site-header-ease),
    -webkit-backdrop-filter 0s linear var(--site-header-dur),
    backdrop-filter 0s linear var(--site-header-dur);
}
html.js .site-header::after {
  opacity: 0;
  box-shadow: inset 0 -1px 0 var(--site-header-hairline), 0 1.2rem 3.2rem -1.6rem var(--black-trans-60);
  pointer-events: none;
  transition:
    translate var(--site-header-dur) var(--site-header-ease),
    opacity var(--site-header-dur) var(--site-header-ease);
}

html.js .site-header :is(.site-header__logo, .site-header__action, .site-header__menu, .offcanvas) { pointer-events: auto; }
html.js .site-header :is(.site-header__logo, .site-header__action, .site-header__menu) {
  transition:
    translate var(--site-header-dur) var(--site-header-ease),
    scale var(--site-header-dur) var(--site-header-ease);
}
html.js .site-header__logo { transform-origin: 0 50%; }       /* shrinks towards its left edge */
html.js .site-header__menu { transform-origin: 100% 50%; }    /* … and "Menu" towards the right edge */

html.js .site-header-spacer {
  display: block;
  flex: none;
  width: 100%;
  min-height: calc(2 * var(--space-xs) + 4.4rem);   /* < 448: "Call now" wraps to two lines, a 44px row */
  padding: var(--space-xs) var(--gutter);
}
html.js .site-header-spacer::before {                 /* the logo box: 1/3 of the content width, capped, 861:241 */
  content: "";
  display: block;
  width: calc(min(100%, var(--content-width)) / 3);
  max-width: 13rem;
  aspect-ratio: 861 / 241;
}

/* Anchors (#join, the skip links, …) land below the compact bar: ACSS's --offset feeds base.css's
   [id] { scroll-margin-top } (native jumps and WFX.scrollTo both honour it). "Skip to main content" lands at
   the very top instead (#brx-content starts right under the full-height bar), and keyboard focus moving onto
   something under the bar scrolls it clear of the bar. */
html.js { --offset: var(--site-header-compact-h); }
html.js #brx-content { scroll-margin-top: var(--site-header-h, 0px); }
html.js :where(.site-main, #brx-footer) :where(a[href], button, input, select, textarea, summary, [tabindex]) {
  scroll-margin-top: var(--site-header-compact-h);
}

/* The replica-only content-preview banner (sticky, first in <body>) stays above the header: header.js sets
   --site-header-top to its measured height; these are its heights for the first paint (1–3 lines). */
html.js .preview-banner ~ .site-header { top: var(--site-header-top, 9.2rem); }

/* ---- Compact + frosted --------------------------------------------------------------------------- */
html.js .site-header.is-compact::before {
  translate: 0 var(--site-header-shift);
  background-color: var(--site-header-frost);
  -webkit-backdrop-filter: var(--site-header-frost-filter);
  backdrop-filter: var(--site-header-frost-filter);
  transition:
    translate var(--site-header-dur) var(--site-header-ease),
    background-color var(--site-header-dur) var(--site-header-ease),
    -webkit-backdrop-filter 0s,
    backdrop-filter 0s;
}
html.js .site-header.is-compact::after {
  translate: 0 var(--site-header-shift);
  opacity: 1;
}
html.js .site-header.is-compact .site-header__logo {
  translate: 0 var(--site-header-lift);
  scale: var(--site-header-logo-scale);
}
html.js .site-header.is-compact .site-header__action {
  translate: 0 var(--site-header-lift);
  scale: var(--site-header-btn-scale);
}
html.js .site-header.is-compact .site-header__menu {
  translate: 0 var(--site-header-lift);
  scale: var(--site-header-menu-scale);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html.js .site-header.is-compact::before { background-color: var(--site-header-frost-solid); }
}

/* First state after load (a restored scroll position) and reduced motion: no transition. */
html.js .site-header.is-instant::before,
html.js .site-header.is-instant::after,
html.js .site-header.is-instant :is(.site-header__logo, .site-header__action, .site-header__menu) { transition: none; }

/* ---- Load entrance ------------------------------------------------------------------------------- */
/* Starts with the first paint: while the <head> script's html.reveal-pending is set (motion on: JS, no
   ?nomotion=1, no reduced motion) the bar's contents play the fade-down, so they are never painted in their
   final state first and the bar leads the hero timeline. header.js adds .is-intro at once, which keeps the same
   animations running after animations.js drops html.reveal-pending (same names: they continue, not restart),
   and removes it when they have finished. If header.js never runs they simply end in their final state when the
   class goes (at the latest the 3 s failsafe). The bar colour does not animate (it is the page colour). */
@media (prefers-reduced-motion: no-preference) {
  html.reveal-pending:not(.nomotion) .site-header :is(.site-header__logo, .site-header__action, .site-header__menu),
  .site-header.is-intro :is(.site-header__logo, .site-header__action, .site-header__menu) {
    animation:
      site-header-intro-y .9s var(--site-header-intro-ease) both,
      site-header-intro-fade .6s var(--site-header-fade-ease) both;
  }
  html.reveal-pending:not(.nomotion) .site-header .site-header__action,
  .site-header.is-intro .site-header__action { animation-delay: .07s; }
  html.reveal-pending:not(.nomotion) .site-header .site-header__menu,
  .site-header.is-intro .site-header__menu { animation-delay: .14s; }
}

@keyframes site-header-intro-y {
  from { transform: translate3d(0, -2.4rem, 0); }
  to   { transform: none; }
}
@keyframes site-header-intro-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (min-width: 448px) {
  html.js .site-header-spacer { min-height: 0; }                /* "Call now" on one line: the logo sets the height */
}
@media (min-width: 479px) {
  html.js .site-header-spacer::before { max-width: 20rem; }
}
@media (min-width: 768px) {
  html.js .preview-banner ~ .site-header { top: var(--site-header-top, 4.4rem); }
}
@media (min-width: 992px) {
  html.js .preview-banner ~ .site-header { top: var(--site-header-top, 4.725rem); }
}

@media (prefers-reduced-motion: reduce) {
  html.js .site-header:is(.is-compact, :not(.is-compact))::before,   /* as specific as the .is-compact rule */
  html.js .site-header::after,
  html.js .site-header :is(.site-header__logo, .site-header__action, .site-header__menu) { transition: none; }
}

@media print {
  html.js .site-header { position: static; }
  html.js .site-header-spacer { display: none; }
}

/* DI-U-03: the "Menu" label on the compact frosted bar. Live's #798B9C drops to ~1.3:1 when a white section
   sits under the glass; the lighter tint keeps it legible over any section (no layout change). */
html.js .site-header.is-compact .site-header__menu-label {
  color: var(--white);
}
@media (prefers-reduced-motion: no-preference) {
  html.js .site-header .site-header__menu-label { transition: color .3s ease; }
}
